<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jasmeet Singh</title>
    <description>The latest articles on DEV Community by Jasmeet Singh (@jasmeet_singh_4c2c880bacd).</description>
    <link>https://dev.to/jasmeet_singh_4c2c880bacd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3705251%2F042f1d24-a040-4439-be3e-9eff9b42f6f9.png</url>
      <title>DEV Community: Jasmeet Singh</title>
      <link>https://dev.to/jasmeet_singh_4c2c880bacd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jasmeet_singh_4c2c880bacd"/>
    <language>en</language>
    <item>
      <title>I Built an AI Bot That Writes and Posts to LinkedIn While I Sleep — Here's How</title>
      <dc:creator>Jasmeet Singh</dc:creator>
      <pubDate>Sun, 11 Jan 2026 12:40:48 +0000</pubDate>
      <link>https://dev.to/jasmeet_singh_4c2c880bacd/i-built-an-ai-bot-that-writes-and-posts-to-linkedin-while-i-sleep-heres-how-107g</link>
      <guid>https://dev.to/jasmeet_singh_4c2c880bacd/i-built-an-ai-bot-that-writes-and-posts-to-linkedin-while-i-sleep-heres-how-107g</guid>
      <description>&lt;p&gt;How I automated my LinkedIn presence using Python, Groq AI, and GitHub Actions (and why you should too)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: LinkedIn Consistency is Hard
&lt;/h2&gt;

&lt;p&gt;Let's be honest — maintaining a consistent LinkedIn presence is exhausting.&lt;/p&gt;

&lt;p&gt;As an Android developer building healthcare apps during the day, the last thing I want to do at night is brainstorm "thought leadership" content. But I knew LinkedIn was crucial for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building my professional network&lt;/li&gt;
&lt;li&gt;Showcasing my expertise&lt;/li&gt;
&lt;li&gt;Getting noticed by recruiters&lt;/li&gt;
&lt;li&gt;Long-term career growth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The irony? I'm a developer. &lt;strong&gt;If something is repetitive, I should automate it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I built a bot that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ Finds trending Android topics automatically&lt;/li&gt;
&lt;li&gt;✅ Uses AI to generate engaging posts&lt;/li&gt;
&lt;li&gt;✅ Posts to my LinkedIn profile&lt;/li&gt;
&lt;li&gt;✅ Runs weekly on autopilot via GitHub Actions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; I wake up every Wednesday to a fresh LinkedIn post that I never wrote.&lt;/p&gt;

&lt;p&gt;Let me show you how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: How It Actually Works
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxx9pbf9gcdvoorrtsawz.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxx9pbf9gcdvoorrtsawz.jpeg" alt="Flowchart of the automation" width="784" height="1168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simple, right? But the devil is in the details. Let me break down each component.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Finding Trending Topics (DuckDuckGo API)
&lt;/h2&gt;

