<?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: Tiny Task Pro</title>
    <description>The latest articles on DEV Community by Tiny Task Pro (@tinytaskpro).</description>
    <link>https://dev.to/tinytaskpro</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4010994%2Ffa3739df-0d92-48cd-9e6d-f1e2add1fbfe.png</url>
      <title>DEV Community: Tiny Task Pro</title>
      <link>https://dev.to/tinytaskpro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tinytaskpro"/>
    <language>en</language>
    <item>
      <title>How I Built a Gmail Automation Chrome Extension Using JavaScript and Flask</title>
      <dc:creator>Tiny Task Pro</dc:creator>
      <pubDate>Wed, 01 Jul 2026 19:21:41 +0000</pubDate>
      <link>https://dev.to/tinytaskpro/how-i-built-a-gmail-automation-chrome-extension-using-javascript-and-flask-3af9</link>
      <guid>https://dev.to/tinytaskpro/how-i-built-a-gmail-automation-chrome-extension-using-javascript-and-flask-3af9</guid>
      <description>&lt;p&gt;Building browser &lt;a href="https://tinytask.org/" rel="noopener noreferrer"&gt;automation software&lt;/a&gt; sounds simple until you start dealing with real-world limitations.&lt;/p&gt;

&lt;p&gt;Over the past few months, I've been developing &lt;a href="https://tinytask.org/tinytask-mailer-for-outlook/" rel="noopener noreferrer"&gt;TinyTask Mailer Pro&lt;/a&gt;, a Chrome extension that automates Gmail campaigns while keeping the user in control. It integrates Google Sheets, manages campaign progress, supports licensing, and communicates with a Flask backend for usage tracking.&lt;/p&gt;

&lt;p&gt;This article isn't about sending spam or bypassing Gmail restrictions. Instead, it's about the engineering decisions, architecture, and lessons I learned while building a production-ready automation tool.&lt;/p&gt;

&lt;p&gt;Why Build a Gmail Automation Extension?&lt;/p&gt;

&lt;p&gt;Many small businesses, freelancers, recruiters, and sales teams spend hours performing repetitive tasks like:&lt;/p&gt;

&lt;p&gt;Opening Gmail&lt;br&gt;
Copying recipient emails&lt;br&gt;
Writing personalized messages&lt;br&gt;
Sending emails one by one&lt;br&gt;
Tracking progress manually&lt;/p&gt;

&lt;p&gt;I wanted to build a tool that reduces repetitive work while still keeping the user involved in the process.&lt;/p&gt;

&lt;p&gt;The goal was to save time—not replace responsible email practices.&lt;/p&gt;

&lt;p&gt;Tech Stack&lt;/p&gt;

&lt;p&gt;Here's what powers the project:&lt;/p&gt;

&lt;p&gt;Frontend&lt;br&gt;
HTML&lt;br&gt;
CSS&lt;br&gt;
JavaScript&lt;br&gt;
Chrome Extension APIs&lt;br&gt;
Backend&lt;br&gt;
Python&lt;br&gt;
Flask&lt;br&gt;
Gunicorn&lt;br&gt;
Nginx&lt;br&gt;
SQLite&lt;br&gt;
Deployment&lt;br&gt;
Ubuntu VPS&lt;br&gt;
HTTPS API&lt;br&gt;
License server&lt;/p&gt;

&lt;p&gt;This combination turned out to be lightweight, affordable, and easy to maintain.&lt;/p&gt;

&lt;p&gt;Overall Architecture&lt;br&gt;
Google Sheets&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
Chrome Extension&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
Gmail Web Interface&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
Flask API&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
SQLite Database&lt;/p&gt;

&lt;p&gt;The extension handles browser automation while the backend remains the source of truth for:&lt;/p&gt;

&lt;p&gt;License validation&lt;br&gt;
Machine activation&lt;br&gt;
Daily quotas&lt;br&gt;
Usage statistics&lt;br&gt;
Account management&lt;/p&gt;

&lt;p&gt;Keeping responsibilities separated made debugging significantly easier.&lt;/p&gt;

&lt;p&gt;Reading Data from Google Sheets&lt;/p&gt;

&lt;p&gt;Instead of importing CSV files repeatedly, the extension reads campaign data directly from Google Sheets.&lt;/p&gt;

&lt;p&gt;Typical columns include:&lt;/p&gt;

&lt;p&gt;Name&lt;br&gt;
Email&lt;br&gt;
Subject&lt;br&gt;
Message&lt;/p&gt;

