This Python project opens a real Chromium/Chrome browser, signs into LinkedIn, and performs everyday actions for you—posting, scheduling, uploading images, tagging people, liking, commenting, and running an AI-powered engagement loop. Everything is driven from the command line; no hidden APIs, no browser extensions: the bot simply automates the official web UI with Selenium.
- 1. Quick Start
- 2. Features At A Glance
- 3. Install & Configure
- 4. Common Workflows
- 5. Engage Stream Safety & De-duplication
- 6. AI Notes
- 7. Configuration Reference
- 8. Safety Checklist
- 9. Project Structure
- 10. Contributing
- 11. Changelog Highlights
- 12. License
pip install -r requirements.txt
cat <<'ENV' > .env
LINKEDIN_USERNAME=you@example.com
LINKEDIN_PASSWORD=yourLinkedInPassword
OPENAI_API_KEY=sk-your-openai-key # optional (AI comments)
# HEADLESS=false # uncomment to watch the browser
ENV
python main.py --post-text "Hello LinkedIn!" --no-ai --debug --headless=falseAdd --headless=false the first few runs so you can watch what’s happening. Abort with Ctrl+C if anything looks wrong—the browser closes automatically.
| Capability | What it does |
|---|---|
| Post text | Publish immediately with --post-text (skip AI with --no-ai). |
| Attach images | Use --image (repeatable) or --images-dir; bot uploads via the hidden file input. |
| Schedule | Pick date/time via --schedule-date mm/dd/yyyy and --schedule-time "10:45 AM". |
| Use/fallback AI | Use Gemini/OpenAI, your topic file, or local templates. Disable with --no-ai. |
| Content calendar | --generate-calendar creates a 30-day plan (appends to topics file; supports overwrite and defaults when optional fields are skipped). |
| Mentions | Inline @{Ada Lovelace}, anchor/name pairs, or auto-tag author (--mention-author). |
| Feed one-shots | Like/comment/repost the first visible feed item. |
| Engage stream | Scroll the feed, skip promos, like/comment repeatedly (--engage-stream). |
| AI summariser | Sumy condenses posts and logs the full summary before OpenAI writes a reply. |
| Human-like delays | Control pace with --delay-min/max and --scroll-wait-min/max. |
| Safety guards | Avoid duplicate comments/likes using URNs, hashes, DOM markers, and cached state. |
| Marketing mode | Automatically append promotional tails to AI-generated posts/comments with your project details. |
-
Dependencies
pip install -r requirements.txt
-
Environment variables (create
.env)LINKEDIN_USERNAME=you@example.com LINKEDIN_PASSWORD=yourLinkedInPassword OPENAI_API_KEY=sk-your-openai-key # optional (AI comments) GEMINI_API_KEY=your_gemini_key # optional (AI posts) HEADLESS=true # default; override per run USE_GEMINI=true # Marketing Mode (optional) MARKETING_MODE=true # Append promotional tails to AI content PROJECT_NAME=LinkedIn Bot PROJECT_URL=https://github.com/joeygoesgrey/linkedln-bot PROJECT_SHORT_PITCH=Human-like Selenium automation for LinkedIn
-
Run with help
python main.py --help
-
Watch the first run (
--headless=false --debug) to validate selectors and mention behaviour. -
Check logs in
logs/linkedin_bot_YYYYMMDD_HHMMSS.log. Useful tags:ENGAGE_KEYS,ENGAGE_SKIP,COMMENT_ORDER,MENTIONS_*,SCROLL_*.
python main.py --post-text "Hello LinkedIn 👋" --no-ai --debug --headless=falsepython main.py \
--post-text "Shipping screenshots" \
--images-dir ./static \
--headless=false --debug- Repeat
--imagefor exact files. Supported:.png,.jpg,.jpeg,.gif.
python main.py \
--post-text "See you tomorrow" \
--schedule-date 09/16/2025 \
--schedule-time "10:45 AM" \
--headless=false --debugpython main.py \
--post-text "Thanks @{Ada Lovelace}!" \
--headless=false --debug --no-aior use anchor/name pairs:
python main.py \
--post-text "Thanks for the push" \
--mention-anchor "for the push" \
--mention-name "Ada Lovelace"python main.py --like-first
python main.py --comment-first "Nice take!" --mention-authorpython main.py \
--repost-first \
--repost-thoughts "My take 👇" \
--mention-author \
--author-mention-position appendpython main.py pursue "Lara Acosta" \
--max-posts 5 \
--perspectives insightful professional \
--bio-keywords investor venture startup advisor \
--debug --headless=false- Finds the profile via search (optionally filtering by
--bio-keywords). - Clicks Show all posts to open the
recent-activity/allview. - Scrolls the profile’s posts feed, liking and (optionally) commenting on the first
--max-postsvisible items without navigating away. - Comments use AI when an OpenAI key is configured; otherwise fall back to the default text. Mentions of the author are prepended automatically.
- Respect LinkedIn rate limits—start with small caps (e.g.,
--max-posts 3) while observing in headful mode.
python main.py \
--generate-calendar \
--calendar-niche "fitness" \
--calendar-goal "educate busy professionals about at-home fitness" \
--calendar-audience "busy professionals aged 25-40 who struggle to find workout time" \
--calendar-tone inspirational \
--calendar-content-type "educational tips" \
--calendar-content-type "motivational stories" \
--calendar-frequency "daily posts" \
--calendar-hashtag "FitnessMotivation" \
--calendar-hashtag "HomeWorkout" \
--calendar-inspiration "Kayla Itsines, Chris Hemsworth" \
--calendar-personal-story "How fitness improved the founder's mental health" \
--calendar-output Topics.txt \
--calendar-overwrite- Required flags:
--calendar-niche,--calendar-goal,--calendar-audience. - Optional flags let you tailor tone, content formats, frequency, total posts, hashtags, inspirational accounts, and personal stories. If you skip them the generator falls back to neutral defaults (e.g., “a variety of formats”, “relevant hashtags”).
- By default the plan appends to
--topics-file; use--calendar-outputto choose another file and--calendar-overwriteto replace existing content. - Each idea is written on its own line so you can feed it back into the posting workflow later.
python main.py \
--engage-stream both \
--stream-ai \
--max-actions 5 \
--mention-author \
--headless=false --debug- Without
--stream-ai, supply--stream-comment "Great point!". --infiniteruns untilCtrl+C.- Adjust pacing:
--delay-min 2 --delay-max 5,--scroll-wait-min 2 --scroll-wait-max 4.
During AI runs you’ll see lines like:
ENGAGE_AI summary: As developers, it’s easy to fall into the trap…
COMMENT_ORDER mention=prepend
MENTIONS_SELECT prefer_first=yes selected=True
These confirm the summary and mention steps succeeded.
- Order: comment first, then like. In comment-only mode, a courtesy Like is added but not counted.
- De-dup cache: URNs and text hashes persist in
logs/engage_state.json(7-day TTL). Delete this file to reset history. - Skip logic: checks for prior likes, existing “You” comments, and similar comment text before posting.
- Promoted posts: skipped unless
--include-promoted. - Mentions: author mention is forced to the caret start when using AI, ensuring LinkedIn’s typeahead picks the first suggestion. If typeahead fails, the bot leaves a plain
@nameso text still reads naturally. - Topic history: successfully posted topics are removed from the source file and appended (with timestamps) to
<topics>_posted.<ext>so you always know what has gone out.
Troubleshooting duplicates:
- Run headful with
--debugand a small cap. - Inspect the log for
ENGAGE_SKIP reason=...to see why a post was skipped or processed. - Make sure the author name appears in the feed DOM; adjust selectors in
linkedin_ui/engage_dom.pyif you notice missing names.
- Summaries: Sumy TextRank condenses long posts and normalises whitespace (no newlines/excess spaces) before hitting OpenAI.
- Comments:
openai_client.pyuses style hints; you can tweak them to allow emojis, change tone, or shorten responses. - Token billing: everything you send (including spaces and line breaks) counts as tokens.
- Fallbacks: if OpenAI/Gemini fail, the bot falls back to
CUSTOM_POSTS_FILEtemplates, then to randomised phrases.
Click the image to open the live LinkedIn post that was created by this automation.
The bot also drives meaningful engagement—this screenshot shows one of its comments reaching over 600 impressions.
- Every module, class, and function now ships with Why/When/How docstrings so you can
help()anything in the stack and understand intent quickly. - Re-run the bot with
--debugto see the enriched logging alongside the new structured docs if you are auditing behaviour. - Export docs into plain text with:
The output mirrors the new docstring format (What/Why/When/How, Args, Returns), making it easy to embed in your own runbooks.
python - <<'PY' import inspect from linkedin_bot import LinkedInBot print(inspect.getdoc(LinkedInBot.post_custom_text)) PY
| Location | Purpose |
|---|---|
.env |
Credentials, API keys, default headless mode, etc. |
config.py |
Constants (selectors, timeouts, logging format, user agent). |
linkedin_ui/ |
Modular Selenium mixins (login, composer, mentions, engage, etc.). |
logs/ |
Run logs + engage cache (engage_state.json). |
requirements.txt |
Python dependencies. |
Reset the engage cache by deleting logs/engage_state.json.
- Follow LinkedIn’s Terms. Use humane delays and sensible limits.
- Start with
--headless=false --debugso you understand each step. - Store
.envsecurely. Consider a separate account for testing. - Review logs periodically to confirm mention placement and summary output.
├── main.py # CLI entry point
├── config.py # Central configuration
├── linkedin_bot.py # High-level orchestrator
├── linkedin_ui/
│ ├── engage.py # Orchestrator, context builder
│ ├── engage_flow.py # Engage loop + AI summariser/comment
│ ├── engage_dom.py # DOM helpers (author lookup, mention support)
│ ├── engage_utils.py # Utilities (pauses, perspectives, summariser)
│ ├── engage_types.py # Dataclasses for engage context
│ ├── ... # login.py, composer.py, mentions.py, etc.
├── openai_client.py # OpenAI prompt helpers
├── content_generator.py # Gemini/local post generation
├── logs/ # Session logs + engage_state.json
└── static/ # Example images
We welcome fixes, docs improvements, and new features. Before you start, read
CONTRIBUTING.md for the full workflow:
- Reproduce the issue headfully with
--debugand grab logs fromlogs/linkedin_bot_<timestamp>.log. - Open an issue describing the problem or proposal.
- Submit a focused pull request that includes tests or manual verification, and update the README/help text if behaviour changes.
All contributors must follow the CODE_OF_CONDUCT.md.
Security-sensitive findings should be reported privately (see
SECURITY.md).
- Modularised LinkedIn UI logic under
linkedin_ui/. - Added AI summarisation and OpenAI comment generation with full summary logging.
- Hardened engage stream (comment-first, dedupe per URN/text hash, session cache).
- Restored mention reliability by reintroducing author extraction and caret control.
- Added CLI scheduling, explicit image attachment, and safety diagnostics (
SCROLL_*,ENGAGE_SKIP). - Expanded docstrings across the entire codebase with Why/When/How context for every module, class, and function to streamline onboarding and maintenance.
This project is released under the MIT License. You are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software, subject to the license terms.
Stay safe, automate responsibly, and enjoy reclaiming your time on LinkedIn!
If the project helps you, a shout-out or quick note on how you're using it goes a long way. I'm also available for consulting or deeper integration work—reach out via LinkedIn before we automate your next growth push.

