diff --git a/LinkedIn/LinkedIn_Send_like_to_latest_profile_post.ipynb b/LinkedIn/LinkedIn_Send_like_to_latest_profile_post.ipynb new file mode 100644 index 0000000000..b976f25596 --- /dev/null +++ b/LinkedIn/LinkedIn_Send_like_to_latest_profile_post.ipynb @@ -0,0 +1,400 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ba8798cc-1351-44ff-be1e-4f762a5b0ebf", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "\"Naas\"" + ] + }, + { + "cell_type": "markdown", + "id": "de01e1b4-4603-4fe9-a70e-b438b91ef689", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "# LinkedIn - Send like to latest profile post" + ] + }, + { + "cell_type": "markdown", + "id": "e493502e-ce65-47a2-b5e6-22b071da07e2", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "**Tags:** #linkedin #socialmedia #like #profile #post #python" + ] + }, + { + "cell_type": "markdown", + "id": "2806c98c-3aa0-4af4-a27f-d75e2d1d05eb", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "**Author:** [Florent Ravenel](https://www.linkedin.com/in/florent-ravenel)" + ] + }, + { + "cell_type": "markdown", + "id": "1d877922-0cdb-49c9-a035-8edadc8bdddf", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "**Last update:** 2023-08-03 (Created: 2023-08-03)" + ] + }, + { + "cell_type": "markdown", + "id": "c214a89b-3395-4d18-852f-8c8fb65c42f8", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "**Description:** This notebook will follow a profile on LinkedIn and send like to its last posts." + ] + }, + { + "cell_type": "markdown", + "id": "3902e0dc-9fcf-4a13-81eb-1338edc70008", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "\n", + "
\n", + "Disclaimer:
\n", + "This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by Linkedin or any of its affiliates or subsidiaries. It uses an independent and unofficial API. Use at your own risk.\n", + "\n", + "This project violates Linkedin's User Agreement Section 8.2, and because of this, Linkedin may (and will) temporarily or permanently ban your account. We are not responsible for your account being banned.\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "d67831a3-60c6-4cbb-bc06-f03a6c069083", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "## Input" + ] + }, + { + "cell_type": "markdown", + "id": "350d10b2-83f3-45ee-bf43-773af077f390", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "### Import libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6fada83-c9cc-4478-87d0-cca638901100", + "metadata": { + "papermill": {}, + "tags": [] + }, + "outputs": [], + "source": [ + "from naas_drivers import linkedin\n", + "import requests\n", + "import naas\n", + "import os\n", + "try:\n", + " import hashlib\n", + "except:\n", + " !pip install hashlib --user\n", + " import hashlib" + ] + }, + { + "cell_type": "markdown", + "id": "f6cfef89-38f3-4601-86a8-5381e2194ee6", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "### Setup variables\n", + "**Mandatory**\n", + "\n", + "[Learn how to get your cookies on LinkedIn](https://www.notion.so/LinkedIn-driver-Get-your-cookies-d20a8e7e508e42af8a5b52e33f3dba75)\n", + "- `li_at`: Cookie used to authenticate Members and API clients\n", + "- `JSESSIONID`: Cookie used for Cross Site Request Forgery (CSRF) protection and URL signature validation\n", + "- `profiles_url`: This variable represents a list of LinkedIn profiles URL\n", + "- `cron`: This variable represents the CRON syntax used to run the scheduler. More information here: https://crontab.guru/#0_12,18_*_*_1-5\n", + "\n", + "**Optional**\n", + "\n", + "- `reaction_type`: This variable represents the type of reaction to sent to the post. It could be \"LIKE\", \"PRAISE\", \"APPRECIATION\", \"EMPATHY\", \"INTEREST\", \"ENTERTAINMENT\"\n", + "- `dir_path`: This variable represents the directory path to store the post URL" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47d4eeb3-5cd5-4e0e-9a31-bbdedd0e0263", + "metadata": { + "papermill": {}, + "tags": [] + }, + "outputs": [], + "source": [ + "# Mandatory\n", + "li_at = naas.secret.get(\"LINKEDIN_LI_AT\") or \"YOUR_LINKEDIN_LI_AT\" #example: AQFAzQN_PLPR4wAAAXc-FCKmgiMit5FLdY1af3-2\n", + "JSESSIONID = naas.secret.get(\"LINKEDIN_JSESSIONID\") or \"YOUR_LINKEDIN_JSESSIONID\" #example: ajax:8379907400220387585\n", + "profiles_url = [\n", + " \"https://www.linkedin.com/in/jeremyravenel/\",\n", + " \"https://www.linkedin.com/in/florent-ravenel/\",\n", + "]\n", + "cron = \"0 12,18 * * 1-5\" #At minute 0 past hour 12 and 18 on every day-of-week from Monday through Friday.\n", + "\n", + "# Optional\n", + "reaction_type = \"LIKE\" # \"PRAISE\", \"APPRECIATION\", \"EMPATHY\", \"INTEREST\", \"ENTERTAINMENT\"\n", + "dir_path = \"posts_liked\"" + ] + }, + { + "cell_type": "markdown", + "id": "54825487-e86e-46c7-b385-faac1f1e3da2", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "## Model" + ] + }, + { + "cell_type": "markdown", + "id": "358541d1-64e1-46de-ac4f-3877e5c7b5ae", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "### Connect to LinkedIn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e704bdb-a00f-4738-bbe7-41240d0a7efd", + "metadata": { + "papermill": {}, + "tags": [] + }, + "outputs": [], + "source": [ + "LK = linkedin.connect(li_at, JSESSIONID)\n", + "cookies = LK.cookies\n", + "headers = LK.headers" + ] + }, + { + "cell_type": "markdown", + "id": "a4164feb-ef87-4dac-9a93-58fd6efc8fa6", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "### Send like to post" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10cb0b8c-cc95-4b14-9b2a-2daffcb9824a", + "metadata": { + "papermill": {}, + "tags": [] + }, + "outputs": [], + "source": [ + "def send_like(\n", + " cookies,\n", + " headers,\n", + " post_url,\n", + " reaction_like\n", + "):\n", + " # Check post URL\n", + " response = None\n", + " if not post_url.startswith(\"https://www.linkedin.com/\"):\n", + " print(\"🛑 Post URL not valid! Please update it in the input section.\")\n", + " return response\n", + " # Parse url to get activity id\n", + " if \":activity:\" in post_url:\n", + " activity_tag = \":activity:\"\n", + " tag_end = \"?\"\n", + " elif \"-activity-\" in post_url:\n", + " activity_tag = \"-activity-\"\n", + " tag_end = \"-\"\n", + " activity_id = post_url.split(activity_tag)[-1].split(tag_end)[0]\n", + " \n", + " # Send like\n", + " url = f\"https://www.linkedin.com/voyager/api/voyagerSocialDashReactions?threadUrn=urn%3Ali%3Aactivity%3A{activity_id}\"\n", + " payload = {\"reactionType\": reaction_like}\n", + " res = requests.post(url, headers=headers, cookies=cookies, json=payload)\n", + " if res:\n", + " if res.status_code == 201:\n", + " print(\"👍 Like sent to:\", post_url)\n", + " else:\n", + " print(res.json())\n", + " return res" + ] + }, + { + "cell_type": "markdown", + "id": "fc9a3d8f-7b93-4c87-b4b1-fcdbe6482552", + "metadata": { + "papermill": {}, + "tags": [] + }, + "source": [ + "## Output" + ] + }, + { + "cell_type": "markdown", + "id": "624ab8d9-e42e-4fd9-be56-eab2834e49c1", + "metadata": {}, + "source": [ + "### Send like to latest profile posts published" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1cb70284-fecd-4df6-8dfe-8d0a2be3543f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def create_sha_256_hash(message):\n", + " # Encode the message to bytes\n", + " message_bytes = message.encode()\n", + "\n", + " # Create the hash object\n", + " sha_256_hash = hashlib.sha256(message_bytes)\n", + "\n", + " # Return the hexadecimal digest of the hash\n", + " return sha_256_hash.hexdigest()\n", + "\n", + "def is_file_exists(dir_path, file_name):\n", + " file_path = os.path.join(dir_path, file_name)\n", + " try:\n", + " with open(file_path):\n", + " return True\n", + " except IOError:\n", + " return False\n", + "\n", + "def save_file(dir_path, file_name):\n", + " # Create dir\n", + " if not os.path.exists(dir_path):\n", + " os.makedirs(dir_path)\n", + " \n", + " # Create file path\n", + " file_path = os.path.join(dir_path, file_name)\n", + " \n", + " # Open the file in write mode. This will create the file if it doesn't exist.\n", + " with open(file_path, 'w') as f:\n", + " pass # Do nothing\n", + "\n", + "# Loop on profile\n", + "for profile in profiles_url:\n", + " # Get last post\n", + " df = LK.profile.get_posts_feed(profile, count=1, limit=1)\n", + " if len(df) > 0:\n", + " # Get post URL\n", + " post_url = df.loc[0, \"POST_URL\"]\n", + " \n", + " # Transform URL to SHA\n", + " sha = create_sha_256_hash(post_url)\n", + " \n", + " # Check if file exists\n", + " if not is_file_exists(dir_path, sha):\n", + " send_like(\n", + " cookies,\n", + " headers,\n", + " post_url,\n", + " reaction_type\n", + " )\n", + " else:\n", + " print(\"👌 You already liked this post: \", post_url)" + ] + }, + { + "cell_type": "markdown", + "id": "5175a9ff-dd99-452a-9f07-dd54a2213ab0", + "metadata": {}, + "source": [ + "### Add scheduler" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8300a9e7-7250-48eb-8c2c-e17fd8138502", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "naas.scheduler.add(cron=cron)\n", + "\n", + "# Delete scheduler\n", + "# naas.scheduler.delete()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.6" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}