Skip to content

Commit 04ccf4e

Browse files
committed
base version (design by sunrisepeak / write by claude-code)
1 parent 3144321 commit 04ccf4e

File tree

12 files changed

+1061
-1
lines changed

12 files changed

+1061
-1
lines changed

.github/workflows/daily-scan.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Daily Bot Tasks
2+
3+
on:
4+
schedule:
5+
# Run daily at 02:00 China Time (18:00 UTC)
6+
- cron: "0 18 * * *"
7+
# Allow manual trigger
8+
workflow_dispatch:
9+
inputs:
10+
components:
11+
description: 'Components to run (all, join-issues, task-checker)'
12+
required: false
13+
default: 'all'
14+
type: choice
15+
options:
16+
- all
17+
- join-issues
18+
- task-checker
19+
20+
permissions:
21+
issues: write
22+
contents: read
23+
24+
jobs:
25+
run-bot:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v4
30+
31+
- name: Create GitHub App installation token
32+
id: app-token
33+
uses: actions/create-github-app-token@v1
34+
with:
35+
app-id: ${{ secrets.APP_ID }}
36+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
37+
owner: mcpp-community
38+
39+
- name: Setup Python
40+
uses: actions/setup-python@v5
41+
with:
42+
python-version: "3.11"
43+
44+
- name: Run MCPP Bot
45+
env:
46+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
47+
run: |
48+
# Determine which components to run
49+
COMPONENTS="${{ github.event.inputs.components || 'all' }}"
50+
51+
echo "Running MCPP Bot with components: $COMPONENTS"
52+
python src/main.py $COMPONENTS --verbose

LICENSE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
Copyright (c) 2026-present mcpp community, speakshen@163.com
2+
3+
---
4+
15
Apache License
26
Version 2.0, January 2004
37
http://www.apache.org/licenses/

README.md

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,115 @@
1-
# mcpp-bot
1+
# MCPP Bot
2+
3+
A collection of GitHub automation tools for managing repositories and organizations.
4+
5+
## Project Structure
6+
7+
```
8+
src/
9+
├── components/ # Bot components and modules
10+
│ ├── scan_join_issues.py # Process join requests
11+
│ ├── task_checker.py # Check and remind on overdue tasks
12+
│ └── README.md # Component documentation
13+
├── config/ # Configuration files
14+
│ ├── join-config.yml # Join request configuration
15+
│ └── task-checker.yml # Task checker configuration
16+
├── libs/ # Shared libraries
17+
│ ├── github_client.py # GitHub API client
18+
│ ├── utils.py # Utility functions
19+
│ └── __init__.py
20+
└── main.py # Main entry point
21+
```
22+
23+
## Components
24+
25+
### 1. Task Checker
26+
27+
Monitors task issues with priority labels (P0/P1/P2) and sends reminders when they haven't been updated within configured timeouts.
28+
29+
**Quick Start:**
30+
```bash
31+
export GH_TOKEN="your_github_token"
32+
python src/components/task_checker.py
33+
```
34+
35+
**Features:**
36+
- Scan single repository or entire organization
37+
- Customizable timeout thresholds per priority (P0/P1/P2)
38+
- Flexible reminder templates
39+
- Repository exclusion support
40+
- Configurable priority patterns
41+
42+
See [components/README.md](src/components/README.md) for detailed configuration options.
43+
44+
### 2. Join Request Scanner
45+
46+
Processes join request issues for organization membership management.
47+
48+
**Quick Start:**
49+
```bash
50+
export GH_TOKEN="your_github_token"
51+
python src/components/scan_join_issues.py
52+
```
53+
54+
## Quick Start
55+
56+
1. Clone the repository
57+
2. Set up your GitHub token:
58+
```bash
59+
export GH_TOKEN="your_github_personal_access_token"
60+
```
61+
3. Configure the components in `src/config/`
62+
4. Run the bot:
63+
```bash
64+
# Run all components
65+
python src/main.py all
66+
67+
# Run specific component
68+
python src/main.py join-issues
69+
python src/main.py task-checker
70+
71+
# Run multiple components
72+
python src/main.py join-issues task-checker
73+
74+
# Enable verbose error output
75+
python src/main.py all --verbose
76+
```
77+
78+
## Running Individual Components
79+
80+
You can also run components directly:
81+
82+
```bash
83+
# Task checker
84+
python src/components/task_checker.py
85+
86+
# Join issues scanner
87+
python src/components/scan_join_issues.py
88+
```
89+
90+
## Configuration
91+
92+
All components use YAML configuration files in `src/config/`:
93+
94+
- `join-config.yml` - Join request processing settings
95+
- `task-checker.yml` - Task monitoring and reminder settings
96+
97+
## Requirements
98+
99+
- Python 3.7+
100+
- GitHub Personal Access Token with appropriate permissions
101+
- For task checker: `repo` scope (read issues, write comments)
102+
- For join scanner: `admin:org` scope (manage organization members)
103+
104+
## GitHub Actions Workflows
105+
106+
This project includes automated workflows for scheduled execution:
107+
108+
- **Daily Bot Tasks** (`daily-scan.yml`): Runs all components daily at 02:20 UTC
109+
- **Task Checker Hourly** (`task-checker-hourly.yml`): Checks task timeouts every 6 hours
110+
111+
See [.github/workflows/README.md](.github/workflows/README.md) for workflow documentation.
112+
113+
## License
114+
115+
See [LICENSE](LICENSE) for details.