&lt;p&gt;The first challenge: &lt;strong&gt;How do you know what's trending?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I tried multiple approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Google Trends API (requires API key, rate limited)&lt;/li&gt;
&lt;li&gt;❌ Twitter API (expensive, and it's X now)&lt;/li&gt;
&lt;li&gt;❌ Reddit API (too noisy, hard to filter)&lt;/li&gt;
&lt;li&gt;✅ DuckDuckGo Search (free, no API key, instant results)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;duckduckgo_search&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DDGS&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_trending_topics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Search for trending topics across multiple queries&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;all_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;DDGS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ddgs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ddgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="n"&gt;max_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wt-wt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Worldwide
&lt;/span&gt;                    &lt;span class="n"&gt;safesearch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;off&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;timelimit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;# Last week only
&lt;/span&gt;                &lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;all_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search error for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;all_results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What I Search For
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SEARCH_QUERIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Android development trends 2025&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Kotlin new features latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jetpack Compose updates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Android Studio tips&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mobile app development best practices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;timelimit='w'&lt;/code&gt; ensures only recent content (last week)&lt;/li&gt;
&lt;li&gt;Multiple queries cast a wider net&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;region='wt-wt'&lt;/code&gt; gets global trends, not just local&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Customize these queries for your niche. If you're in React, search for "React 19 features", "Next.js updates", etc.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: AI Content Generation (Groq + LLaMA 70B)
&lt;/h2&gt;

&lt;p&gt;Now comes the magic: &lt;strong&gt;Getting AI to write like a human.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I experimented with multiple AI providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ OpenAI GPT-4 (expensive at scale)&lt;/li&gt;
&lt;li&gt;❌ Anthropic Claude (also pricey)&lt;/li&gt;
&lt;li&gt;❌ Google Gemini (inconsistent quality)&lt;/li&gt;
&lt;li&gt;✅ Groq + LLaMA 70B (fast, free tier, excellent quality)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Groq?
&lt;/h3&gt;

&lt;p&gt;Groq's LPU (Language Processing Unit) is &lt;strong&gt;insanely fast&lt;/strong&gt;. My posts generate in under 2 seconds. Compare that to 10-15 seconds on other platforms.&lt;/p&gt;

&lt;p&gt;Plus: Their free tier is generous (14,400 requests/day with LLaMA 70B).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Prompt Engineering
&lt;/h3&gt;

&lt;p&gt;This is where 90% of the magic happens. Here's my system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a professional Android developer who writes 
engaging LinkedIn posts. Your writing style is:

- Clear and conversational (not corporate jargon)
- Technical but accessible
- Uses real examples from production work
- Includes practical takeaways
- 3-5 sentences max (LinkedIn attention spans are short)
- Ends with a relevant question to drive engagement
- Uses 2-3 hashtags (no more!)

You specialize in:
- Android SDK and Kotlin
- Jetpack Compose
- Mobile architecture patterns
- Performance optimization
- Developer productivity

Never use:
- Emojis (except strategically, max 1-2)
- Buzzwords like &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;game-changer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revolutionary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
- Generic advice everyone already knows
- Anything that sounds like it was written by AI

Write as if you&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;re sharing a quick insight with fellow developers 
over coffee.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Content Generation Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;groq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Groq&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_post_with_ai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dry_run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Use Groq AI to pick best topic and generate post&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Format topics for the prompt
&lt;/span&gt;    &lt;span class="n"&gt;topics_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;   &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topics&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;From these trending topics:

&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topics_text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Pick the MOST interesting one for Android developers and write a 
LinkedIn post about it. Remember: technical but conversational, 
3-5 sentences, end with a question.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Groq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama-3.3-70b-versatile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# More creative
&lt;/span&gt;        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key parameters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;temperature=0.8&lt;/code&gt; — Higher = more creative, lower = more predictable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_tokens=500&lt;/code&gt; — Keeps posts concise (LinkedIn sweet spot is 150-250 words)&lt;/li&gt;
&lt;li&gt;System prompt defines personality/style&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 3: Posting to LinkedIn (The API Nightmare)
&lt;/h2&gt;

&lt;p&gt;Oh boy. LinkedIn's API is... &lt;strong&gt;let's just say it's an adventure.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Authentication Dance
&lt;/h3&gt;

&lt;p&gt;LinkedIn uses OAuth 2.0, which requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating a LinkedIn App (Developer Portal)&lt;/li&gt;
&lt;li&gt;Getting client ID + secret&lt;/li&gt;
&lt;li&gt;Redirecting user for authorization&lt;/li&gt;
&lt;li&gt;Exchanging code for access token&lt;/li&gt;
&lt;li&gt;Storing token securely&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I created a separate script (&lt;code&gt;linkedin_post.py&lt;/code&gt;) to handle this one-time setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Posting Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post_to_linkedin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person_urn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Post content to LinkedIn&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.linkedin.com/v2/ugcPosts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Restli-Protocol-Version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;post_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;person_urn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lifecycleState&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUBLISHED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;specificContent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.linkedin.ugc.ShareContent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shareCommentary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shareMediaCategory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NONE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visibility&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.linkedin.ugc.MemberNetworkVisibility&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUBLIC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Post successful!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Gotchas I Discovered
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem #1: Token Expiration&lt;/strong&gt;&lt;br&gt;
LinkedIn access tokens expire after 60 days. Solution: Store expiry date and refresh proactively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem #2: Rate Limits&lt;/strong&gt;&lt;br&gt;
LinkedIn allows ~100 posts/day. For weekly automation, this is fine. But test carefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem #3: Person URN&lt;/strong&gt;&lt;br&gt;
You need your LinkedIn &lt;code&gt;person_urn&lt;/code&gt; (not just your profile URL). Get it via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.linkedin.com/v2/me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 4: Automation (GitHub Actions)
&lt;/h2&gt;

&lt;p&gt;The final piece: &lt;strong&gt;Running this automatically every week.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why GitHub Actions?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Free (2,000 minutes/month on free tier)&lt;/li&gt;
&lt;li&gt;✅ Cron scheduling built-in&lt;/li&gt;
&lt;li&gt;✅ Secure secrets management&lt;/li&gt;
&lt;li&gt;✅ No server maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Workflow File
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/weekly-post.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Weekly LinkedIn Post&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;  
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Manual trigger for testing&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;post-to-linkedin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.10'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
          &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run LinkedIn poster&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GROQ_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GROQ_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;LINKEDIN_ACCESS_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.LINKEDIN_ACCESS_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;LINKEDIN_PERSON_URN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.LINKEDIN_PERSON_URN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python linkedin_ai_poster.py&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit post history&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git config user.name "GitHub Actions Bot"&lt;/span&gt;
          &lt;span class="s"&gt;git config user.email "actions@github.com"&lt;/span&gt;
          &lt;span class="s"&gt;git add post_history.json&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "📝 New LinkedIn post published" || echo "No changes"&lt;/span&gt;
          &lt;span class="s"&gt;git push&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cron Syntax Explained
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'0 3 * * 3'
 │ │ │ │ │
 │ │ │ │ └── Day of week (3 = Wednesday)
 │ │ │ └──── Month (any)
 │ │ └────── Day of month (any)
 │ └──────── Hour (03 UTC)
 └────────── Minute (0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;My schedule:&lt;/strong&gt; Every Wednesday at 8:30 AM ITC &lt;/p&gt;

&lt;h3&gt;
  
  
  Storing Secrets
&lt;/h3&gt;

&lt;p&gt;In your GitHub repo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Settings → Secrets and variables → Actions&lt;/li&gt;
&lt;li&gt;Add these secrets:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GROQ_API_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LINKEDIN_ACCESS_TOKEN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LINKEDIN_PERSON_URN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Never commit these to your code!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Safety Features
&lt;/h2&gt;

&lt;p&gt;I added several safeguards to avoid looking like a spam bot:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Dry Run Mode
&lt;/h3&gt;

&lt;p&gt;Test without actually posting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python linkedin_ai_poster.py &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What topics were found&lt;/li&gt;
&lt;li&gt;What post would be generated&lt;/li&gt;
&lt;li&gt;NO actual posting&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Post History Tracking
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_post_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Track what we&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ve posted to avoid duplicates&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_history&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;post_content&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# First 100 chars
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;post_history.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents posting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicate content&lt;/li&gt;
&lt;li&gt;Same topics repeatedly&lt;/li&gt;
&lt;li&gt;Helps audit what's been posted&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Content Validation
&lt;/h3&gt;

&lt;p&gt;Before posting, I check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Post isn't empty&lt;/li&gt;
&lt;li&gt;✅ Post is 50-2000 characters&lt;/li&gt;
&lt;li&gt;✅ Post doesn't match recent history&lt;/li&gt;
&lt;li&gt;✅ Post contains at least one hashtag&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The Results:&lt;br&gt;
Sample Generated Posts:&lt;br&gt;
Post #1(Jetbrains previewing new features for ktor)&lt;br&gt;
Post#2(Moving from Java to Kotlin: An Android Developer’s Perspective)&lt;br&gt;
Total time invested: 0 minutes (after initial setup)&lt;/p&gt;
&lt;h3&gt;
  
  
  What Surprised Me
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AI quality is REALLY good&lt;/strong&gt; — Most posts needed zero editing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engagement is comparable to manual posts&lt;/strong&gt; — Sometimes even better&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;People ask questions&lt;/strong&gt; — The ending question drives real conversations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No one realizes it's automated&lt;/strong&gt; — (Until now, I guess! 😄)&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  The Challenges I Faced
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Challenge #1: AI Generated Generic Content
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Early versions sounded too much like AI — lots of "game-changing" and "revolutionary" language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Heavily refined the system prompt with explicit "never use" lists and examples of good posts.&lt;/p&gt;
&lt;h3&gt;
  
  
  Challenge #2: DuckDuckGo Rate Limiting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Occasionally got blocked for too many searches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Added exponential backoff and reduced search frequency (5 queries, not 20).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uniform&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_with_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ddgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;max_retries&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Exponential backoff
&lt;/span&gt;                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenge #3: LinkedIn Token Expiration(Upcoming)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Tokens expire every 60 days. Automation breaks silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Added expiry tracking + email notification (via GitHub Actions) when token is &amp;lt;7 days from expiration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Should You Build This?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Build this if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You struggle with LinkedIn consistency&lt;/li&gt;
&lt;li&gt;You're comfortable with Python&lt;/li&gt;
&lt;li&gt;You have 2-3 hours for initial setup&lt;/li&gt;
&lt;li&gt;You want to experiment with AI automation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Skip this if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You prefer fully manual, authentic posting&lt;/li&gt;
&lt;li&gt;You're uncomfortable with automation ethics&lt;/li&gt;
&lt;li&gt;You don't have time to monitor it initially&lt;/li&gt;
&lt;li&gt;Your industry requires very personalized content&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Ethics Question
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"Isn't this inauthentic?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wrestled with this. Here's my thinking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the bot does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Finds relevant, real topics&lt;/li&gt;
&lt;li&gt;✅ Generates posts in MY voice (via prompt engineering)&lt;/li&gt;
&lt;li&gt;✅ Posts to MY profile with MY name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What the bot doesn't do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Impersonate someone else&lt;/li&gt;
&lt;li&gt;❌ Spam or mislead&lt;/li&gt;
&lt;li&gt;❌ Post irrelevant content&lt;/li&gt;
&lt;li&gt;❌ Engage with others (I still do that manually)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My take:&lt;/strong&gt; It's a tool, like a spell-checker or Grammarly. It helps me maintain presence, but I'm still responsible for the content.&lt;/p&gt;

&lt;p&gt;I also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review posts weekly (dry run first)&lt;/li&gt;
&lt;li&gt;Respond to all comments personally&lt;/li&gt;
&lt;li&gt;Occasionally edit AI suggestions&lt;/li&gt;
&lt;li&gt;Stay transparent (this article!)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to Build Your Own
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Clone the Repo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/jasi381/LinkedInPostGenerator.git
&lt;span class="nb"&gt;cd &lt;/span&gt;LinkedInPostGenerator
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Get API Keys
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Groq (Free):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Visit &lt;a href="https://console.groq.com" rel="noopener noreferrer"&gt;https://console.groq.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign up&lt;/li&gt;
&lt;li&gt;Generate API key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;LinkedIn:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create LinkedIn App (&lt;a href="https://developer.linkedin.com" rel="noopener noreferrer"&gt;https://developer.linkedin.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Get client ID + secret&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;linkedin_post.py&lt;/code&gt; for OAuth flow&lt;/li&gt;
&lt;li&gt;Save tokens&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Test Locally
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dry run (no posting)&lt;/span&gt;
python linkedin_ai_poster.py &lt;span class="nt"&gt;--dry-run&lt;/span&gt;

&lt;span class="c"&gt;# Review the generated post&lt;/span&gt;
&lt;span class="c"&gt;# If it looks good, run for real:&lt;/span&gt;
python linkedin_ai_poster.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Set Up Automation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Fork the repo&lt;/li&gt;
&lt;li&gt;Add secrets to GitHub Actions&lt;/li&gt;
&lt;li&gt;Customize workflow schedule&lt;/li&gt;
&lt;li&gt;Enable Actions in repo settings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;That's it!&lt;/strong&gt; Your bot is live.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;This project taught me something important: &lt;strong&gt;Automation isn't about replacing authenticity — it's about enabling consistency.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As developers, we have skills that most professionals don't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API integration&lt;/li&gt;
&lt;li&gt;AI prompt engineering&lt;/li&gt;
&lt;li&gt;CI/CD workflows&lt;/li&gt;
&lt;li&gt;System automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why not use these skills for career growth?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This LinkedIn bot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saves me 2+ hours weekly&lt;/li&gt;
&lt;li&gt;Maintains my professional presence&lt;/li&gt;
&lt;li&gt;Showcases my automation skills&lt;/li&gt;
&lt;li&gt;Generates opportunities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;And now, it's a portfolio piece too.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Full code is on GitHub: &lt;a href="https://github.com/jasi381/LinkedInPostGenerator" rel="noopener noreferrer"&gt;repo link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What would you automate next?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm thinking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cover letter generator (for job applications)&lt;/li&gt;
&lt;li&gt;GitHub contribution tracker (auto-posts weekly stats)&lt;/li&gt;
&lt;li&gt;Dev.to article sync (cross-platform publishing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop your ideas in the comments! 👇&lt;/p&gt;

</description>
      <category>python</category>
      <category>automation</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