&lt;p&gt;This allows users to edit campaigns without rebuilding or re-uploading anything.&lt;/p&gt;

&lt;p&gt;Whenever changes are made in Google Sheets, the extension can work with the updated data.&lt;/p&gt;

&lt;p&gt;Personalizing Every Email&lt;/p&gt;

&lt;p&gt;Nobody likes receiving generic emails.&lt;/p&gt;

&lt;p&gt;Each message supports placeholders like:&lt;/p&gt;

&lt;p&gt;Hello {Name},&lt;/p&gt;

&lt;p&gt;which become&lt;/p&gt;

&lt;p&gt;Hello Sarah,&lt;/p&gt;

&lt;p&gt;during the sending process.&lt;/p&gt;

&lt;p&gt;Simple personalization greatly improves the user experience while keeping implementation straightforward.&lt;/p&gt;

&lt;p&gt;Managing Long Campaigns&lt;/p&gt;

&lt;p&gt;One challenge appeared very quickly.&lt;/p&gt;

&lt;p&gt;What happens if:&lt;/p&gt;

&lt;p&gt;Chrome crashes?&lt;br&gt;
Windows restarts?&lt;br&gt;
The browser closes?&lt;br&gt;
The user clicks Stop?&lt;/p&gt;

&lt;p&gt;Losing campaign progress wasn't acceptable.&lt;/p&gt;

&lt;p&gt;Instead of storing temporary variables in memory, campaign progress is saved using chrome.storage.local.&lt;/p&gt;

&lt;p&gt;Information such as:&lt;/p&gt;

&lt;p&gt;Current row&lt;br&gt;
Emails sent&lt;br&gt;
Pending emails&lt;br&gt;
Delay settings&lt;br&gt;
Campaign status&lt;/p&gt;

&lt;p&gt;can all survive browser restarts.&lt;/p&gt;

&lt;p&gt;This became one of the most valuable improvements in the project.&lt;/p&gt;

&lt;p&gt;Building Resume Support&lt;/p&gt;

&lt;p&gt;Resume functionality sounds simple.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;The extension needs to restore:&lt;/p&gt;

&lt;p&gt;Current spreadsheet row&lt;br&gt;
Campaign status&lt;br&gt;
Delay timer&lt;br&gt;
Statistics&lt;br&gt;
Current Gmail account&lt;/p&gt;

&lt;p&gt;without accidentally sending duplicate emails.&lt;/p&gt;

&lt;p&gt;Carefully restoring state while preventing duplicates required much more testing than expected.&lt;/p&gt;

&lt;p&gt;Random Delays Matter&lt;/p&gt;

&lt;p&gt;Sending hundreds of emails instantly isn't realistic.&lt;/p&gt;

&lt;p&gt;Instead, every send operation waits for a randomized interval.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;7 seconds&lt;br&gt;
12 seconds&lt;br&gt;
9 seconds&lt;br&gt;
14 seconds&lt;/p&gt;

&lt;p&gt;rather than&lt;/p&gt;

&lt;p&gt;10&lt;br&gt;
10&lt;br&gt;
10&lt;br&gt;
10&lt;/p&gt;

&lt;p&gt;The goal isn't to "beat" Gmail—it's simply to avoid robotic timing and create a smoother workflow.&lt;/p&gt;

&lt;p&gt;Designing the License System&lt;/p&gt;

&lt;p&gt;I didn't want all business logic inside the extension.&lt;/p&gt;

&lt;p&gt;Instead, the backend validates every license.&lt;/p&gt;

&lt;p&gt;The Flask API checks:&lt;/p&gt;

&lt;p&gt;License validity&lt;br&gt;
Activated machine&lt;br&gt;
Daily limits&lt;br&gt;
Remaining quota&lt;br&gt;
Expiration status&lt;/p&gt;

&lt;p&gt;This keeps sensitive logic off the client side.&lt;/p&gt;

&lt;p&gt;Why Use Flask?&lt;/p&gt;

&lt;p&gt;I considered several backend frameworks.&lt;/p&gt;

&lt;p&gt;Flask won because it offers:&lt;/p&gt;

&lt;p&gt;Minimal setup&lt;br&gt;
Fast development&lt;br&gt;
Excellent documentation&lt;br&gt;
Easy REST API creation&lt;br&gt;
Great Python ecosystem&lt;/p&gt;

&lt;p&gt;For a lightweight licensing server, it was exactly what I needed.&lt;/p&gt;