src/components/README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Components
2+
3+
This directory contains various bot components for managing GitHub repositories and organizations.
4+
5+
## Available Components
6+
7+
### 1. scan_join_issues.py
8+
9+
Scans and processes join request issues for organization membership.
10+
11+
**Usage:**
12+
```bash
13+
export GH_TOKEN="your_github_token"
14+
python src/components/scan_join_issues.py
15+
```
16+
17+
**Configuration:** `config/join-config.yml`
18+
19+
### 2. task_checker.py
20+
21+
Scans for task issues with priority labels (P0/P1/P2) and sends reminders if they haven't been updated within configured timeouts.
22+
23+
**Usage:**
24+
```bash
25+
export GH_TOKEN="your_github_token"
26+
python src/components/task_checker.py
27+
```
28+
29+
**Configuration:** `config/task-checker.yml`
30+
31+
**Features:**
32+
- Flexible scan modes: single repository or entire organization
33+
- Customizable timeout thresholds for P0/P1/P2 priorities
34+
- Customizable reminder message template
35+
- Repository exclusion support
36+
- Optional reminders for unassigned tasks
37+
38+
**Configuration Options:**
39+
40+
- `scan_mode`: Set to `"repo"` for single repository or `"org"` for organization-wide scanning
41+
- `timeout_hours`: Configure different timeout thresholds for each priority level
42+
- `P0`: High priority tasks (default: 24 hours)
43+
- `P1`: Medium priority tasks (default: 72 hours)
44+
- `P2`: Low priority tasks (default: 168 hours)
45+
- `priorities_to_check`: Limit which priorities to scan (e.g., only P0 and P1)
46+
- `exclude_repos`: Exclude specific repositories when scanning an organization
47+
- `task_label`: Customize the label that identifies task issues (default: "Task")
48+
- `priority_pattern`: Regex pattern to match priority labels
49+
- `reminder_template`: Customize the reminder message format
50+
- `notify_unassigned`: Whether to send reminders for tasks without assignees
51+
- `default_mention`: Default mentions for unassigned tasks (can be string or list)
52+
53+
**Example Scenarios:**
54+
55+
1. **Scan single repository:**
56+
```yaml
57+
scan_mode: repo
58+
repo: mcpp-community/mcpp-bot
59+
```
60+
61+
2. **Scan entire organization:**
62+
```yaml
63+
scan_mode: org
64+
org: mcpp-community
65+
exclude_repos:
66+
- archived-project
67+
```
68+
69+
3. **Only check critical tasks (P0):**
70+
```yaml
71+
priorities_to_check: ["P0"]
72+
timeout_hours:
73+
P0: 12 # Alert after 12 hours
74+
```
75+
76+
## Common Configuration
77+
78+
All components use configuration files in the `config/` directory and require a GitHub token via the `GH_TOKEN` environment variable.

src/components/scan_join_issues.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import json
2+
import os
3+
import sys
4+
from pathlib import Path
5+
6+
# Add parent directory to path to import from libs
7+
sys.path.insert(0, str(Path(__file__).parent.parent))
8+
9+
from libs.github_client import (
10+
comment,
11+
close_issue,
12+
has_label,
13+
get_target_from_labels,
14+
is_org_member,
15+
add_user_to_team,
16+
is_user_in_team,
17+
list_open_join_issues,
18+
)
19+
from libs.utils import load_simple_yaml
20+
21+
def scan():
22+
# Load configuration first
23+
cfg = load_simple_yaml("config/join-config.yml")
24+
25+
# Get token from environment variable
26+
token = os.environ.get("GH_TOKEN", "").strip()
27+
28+
# Get org and repo from config file
29+
org = cfg.get("org", "").strip()
30+
repo = cfg.get("repo", "").strip()
31+
32+
if not token:
33+
raise ValueError("Environment variable GH_TOKEN is not set or empty")
34+
if not org:
35+
raise ValueError("Organization (org) is not set in config file config/join-config.yml")
36+
if not repo:
37+
raise ValueError("Repository (repo) is not set in config file config/join-config.yml")
38+
39+
teams_cfg = cfg.get("teams") or {}
40+
41+
issues = list_open_join_issues(token, repo)
42+
43+
for it in issues:
44+
issue_number = it["number"]
45+
author = it["user"]["login"]
46+
47+
target = get_target_from_labels(it)
48+
if not target or target not in teams_cfg:
49+
continue
50+
51+
team_cfg = teams_cfg[target]
52+
team_slug = team_cfg.get("team_slug", "") or ""
53+
54+
# If not org member yet, remind and keep open
55+
if not is_org_member(token, org, author):
56+
if has_label(it, "invited"):
57+
comment(token, repo, issue_number,
58+
f"@{author} 温馨提示:你还未加入 **@{org}**。请在这里接受邀请:\n\nhttps://github.com/orgs/{org}/invitation")
59+
continue
60+
61+
# If needs team, ensure team membership
62+
if team_slug:
63+
if not is_user_in_team(token, org, team_slug, author):
64+
code, payload = add_user_to_team(token, org, team_slug, author)
65+
if code not in (200, 201):
66+
# Can't add team for some reason; leave a note and continue
67+
comment(token, repo, issue_number,
68+
f"@{author} 已检测到你已加入 **@{org}**,但加入 **@{org}/{team_slug}** 仍失败,将稍后重试。\n\n"
69+
f"HTTP {code}\n\n```json\n{json.dumps(payload, ensure_ascii=False, indent=2)}\n```")
70+
continue
71+
72+
# Now complete: comment + close
73+
if team_slug:
74+
comment(token, repo, issue_number, f"@{author} ✅ 已确认你已加入 **@{org}** 并加入 **@{org}/{team_slug}**,本 Issue 将关闭。")
75+
else:
76+
comment(token, repo, issue_number, f"@{author} ✅ 已确认你已加入 **@{org}**,本 Issue 将关闭。")
77+
78+
close_issue(token, repo, issue_number)
79+
80+
if __name__ == "__main__":
81+
scan()

0 commit comments

Comments
 (0)