{
    "version": "https://jsonfeed.org/version/1",
    "title": "Paing Thu Ta",
    "home_page_url": "https://paingthuta.dev",
    "feed_url": "https://paingthuta.dev/rss/feed.json",
    "description": "Paing Thu Ta is a full stack developer building real time systems and backend products with Node.js, PostgreSQL, Redis, React, and Swift.",
    "icon": "https://paingthuta.dev/favicon.ico",
    "author": {
        "name": "Paing Thu Ta"
    },
    "items": [
        {
            "id": "https://paingthuta.dev/articles/runner-up-alibaba-hackathon",
            "content_html": "<img src=\"/fixed/images/outfitmaxxing/OUTFIT-MAXING.png\" alt=\"OutfitMaxxing system flowchart showing Fast and Extended generation modes\" style=\"width:100%;max-width:700px;border-radius:8px\"/><p>Two weeks ago, I came across a LinkedIn post about the Bangkok Alibaba Cloud x AGICAFET Hackathon. I had just graduated and had time on my hands, so I thought why not. I called everyone I knew. Only Tris and Reon said yes.</p><p>This was going to be my first hackathon. I was nervous and had no idea what to expect, but I wanted to give it a real shot.</p><h2>How Team HotFix Came Together</h2><p>The first problem was finding the right team.</p><p>I was not just looking for people who could code. I wanted people I could trust for a stressful week, the kind where everyone is tired, things break at the worst time, and nobody has the energy to be dramatic about it. Min Myint Moh Soe (Tris) and Lin Myat Phyo (Reon) were exactly that. Once they were in, it felt real.</p><p>The original idea came from Tris. Reon handled the business side and thought more about how the project could grow after the competition. I took care of the technical architecture and tried to make sure we could ship something real by demo day. We called ourselves <strong>HotFix</strong>. At the time it sounded fun. Later it sounded a little too accurate.</p><p>After we submitted our pitch, four days passed without much happening. Then I got a call from the hackathon support team. They told me we had made it into the Top 8 and asked if we would be able to come on site for the final round.</p><p>I said yes right away.</p><p>After that call, we shifted into preparing for the final defense. From that call to the Grand Final, we had only three days left. That is not much time to build anything good. It is definitely not much time to build something you are supposed to show in front of judges.</p><h2>The Idea: OutfitMaxxing</h2><p>The project was called <strong>OutfitMaxxing</strong>.</p><p>The pitch was easy to explain. You upload a photo of your outfit, and the app gives you three alternative looks for different situations: <strong>Casual</strong>, <strong>Business</strong>, and <strong>Night Out</strong>. The idea was simple to explain: <strong>an AI stylist app that generated better outfit options from a photo.</strong></p><p>Building it was a different story.</p><p>We kept the scope tight because we had to. The user uploads a photo, picks preferences like gender, season, aesthetic, and color, then chooses either <strong>Fast</strong> or <strong>Extended</strong> mode.</p><p>Fast mode used Gemini 2.5 Flash Image and returned three outfit ideas right away. Extended mode did more work. It generated results with GPT-image-2, then sent them through Qwen3.6-plus to check the quality. If the result looked bad, it regenerated. Only the outputs that passed made it back to the user.</p><img src=\"/fixed/images/outfitmaxxing/Outfitmaxxing-flowchart.png\" alt=\"OutfitMaxxing system flowchart showing Fast and Extended generation modes\" style=\"width:100%;max-width:700px;border-radius:8px\"/><p>On the tech side, we used React 19, TypeScript, Vite, and Tailwind for the frontend. The backend was Express 5, TypeScript, and SQLite. For the AI models, we used Alibaba Qwen3.6-plus, Gemini 2.5 Flash Image, and gpt-image-2.</p><p>We spent that week building after classes, after work, late at night, whenever we could. By the morning of the Grand Final, the app was working.</p><p>Then, about an hour before everything started, the image generation feature broke.</p><h2>The Hour Before Everything</h2><p>That moment felt weirdly calm and stressful at the same time. There was no space to panic because panic would have wasted time.</p><p>The POV feature had broken in our last push, and we did not notice it before going to sleep. Then, about two hours before the hackathon started, we found out.</p><p>We split the work right away. Tris and I worked on getting the broken feature back up, while I also handled the testing side by checking the latest HotFix pushes to the branch. Reon kept the rest of the app stable and made sure we did not destroy something else while fixing one problem. On top of that, we had to decide whether to cut the POV feature or force it through.</p><p>We somehow got both working.</p><p>Not perfectly. Not cleanly. But enough.</p><p>When the event started, OutfitMaxxing was still alive, and honestly, that was already a win.</p><img src=\"/fixed/images/outfitmaxxing/team.jpeg\" alt=\"Team HotFix at the Bangkok Alibaba Cloud x AGICAFET Hackathon Grand Final\" style=\"width:100%;max-width:700px;height:420px;object-fit:cover;object-position:center;border-radius:8px\"/><h2>Five Minutes I Won&#x27;t Forget</h2><p>For the demo, P&#x27;Natdhanai Praneenatthavee, the CEO and CAIO of AGICAFET, volunteered to test the app live with his own outfit.</p><p>That part was not planned.</p><p>He stood there, uploaded the photo, picked the preferences, and waited while the app ran. Then the three results came back and the crowd reacted almost immediately. I do not think I fully processed the moment until later.</p><p>That was probably my favorite part of the whole day. Not because it was perfect, but because the feature we had been fighting with that same morning worked in front of everyone when it mattered.</p><img src=\"/fixed/images/outfitmaxxing/demo.png\" alt=\"Live demo of OutfitMaxxing at the hackathon\" style=\"width:100%;max-width:700px;border-radius:8px\"/><h2>An Unexpected AU Reunion</h2><p>In between presentations, we ran into two seniors from Assumption University, P&#x27;Krittamet Chuwongworaphinit and P&#x27;Sanpawat Sewsuwan, who built a project called AU Spark.</p><p>It was a small moment, but one I felt grateful for. Seeing people from my own university at the same competition made the whole experience feel more personal. It was nice to see AU represented there.</p><img src=\"/fixed/images/outfitmaxxing/reunion-with-seniors.png\" alt=\"Live demo of OutfitMaxxing at the hackathon\" style=\"width:100%;max-width:700px;border-radius:8px\"/><h2>Runner-Up</h2><p>We placed second and finished as Runner-Up in the Grand Final.</p><p>When they called our name, I mostly felt relief first. Then it turned into pride.</p><p>For my first hackathon, after one week of late nights and one terrible hour right before the event started, that result felt huge. It made all the stress feel worth it.</p><p>The prize was three Keychron K1 mechanical keyboards, which was a pretty nice bonus on top of everything else.</p><div style=\"display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px\"><img src=\"/fixed/images/outfitmaxxing/prize.png\" alt=\"Team HotFix receiving the Runner-Up award\" style=\"width:100%;height:100%;border-radius:8px;object-fit:cover\"/><img src=\"/fixed/images/outfitmaxxing/prize1.png\" alt=\"Keychron K1 mechanical keyboard prize\" style=\"width:100%;height:100%;border-radius:8px;object-fit:cover\"/></div><img src=\"/fixed/images/outfitmaxxing/certificate.png\" alt=\"OutfitMaxxing system flowchart showing Fast and Extended generation modes\" style=\"width:100%;max-width:700px;border-radius:8px\"/><h2>What I Actually Learned</h2><p>Pressure changes the way you think. When something breaks and time is almost gone, you stop caring about perfect code and start caring about what will work in the next ten minutes. I did not expect to feel that clearly in the moment, but I did.</p><p>The team mattered more than I realized going in. Tris had the vision. Reon handled everything outside the code. That made it easier for me to focus without trying to carry everything at once. A good team is not just people who can do the work. It is people who make it easier for everyone else to do theirs.</p><p>We did not build every feature we talked about, but we built enough to make the idea real and get it in front of judges. That was the right call.</p><p>Honestly, I liked the whole experience more than I expected. The pressure was exhausting, but it was also fun in a weird way. I want to do it again.</p><hr/><p>Thank you to Tris and Reon for saying yes when I asked them to join this hackathon with me. And thank you to Bangkok Alibaba Cloud and AGICAFET CO., LTD. for hosting the hackathon and making the whole experience memorable.</p>",
            "url": "https://paingthuta.dev/articles/runner-up-alibaba-hackathon",
            "title": "How We Built OutfitMaxxing in a Week and Placed Runner-Up at Alibaba Cloud x AGICAFET Hackathon",
            "summary": "How Our Team HotFix built OutfitMaxxing in a week, survived a last-minute failure, and finished as Runner-Up at the Bangkok Alibaba Cloud x AGICAFET Hackathon Grand Final.",
            "date_modified": "2025-04-26T00:00:00.000Z",
            "author": {
                "name": "Paing Thu Ta"
            }
        },
        {
            "id": "https://paingthuta.dev/articles/raspberry-jobfinder",
            "content_html": "<h2>The Idea</h2><p>Job hunting as a fresh grad CS student is a lot of work. Not the interview prep or the portfolio building, that part makes sense. The tedious part is the daily browsing. Opening LinkedIn, searching the same keywords, switching to Indeed, searching again, and trying to mentally filter through listings that are not really relevant to where you are in your career.</p><p>I wanted something that could handle that part for me. A personal assistant that runs quietly in the background, goes through the listings every morning, figures out which ones actually match my profile, and just tells me the ones worth looking at.</p><p>I had a Raspberry Pi sitting on my desk already running a local LLM. The pieces were kind of already there. So I put them together and built it.</p><h2>What the Bot Actually Does</h2><p>The system is pretty simple. It scrapes LinkedIn and Indeed on a schedule for roles matching my criteria (software engineer, Bangkok, entry-level or internship). Then it sends each listing to LLaMA via API to score how well it fits my profile. Finally it sends me a Telegram message with the top matches including the job title, company, a one-line summary, and a fit score.</p><p>No dashboard, no database admin panel. Just a clean Telegram notification with the jobs worth looking at, delivered automatically every morning.</p><h2>The Stack</h2><ul><li>Raspberry Pi 4 as the always-on host</li><li>Python for scraping, orchestration, and Telegram integration</li><li>LLaMA via API for job scoring and summarisation</li><li>python-telegram-bot for message delivery</li><li>cron to trigger everything on a schedule</li></ul><h2>Scraping LinkedIn and Indeed</h2><p>Both platforms actively resist scraping so this part required some care. For LinkedIn I used <code>requests</code> with rotating user-agent headers and added deliberate delays between requests to avoid triggering rate limits. The job search URL accepts query parameters for keywords, location, and date posted so it was straightforward to construct a targeted URL and parse the HTML with BeautifulSoup.</p><pre class=\"language-python\"><code class=\"language-python code-highlight\"><span class=\"code-line\"><span class=\"token keyword\">import</span> requests\n</span><span class=\"code-line\"><span class=\"token keyword\">from</span> bs4 <span class=\"token keyword\">import</span> BeautifulSoup\n</span><span class=\"code-line\"><span class=\"token keyword\">import</span> time<span class=\"token punctuation\">,</span> random\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token keyword\">def</span> <span class=\"token function\">scrape_linkedin</span><span class=\"token punctuation\">(</span>keyword<span class=\"token punctuation\">,</span> location<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    url <span class=\"token operator\">=</span> <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;https://www.linkedin.com/jobs/search/?keywords=</span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>keyword<span class=\"token punctuation\">}</span></span><span class=\"token string\">&amp;location=</span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>location<span class=\"token punctuation\">}</span></span><span class=\"token string\">&amp;f_TPR=r86400&quot;</span></span>\n</span><span class=\"code-line\">    headers <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token string\">&quot;User-Agent&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Mozilla/5.0 ...&quot;</span><span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">    response <span class=\"token operator\">=</span> requests<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> headers<span class=\"token operator\">=</span>headers<span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">    soup <span class=\"token operator\">=</span> BeautifulSoup<span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;html.parser&quot;</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">    jobs <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n</span><span class=\"code-line\">    <span class=\"token keyword\">for</span> card <span class=\"token keyword\">in</span> soup<span class=\"token punctuation\">.</span>select<span class=\"token punctuation\">(</span><span class=\"token string\">&quot;.job-search-card&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        title <span class=\"token operator\">=</span> card<span class=\"token punctuation\">.</span>select_one<span class=\"token punctuation\">(</span><span class=\"token string\">&quot;.base-search-card__title&quot;</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">        company <span class=\"token operator\">=</span> card<span class=\"token punctuation\">.</span>select_one<span class=\"token punctuation\">(</span><span class=\"token string\">&quot;.base-search-card__subtitle&quot;</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">        link <span class=\"token operator\">=</span> card<span class=\"token punctuation\">.</span>select_one<span class=\"token punctuation\">(</span><span class=\"token string\">&quot;a&quot;</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">        <span class=\"token keyword\">if</span> title <span class=\"token keyword\">and</span> company <span class=\"token keyword\">and</span> link<span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">            jobs<span class=\"token punctuation\">.</span>append<span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">                <span class=\"token string\">&quot;title&quot;</span><span class=\"token punctuation\">:</span> title<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">.</span>strip<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">                <span class=\"token string\">&quot;company&quot;</span><span class=\"token punctuation\">:</span> company<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">.</span>strip<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">                <span class=\"token string\">&quot;url&quot;</span><span class=\"token punctuation\">:</span> link<span class=\"token punctuation\">[</span><span class=\"token string\">&quot;href&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">        time<span class=\"token punctuation\">.</span>sleep<span class=\"token punctuation\">(</span>random<span class=\"token punctuation\">.</span>uniform<span class=\"token punctuation\">(</span><span class=\"token number\">1.5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3.0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">    <span class=\"token keyword\">return</span> jobs\n</span></code></pre><p>Indeed followed a similar pattern. Parse the search results page, extract the title, company, and URL from each listing card.</p><h2>Scoring with LLaMA</h2><p>Raw listings are noisy. A lot of &quot;Software Engineer&quot; roles are actually looking for five plus years of experience or a stack I have never touched. I did not want those cluttering my Telegram feed so every listing gets sent to LLaMA with a simple prompt.</p><pre class=\"language-python\"><code class=\"language-python code-highlight\"><span class=\"code-line\"><span class=\"token keyword\">def</span> <span class=\"token function\">score_job</span><span class=\"token punctuation\">(</span>job<span class=\"token punctuation\">,</span> my_profile<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    prompt <span class=\"token operator\">=</span> <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;&quot;&quot;\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">You are a career assistant. Given a job listing and a candidate profile, return a JSON object with:\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">- &quot;score&quot;: integer from 1-10 (fit score)\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">- &quot;summary&quot;: one sentence explaining why this role is or isn&#x27;t a good match\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">Job Title: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>job<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;title&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">Company: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>job<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;company&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">Candidate Profile:\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\"></span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>my_profile<span class=\"token punctuation\">}</span></span><span class=\"token string\">\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">Return only valid JSON. No extra text.\n</span></span></span><span class=\"code-line\"><span class=\"token string-interpolation\"><span class=\"token string\">&quot;&quot;&quot;</span></span>\n</span><span class=\"code-line\">    response <span class=\"token operator\">=</span> call_llama_api<span class=\"token punctuation\">(</span>prompt<span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">    <span class=\"token keyword\">return</span> parse_json_response<span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">)</span>\n</span></code></pre><p>The <code>my_profile</code> variable is a short plaintext description of my stack, graduation status, and what I am looking for. LLaMA returns a score and a one-line reason. Anything below a 6 gets dropped before it even reaches Telegram.</p><h2>The Telegram Notification</h2><p>This is honestly the best part. After scoring, the top listings get formatted and sent as a Telegram message.</p><pre class=\"language-python\"><code class=\"language-python code-highlight\"><span class=\"code-line\"><span class=\"token keyword\">from</span> telegram <span class=\"token keyword\">import</span> Bot\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">send_results</span><span class=\"token punctuation\">(</span>jobs<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    bot <span class=\"token operator\">=</span> Bot<span class=\"token punctuation\">(</span>token<span class=\"token operator\">=</span>TELEGRAM_BOT_TOKEN<span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">    message <span class=\"token operator\">=</span> <span class=\"token string\">&quot;🤖 *Today&#x27;s Job Matches*\\n\\n&quot;</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">    <span class=\"token keyword\">for</span> job <span class=\"token keyword\">in</span> jobs<span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        message <span class=\"token operator\">+=</span> <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;*</span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>job<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;title&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">* @ </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>job<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;company&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\\n&quot;</span></span>\n</span><span class=\"code-line\">        message <span class=\"token operator\">+=</span> <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;Score: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>job<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;score&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">/10 — </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>job<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;summary&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\\n&quot;</span></span>\n</span><span class=\"code-line\">        message <span class=\"token operator\">+=</span> <span class=\"token string-interpolation\"><span class=\"token string\">f&quot;[View listing](</span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>job<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;url&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">)\\n\\n&quot;</span></span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">    <span class=\"token keyword\">await</span> bot<span class=\"token punctuation\">.</span>send_message<span class=\"token punctuation\">(</span>\n</span><span class=\"code-line\">        chat_id<span class=\"token operator\">=</span>CHAT_ID<span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">        text<span class=\"token operator\">=</span>message<span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">        parse_mode<span class=\"token operator\">=</span><span class=\"token string\">&quot;Markdown&quot;</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">)</span>\n</span></code></pre><p>Every morning I wake up to a neat list of pre-filtered, LLM-scored job listings in my personal Telegram. No tab switching, no duplicates, just the ones worth my attention.</p><img src=\"/fixed/images/raspberry-jobfinder/img1.jpg\" alt=\"Raspberry Pi running the job finder bot\" style=\"width:100%;max-width:600px;border-radius:8px\"/><h2>Lessons Learned</h2><p>LLM scoring is surprisingly reliable for this use case. I expected it to be a fun gimmick but the assessments were genuinely useful. It caught mismatches I would have caught myself, just faster and before I had already clicked through three pages.</p><p>Scraping is a maintenance burden though. Both LinkedIn and Indeed change their HTML structure from time to time and that breaks the selectors. I have had to update the scraper twice already. If I rebuild this I would look at an unofficial jobs API or a scraping service to absorb that overhead.</p><p>Smaller scope also shipped faster. My original idea had a full scoring rubric, cover letter generation, and auto-apply functionality. I cut all of it and shipped the minimal version first: scrape, score, notify. That version runs reliably. The fancier ideas are still sitting on a list somewhere.</p><p>Running it on the Pi was the right call too. It is always on, costs nothing extra to run, and it keeps the project grounded as a tool I actually use rather than a side project deployed to the cloud and forgotten about.</p><h2>What I Would Do Differently</h2><p>If I started over I would add a simple SQLite database to track which listings I have already seen so the bot only surfaces genuinely new results. Right now there is some overlap between runs. I would also add a quick feedback mechanism, a Telegram button to mark a listing as applied or not interested, so the scoring prompt can be refined over time based on my actual decisions.</p><h2>Final Thought</h2><p>I am still job hunting as I write this. But somewhere along the way, building the assistant became just as valuable as what it was finding for me. It reminded me that I actually enjoy solving problems, even when the problem is my own situation.</p>",
            "url": "https://paingthuta.dev/articles/raspberry-jobfinder",
            "title": "I Built a Personal Job Hunting Assistant That Runs on My Raspberry Pi",
            "summary": "How I built a self-hosted assistant on a Raspberry Pi that scrapes LinkedIn and Indeed, scores listings with LLaMA, and sends me the best matches on Telegram every morning.",
            "date_modified": "2026-04-07T00:00:00.000Z",
            "author": {
                "name": "Paing Thu Ta"
            }
        }
    ]
}