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

> Explore the HelloGitHub bot architecture detailing the data flow from GitHub starred events to email notifications through a five-stage automated pipeline.

- Repository: [削微寒/HelloGitHub](https://github.com/521xueweihan/HelloGitHub)
- Tags: architecture
- Published: 2026-02-25

---

**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`](https://github.com/521xueweihan/HelloGitHub/blob/main/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.

```python

# 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.

```python
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.

```python
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.

```python
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.

```python
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`](https://github.com/521xueweihan/HelloGitHub/blob/main/script/github_bot/github_bot.py):

1. **GitHub API (`received_events`)** → `get_all_data()` fetches up to 300 raw events
2. **Filter Layer** → `check_condition()` isolates `WatchEvent` with `action="started"` within the last 24 hours
3. **Enrichment Layer** → `get_stars()` retrieves live `stargazers_count` and applies the 100-star minimum threshold
4. **Presentation Layer** → `make_content()` renders the HTML table digest
5. **Transport Layer** → `send_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`](https://github.com/521xueweihan/HelloGitHub/blob/main/script/github_bot/github_bot.py) and executing the script:

```bash

# 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`](https://github.com/521xueweihan/HelloGitHub/blob/main/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`](https://github.com/521xueweihan/HelloGitHub/blob/main/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`](https://github.com/521xueweihan/HelloGitHub/blob/main/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.