&lt;p&gt;Keeping the Backend Simple&lt;/p&gt;

&lt;p&gt;The database is intentionally straightforward.&lt;/p&gt;

&lt;p&gt;Example tables include:&lt;/p&gt;

&lt;p&gt;Licenses&lt;br&gt;
Activations&lt;br&gt;
Gmail usage&lt;br&gt;
Admin users&lt;/p&gt;

&lt;p&gt;Simple schemas are often easier to maintain than highly complex designs.&lt;/p&gt;

&lt;p&gt;Problems I Didn't Expect&lt;/p&gt;

&lt;p&gt;Building browser automation introduced challenges I hadn't anticipated.&lt;/p&gt;

&lt;p&gt;Some examples:&lt;/p&gt;

&lt;p&gt;Gmail UI Changes&lt;/p&gt;

&lt;p&gt;Small interface updates can break selectors.&lt;/p&gt;

&lt;p&gt;Building resilient selectors became essential.&lt;/p&gt;

&lt;p&gt;Asynchronous Timing&lt;/p&gt;

&lt;p&gt;Browser automation depends heavily on waiting for elements.&lt;/p&gt;

&lt;p&gt;Too early?&lt;/p&gt;

&lt;p&gt;The element doesn't exist.&lt;/p&gt;

&lt;p&gt;Too late?&lt;/p&gt;

&lt;p&gt;The workflow becomes slow.&lt;/p&gt;

&lt;p&gt;Finding the right balance required many iterations.&lt;/p&gt;

&lt;p&gt;Extension State&lt;/p&gt;

&lt;p&gt;Managing state across popup scripts, background scripts, and content scripts was harder than expected.&lt;/p&gt;

&lt;p&gt;Keeping everything synchronized became one of the biggest engineering tasks.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;p&gt;A few lessons stand out.&lt;/p&gt;

&lt;p&gt;Keep State in One Place&lt;/p&gt;

&lt;p&gt;Duplicated state causes bugs.&lt;/p&gt;

&lt;p&gt;Using a single source of truth simplified debugging dramatically.&lt;/p&gt;

&lt;p&gt;Log Everything&lt;/p&gt;

&lt;p&gt;Detailed logging reduced debugging time from hours to minutes.&lt;/p&gt;

&lt;p&gt;Expect UI Changes&lt;/p&gt;

&lt;p&gt;If you're automating websites, assume they'll change.&lt;/p&gt;

&lt;p&gt;Design your automation to fail gracefully.&lt;/p&gt;

&lt;p&gt;Build for Recovery&lt;/p&gt;

&lt;p&gt;Users will eventually close their browser, lose internet connectivity, or restart their computer.&lt;/p&gt;

&lt;p&gt;Recovery is more important than perfection.&lt;/p&gt;

&lt;p&gt;Future Improvements&lt;/p&gt;

&lt;p&gt;The project is still evolving.&lt;/p&gt;

&lt;p&gt;Some features I'm exploring include:&lt;/p&gt;

&lt;p&gt;Better campaign analytics&lt;br&gt;
Improved scheduling&lt;br&gt;
More reporting options&lt;br&gt;
Enhanced dashboard metrics&lt;br&gt;
Additional workflow automation&lt;/p&gt;

&lt;p&gt;Every version teaches something new.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Building browser automation software has been one of the most rewarding projects I've worked on.&lt;/p&gt;

&lt;p&gt;It combines frontend development, backend APIs, browser internals, state management, deployment, and user experience into a single product.&lt;/p&gt;

&lt;p&gt;If you're interested in Chrome extension development, my advice is simple:&lt;/p&gt;

&lt;p&gt;Start with a small feature.&lt;/p&gt;

&lt;p&gt;Ship it.&lt;/p&gt;

&lt;p&gt;Test it with real users.&lt;/p&gt;

&lt;p&gt;Then improve it one iteration at a time.&lt;/p&gt;

&lt;p&gt;That's exactly how this project has grown.&lt;/p&gt;

&lt;p&gt;If you'd like to see the project that inspired this article, you can explore TinyTask Mailer Pro and other productivity tools at &lt;a href="https://tinytask.org" rel="noopener noreferrer"&gt;https://tinytask.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm also interested in hearing how others approach browser automation. What challenges have you run into while building Chrome extensions?&lt;/p&gt;

</description>
      <category>automation</category>
      <category>javascript</category>
      <category>python</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
