Data Flow from GitHub Starred Events to Email Notifications: HelloGitHub Bot Architecture

The HelloGitHub bot automates a five-stage pipeline that polls the GitHub received_events API, isolates WatchEvent "started" actions, enriches them with live repository star counts, and delivers a formatted HTML digest via SMTP email.

The 521xueweihan/HelloGitHub repository contains a Python automation script that implements this data flow from GitHub starred events to email notifications. Located in script/github_bot/github_bot.py, the utility enables developers to receive daily digests titled "今日 GitHub 热点" without manually monitoring GitHub activity streams.

Stage 1: Fetching Raw Events from the GitHub API

The pipeline begins in get_all_data() (lines 70‑101), which paginates through the GitHub received_events API endpoint. The function repeatedly calls get_data(page) to collect up to 300 recent activity items associated with the configured user account. Each page request returns a JSON array of event objects that the bot stores for downstream processing.


# Core fetching logic in script/github_bot/github_bot.py

def get_all_data():
    page = 1
    all_events = []
    while page < 4:  # Caps at 300 items (3 pages × 100 items)

        data = get_data(page)
        if not data:
            break
        all_events.extend(data)
        page += 1
    return all_events

Stage 2: Filtering for Starred Events

Once raw events are collected, check_condition() (lines 104‑119) filters the stream to isolate relevant starred activity. The function retains only WatchEvent types where the action field equals "started", indicating a user starred a repository. It applies a temporal filter using the DAY constant (default 1 day) to exclude stale events, and explicitly drops any activity originating from the bot's own repository to prevent self-referential noise.

def check_condition(event):
    created_at = event.get('created_at')
    date_time = get_local_time(created_at)
    now = get_now()
    if (now - date_time).days > DAY:
        return False
    if event.get('type') == 'WatchEvent' and \
       event.get('payload', {}).get('action') == 'started':
        return True
    return False

Stage 3: Enriching Repository Metadata

After filtering, get_stars() (lines 134‑162) performs data enrichment by issuing a second API call to each repository's dedicated endpoint. This retrieves the current stargazers_count to ensure the digest reflects live statistics. The function constructs a dictionary containing user, avatar_url, repo_name, and date_time, then applies the STARS threshold (default 100) to drop low-visibility projects. Notably, repositories returning an unknown star count (-1) are preserved to prevent data loss from API errors.

def get_stars(events):
    star_list = []
    for event in events:
        repo_name = event.get('repo', {}).get('name')
        # Secondary API call for live star count

        stars = get_repo_stars(repo_name)
        if stars != -1 and stars < STARS:
            continue
        star_list.append({
            'user': event.get('actor', {}).get('login'),
            'avatar_url': event.get('actor', {}).get('avatar_url'),
            'repo_name': repo_name,
            'date_time': get_local_time(event.get('created_at')),
            'stars': stars
        })
    return star_list

Stage 4: Rendering the HTML Email Body

With enriched data ready, make_content() (lines 165‑184) iterates over the list to generate the email body. The function formats each entry into an HTML table row (<tr>) containing the user's avatar image, profile link, repository link, starred timestamp, and current star count. These rows are injected into the CONTENT_FORMAT template, which defines the table structure and styling for the final digest.

def make_content(star_list):
    content = ''
    for info in star_list:
        content += f"""
        <tr>
            <td><img src={info['avatar_url']} width=32px></img></td>
            <td><a href=https://github.com/{info['user']}>{info['user']}</a></td>
            <td><a href=https://github.com/{info['repo_name']}>{info['repo_name']}</a></td>
            <td>{info['date_time']}</td>
            <td>{info['stars']}</td>
        </tr>
        """
    return CONTENT_FORMAT.format(content=content)

Stage 5: Delivering Email via SMTP

The final stage executes in send_email() (lines 186‑203), which constructs a MIME-HTML message using the rendered table. The function connects to an SSL-authenticated SMTP server—defaulting to QQ mail—and transmits the digest to all addresses defined in the RECEIVERS list. This completes the automated delivery of GitHub starred events to the recipient's inbox.

def send_email(content):
    message = MIMEText(content, 'html', 'utf-8')
    message['Subject'] = '今日 GitHub 热点'
    message['From'] = MAIL['mail']
    message['To'] = ', '.join(RECEIVERS)
    
    with smtplib.SMTP_SSL(MAIL['server'], MAIL['port']) as server:
        server.login(MAIL['username'], MAIL['password'])
        server.sendmail(MAIL['mail'], RECEIVERS, message.as_string())

End-to-End Data Flow Architecture

The complete pipeline flows through distinct functional layers as implemented in script/github_bot/github_bot.py:

  1. GitHub API (received_events)get_all_data() fetches up to 300 raw events
  2. Filter Layercheck_condition() isolates WatchEvent with action="started" within the last 24 hours
  3. Enrichment Layerget_stars() retrieves live stargazers_count and applies the 100-star minimum threshold
  4. Presentation Layermake_content() renders the HTML table digest
  5. Transport Layersend_email() delivers via SSL SMTP

When executed via python script/github_bot/github_bot.py, the script orchestrates these stages sequentially, transforming raw GitHub activity into a structured email notification.

Configuration and Usage

Deploy the bot by configuring the constants at the top of script/github_bot/github_bot.py and executing the script:


# 1. Configure credentials in the script header:

#    ACCOUNT = {'username': 'your_github_user', 'password': 'token'}

#    MAIL = {'server': 'smtp.qq.com', 'port': 465, ...}

#    RECEIVERS = ['admin@example.com']

#    STARS = 100  # Minimum star threshold

#    DAY = 1      # Lookback window in days

# 2. Run the automation

python script/github_bot/github_bot.py

Summary

  • The bot polls the GitHub received_events API (up to 300 items) via get_all_data() in script/github_bot/github_bot.py (lines 70‑101).
  • check_condition() (lines 104‑119) isolates WatchEvent records with action="started" from the last 24 hours while excluding the bot's own repository.
  • get_stars() (lines 134‑162) filters repositories by the STARS threshold (default 100) and enriches data with live stargazers_count via secondary API calls.
  • make_content() (lines 165‑184) generates an HTML table digest titled "今日 GitHub 热点" with avatar images and repository metadata.
  • send_email() (lines 186‑203) transmits the MIME-HTML message through SSL-authenticated SMTP to configured receivers.

Frequently Asked Questions

What GitHub API endpoint does the HelloGitHub bot use to detect starred events?

The bot queries the received_events endpoint for the configured user account, implemented in get_data(page) at lines 70‑101 of script/github_bot/github_bot.py. This endpoint returns public activity events from users the account follows, including WatchEvent actions that indicate starring behavior.

How does the bot determine which starred repositories to include in the email?

The check_condition() function (lines 104‑119) filters for WatchEvent types where payload.action equals "started" and the event occurred within the DAY window (default 1 day). Subsequently, get_stars() (lines 134‑162) drops repositories with fewer than STARS (100) stars, though entries with unknown counts (-1) are retained to ensure API errors do not silence legitimate notifications.

What email format and delivery method does the bot use?

The bot constructs a MIME-HTML message containing a formatted table with columns for avatar, username, repository name, starred date, and star count. The send_email() function (lines 186‑203) delivers the digest via SSL-authenticated SMTP, defaulting to QQ mail servers but configurable for any SMTP provider.

Can I customize the minimum star threshold or time window for the digest?

Yes. The script defines modular constants at the top of script/github_bot/github_bot.py: STARS (default 100) sets the minimum repository popularity, and DAY (default 1) controls the lookback period for event filtering. Adjust these values before execution to tailor the notification criteria to your needs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →