tg12/phantomstars
GitHub: tg12/phantomstars
phantomstars 是一个自动化工具,用于检测和追踪 GitHub 上的虚假互动,以维护开源平台的信任信号。
Stars: 30 | Forks: 2
phantomstars
自动检测与追踪 GitHub 上的虚假互动
一个 JS Labs 项目 —
隶属于 AI Slop Intelligence 计划的一部分。
每日运行。为每个可疑账户评分。检测协调的机器人活动。
直接在受影响的仓库上提交 Issue,以便维护者采取行动。
BTC 3QjWqhQbHdHgWeYHTpmorP8Pe1wgDjJy54
ETH 0x5851e6145F4773d1585b8686095FB16E368a4dA1
ZEC t1KSR5YkNPbjqRSCoLKo5AddFWdm9Kzxh1B
## 为何存在
GitHub Stars 是一种信任信号。开发者通过它来决定评估什么、依赖什么、推荐什么。这个信号正在被系统性地破坏。
在 2024-2026 年的 AI 热潮中,一个机器人农场产业应运而生,为低质量、通常带有恶意的仓库制造可信度。一个在 48 小时内获得 800 Stars 的项目,对于正在扫描搜索结果的开发者来说,看起来是合法的。这就是目的所在。虚假互动的目标不是 Stars 本身;而是这些 Stars 产生的社会认同,以及这种社会认同所影响的下游决策。
这种模式是可以识别的。账户在同一周创建,没有简介,没有关注者,没有原创仓库,在 2 小时内给相同的 15 个仓库点 Star。这不是一个活动,而是每天同时运行的数十个活动,涉及数千个账户。数据显示,有些仓库的 185 个互动者中有 185 个是机器人。100% 的虚假比例。整个热门榜排名完全建立在虚无之上。
**phantomstars** 之所以被构建,是因为这个问题是可控的。就目前而言,GitHub 公开 API 中的信噪比仍然足够高,协调的活动会留下清晰的指纹。该项目读取这些指纹,发布原始数据,并直接通知受影响的仓库维护者。
这是 [JS Labs](https://labs.jamessawyer.co.uk/) 开展的更广泛的 [AI Slop Intelligence](https://labs.jamessawyer.co.uk/ai-slop-intelligence-dashboards/) 工作的一部分,持续研究低质量 AI 生成内容涌入开发者生态系统的机制和可衡量影响。虚假互动不是边缘问题。它是让低质内容触达真实用户的分发机制。
## 功能
**phantomstars** 运行一个每日的 GitHub Actions 作业,它:
1. 抓取 [GitHub Trending](https://github.com/trending) 页面,获取今日获得 Stars 的仓库
2. 查询 GitHub Search API,获取过去 **7 天** 创建且 Star 活动突然增加的仓库(更宽的时间窗口能捕获仅扫描 24 小时可能错过的多日活动)
3. 通过 Events API 拉取近期的互动事件(每个仓库过去 24 小时的 Star、Fork)
4. 通过 GraphQL 获取每个互动账户的完整资料:**账户创建日期**、关注者/关注数、简介、仓库历史
5. 根据一个综合启发式模型对每个账户进行评分:账户年龄、资料完整度、仓库模式、活动历史
6. 使用时间戳聚类和并查集算法检测**协调活动**:在 3 小时窗口内互动的可疑账户集群
7. 将所有可疑账户追加到一个仅追加的 JSONL 账本中,并提交回此仓库
8. 发布每个仓库的情报源,显示哪些仓库正被针对以及虚假比例
9. **直接在目标仓库上提交 GitHub Issue**,以便维护者在自己的 Issue 跟踪器中看到活动数据
10. 将格式化的扫描报告写入 GitHub Actions 作业摘要
无需服务器。无需数据库。无基础设施费用。
## 常见问题
### 它会通知目标仓库吗?
**会。** 当一个仓库的虚假比例超过 40% 或检测到协调活动时,phantomstars 会直接在该仓库上创建一个 Issue。该 Issue 包含完整的可疑账户表、活动成员、综合评分和账户创建日期:维护者调查并向 GitHub 报告所需的一切信息。
如果目标仓库禁用了 Issue,通知将被静默跳过并记录在扫描日志中。
### 我可以请求检查某个特定仓库吗?
可以。
- 对于普通的一次性检查,请以 `owner/repo` 格式提交一个仓库并运行定向扫描。
- 对于终身审计请求,请使用一次性终身模式。它与每日扫描是分开的。
为何分开:
- 普通扫描模型专为近期的公开互动设计,运营成本低。
- 终身审计可能涉及大型仓库的数万个 Stars 和数千个 Fork。
- 对于一次性调查这是可行的,但对于默认的每日路径来说,它太昂贵且太慢。
- 因此,终身请求仅在明确的一次性模式下运行,并设有保障措施。
### 我可以报告误判吗?
可以。如果你的账户出现在 `data/suspects.jsonl` 中,并且你认为分类不正确,请使用提供的模板[提交一个误判 Issue](../../issues/new?template=false_positive.yml)。在添加到允许列表之前,报告会被人工审核。允许列表存储在 `data/allowlist.txt` 中;列出的账户将从所有未来扫描和可疑账户账本中排除。
### 活动 ID 是什么?
活动 ID(例如 `c-a3f9b2e1`)是一个**确定性的 8 字符十六进制指纹**,由该活动中排序后的成员登录名集合的 SHA-256 哈希值派生而来。同一组账户在独立的扫描运行中将产生相同的活动 ID,从而支持长期跟踪。它不是仓库名、用户名或任何外部标识符。
**稳定性:** 只要活动的成员集合保持不变,ID 就是稳定的。如果在扫描之间机器人被添加或暂停,ID 就会改变,因为成员关系发生了变化。这是预期的行为,反映了机器人农场组成在现实世界中的漂移。
### 它会检查账户创建日期吗?
会。每个账户的创建日期通过 GitHub GraphQL API(`createdAt` 字段)获取,并存储在每个可疑记录的 `account_created_at` 字段中。它也是账户年龄评分的主要输入,这是针对虚假账户最强的单一信号。在互动后 2 天内创建的账户仅凭年龄就会得到 1.0 分。
### 它的置信度如何?
单个评分具有显著的误判率。一个资料稀疏的新开发者单独可能得到 0.75+ 分。该工具通过要求活动级别的证据来应对这一点,然后才提交 Issue;单个可疑账户是不够的。一个由 40 多个账户组成的协调集群,全部在同一周创建,全部评分 0.75+,全部在 90 分钟内互动,那就不同了。这才是置信度变得可操作的地方。
数据始终是概率性的。Issue 正文明确说明了这一点。目标是给维护者信号和原始证据,以便他们做出自己的判断。
## 实时仪表盘
| 日期 | 已扫描 | 可能虚假 | 可疑 | 活动数 | 新增虚假 (24小时) |
|------|---------|-------------|------------|-----------|-----------------|
| 2026-05-19 | 2012 | 499 | 1513 | 31 | 323 |
| 2026-05-18 | 8838 | 670 | 7950 | 128 | 340 |
| 2026-05-17 | 8015 | 831 | 5709 | 82 | 831 |
## 今日最受针对的仓库
| 仓库 | 互动者 | 可能虚假 | 已知虚假 % | 虚假比例 % | 活动数 |
|------|----------|-------------|--------------|------------|-----------|
| DuskMosquito/Lossless-Scaling-Desktop-2026 | 299 | 155 | 0.0% | 51.8% | 1 |
| Dangerous-hole/Pumpfun_AI_Trading_Bot | 143 | 111 | 65.0% | 77.6% | 1 |
| pro-tech-killers/coinbase-trading-bot | 143 | 111 | 65.0% | 77.6% | 1 |
| haiddrrs/Steam-Tools | 298 | 53 | 20.8% | 17.8% | 1 |
| AbhishekK130804/Claude-Mythos-AI-Anthropic-App | 299 | 52 | 21.4% | 17.4% | 1 |
| dex-original/okx-agent-trade-kit | 59 | 51 | 54.2% | 86.4% | 1 |
| pro-tech-killers/binance-trading-bot | 60 | 51 | 53.3% | 85.0% | 1 |
| ZoyaMalhotra/DualSenseX-DSX-Steam-Edition | 298 | 51 | 20.8% | 17.1% | 1 |
| xw7872081123/wallpaper-engine-steam | 298 | 50 | 19.5% | 16.8% | 1 |
| thongthaibm/Lossless-Scaling-LSFG | 297 | 50 | 19.5% | 16.8% | 1 |
| labelprosecutorwatt/FL-Studio-2026-Full-Cracked-Edition | 297 | 41 | 19.5% | 13.8% | 1 |
| cat9999aaa/thinshell | 169 | 38 | 0.0% | 22.5% | 1 |
| heyFive-dev/Polymarket-Arbitrage-Trading-Bot-v2 | 34 | 29 | 55.9% | 85.3% | 1 |
| Flizorules05/ROM-MGBA-Pokemon-Emulator-PC | 152 | 23 | 13.8% | 15.1% | 1 |
| BasZ4ll/Stable-Diffusion-WebUI | 148 | 19 | 14.2% | 12.8% | 1 |
| pedrodg28/yuzu-emu | 151 | 19 | 15.2% | 12.6% | 1 |
| zigabratun/Umbrella-HWID-Tool | 121 | 18 | 6.6% | 14.9% | 1 |
| arnabchoudhury404/hydra-launcher | 143 | 18 | 14.0% | 12.6% | 1 |
| python-telegramBot/ai-auto-trading | 21 | 17 | 38.1% | 81.0% | 1 |
| Sunislazi/rbxfpsunlocker-boost-More-240FPS | 146 | 17 | 13.7% | 11.6% | 1 |
| DEV-OCR/polymarket-arbitrage-trading-bot | 22 | 16 | 40.9% | 72.7% | 1 |
| POLYMARKET-TRADER-LAB/Polymarket-trading-bot | 24 | 16 | 37.5% | 66.7% | 1 |
| cdanielc293/Jenny-Mod-All-Versions | 120 | 16 | 7.5% | 13.3% | 1 |
| ZhuLinsen/daily_stock_analysis | 265 | 16 | 0.0% | 6.0% | 1 |
| thinkpixelIab/polymarket-ai-trading | 19 | 15 | 36.8% | 78.9% | 1 |
## 评分模型
每个账户根据四个信号获得一个综合可疑评分(0.0 = 正常,1.0 = 可能虚假):
| 信号 | 权重 | 测量方式 |
|--------|--------|-------------|
| 账户年龄 | 35% | `< 2 天` → 1.00 · `< 7 天` → 0.90 · `< 30 天` → 0.55 · `< 90 天` → 0.20 · 更老 → 0.00 |
| 资料完整度 | 30% | 加分项:无简介 (+0.25),无所在地 (+0.15),无公司 (+0.10),零关注者 (+0.30),零关注数 (+0.10),机器人模式用户名 (+0.20) |
| 仓库模式 | 25% | 零仓库 → 0.90 · 所有仓库均为 Fork → 0.80 · Fork 比例 >85% → 0.55 |
| 活动历史 | 10% | 账户超过 14 天但零仓库 + 零社交图谱 → 0.80(幽灵账户)。仅零仓库 → 0.60。全部 Fork + 无社交图谱 → 0.50 |
**分类阈值:**
| 评分 | 分类 |
|-------|---------------|
| ≥ 0.75 | `可能虚假` |
| ≥ 0.45 | `可疑` |
| < 0.45 | `正常`(不存储) |
### 活动检测
一个**活动**是指一组 ≥ 4 个可疑账户,它们都在 3 小时窗口内与同一个仓库互动。该算法使用并查集构建连通分量;在窗口内共同互动的账户被合并,任何超过最小规模的连通分量都会被标记为协调活动。
活动 ID 是排序后的成员集合的稳定 SHA-256 指纹。只要成员关系不变,在连续几天检测到的同一活动将具有相同的 ID。
**为何活动才是真正的信号:** 单个评分具有显著的误判率。一个资料稀疏的新开发者单独可能得到 0.80 分。但四十个账户全部评分 0.75+,全部在同一周创建,全部在 90 分钟内给同一个仓库点 Star,这就不是巧合了。活动信号是数据变得可操作的地方:它是可疑数据点与协调操作证据之间的区别。
## 数据格式
所有发现都被提交到 [`data/suspects.jsonl`](data/suspects.jsonl) 和 [`data/repos.jsonl`](data/repos.jsonl),每行一个 JSON 记录,仅追加。GitHub Actions 作业摘要(每次运行后在 Actions UI 中可见)提供了格式化的每次扫描报告。
**suspects.jsonl** — 每次扫描中每个被标记账户一条记录:
```
{
"login": "user98432",
"account_age_score": 0.9,
"profile_score": 0.8,
"repo_pattern_score": 0.8,
"activity_score": 0.85,
"composite": 0.842,
"classification": "likely_fake",
"campaign_id": "c-a3f9b2e1",
"scan_date": "2026-05-17",
"account_created_at": "2026-05-15",
"target_repos": ["owner/repo-a", "owner/repo-b"]
}
```
**repos.jsonl** — 每次扫描中每个目标仓库一条记录:
```
{
"full_name": "owner/suspicious-repo",
"total_scanned": 87,
"likely_fake": 62,
"suspicious": 18,
"known_likely_fake": 27,
"known_likely_fake_ratio": 0.310,
"repeat_offenders": 11,
"fakeness_ratio": 0.713,
"classification": "likely_fake",
"campaign_count": 3,
"scan_date": "2026-05-17"
}
```
**查询示例:**
```
# 所有今天出现的 likely_fake 账户
jq 'select(.scan_date == "2026-05-17" and .classification == "likely_fake") | .login' data/suspects.jsonl
# 在过去3天内创建且被标记的账户
jq 'select(.account_created_at >= "2026-05-14") | [.login, .account_created_at, .classification] | @tsv' -r data/suspects.jsonl
# 哪些 repos 今天被针对,按 fakeness 比率排序
jq 'select(.scan_date == "2026-05-17") | [.full_name, .fakeness_ratio, .likely_fake] | @tsv' -r data/repos.jsonl | sort -t$'\''\t'\'' -k2 -rn
# 来自之前出现的 likely_fake 账户中,具有最高 recycled-bot 占比的 repos
jq 'select(.scan_date == "2026-05-17") | [.full_name, .known_likely_fake_ratio, .repeat_offenders] | @tsv' -r data/repos.jsonl | sort -t$'\''\t'\'' -k2 -rn
# 特定活动的所有成员
jq 'select(.campaign_id == "c-a3f9b2e1") | [.login, .account_created_at, .composite] | @tsv' -r data/suspects.jsonl
# 特定账户针对的 repos
jq 'select(.login == "user98432") | .target_repos[]' data/suspects.jsonl
# 高置信度 repos:fakeness 比率超过 60%
jq 'select(.fakeness_ratio >= 0.6) | [.full_name, .fakeness_ratio, .campaign_count] | @tsv' -r data/repos.jsonl | sort -t$'\t' -k2 -rn
```
## 设置
### 1. Fork 此仓库
你的 Fork 拥有数据。每次每日运行后,结果会被提交回你 Fork 仓库的 `data/suspects.jsonl` 和 `data/repos.jsonl` 中。
### 2. 添加 GitHub PAT 密钥
创建一个 **classic** Personal Access Token,权限范围包括:
- `public_repo`:读取公开仓库事件和 Stargazer,在公开仓库上创建 Issue
- `read:user`:通过 GraphQL 获取用户资料
**Settings → Secrets and variables → Actions → New repository secret** → 将其命名为 `GH_TOKEN`。
### 3. 启用 Actions
在你的 Fork 上 **Actions → Enable GitHub Actions**。工作流在 **每天英国时间 07:00** 使用 `Europe/London` 时钟运行:
- 英国夏令时期间 **UTC 06:00**
- 格林威治标准时间期间 **UTC 07:00**
无需额外的调度环境变量。GitHub Actions cron 仅使用 UTC,因此工作流会在两个 UTC 小时触发,但仅在伦敦当地时间是 07:00 时才继续。可通过 **Actions → Daily Phantom Stars Scan → Run workflow** 手动触发。
每次运行后,格式化的扫描报告可在 **Actions → [运行记录] → Summary** 中查看。
### 4. 本地运行
```
git clone https://github.com/YOUR_USERNAME/phantomstars.git
cd phantomstars
python -m venv venv && source venv/bin/activate
pip install -e .
GH_TOKEN=ghp_your_token python -m phantomstars.main
```
设置完成后,进行临时本地运行:
```
GH_TOKEN=ghp_your_token python -m phantomstars.main
```
扫描单个仓库而不是正常的发现集:
```
PHANTOMSTARS_TARGET_REPO=owner/repo GH_TOKEN=ghp_your_token python -m phantomstars.main
```
### 一次性请求
用户可以通过两种方式请求一次性仓库检查:
1. 打开 `Repo Check Request` Issue 模板,提供目标仓库和请求深度。
2. 使用 **Actions -> Daily Phantom Stars Scan -> Run workflow** 并可选设置:
- `target_repo`:`owner/repo`
- `request_depth`:`recent` 或 `lifetime-request`
当前行为:
- `recent`:立即运行针对性的近期互动扫描。
- `lifetime-request`:运行针对性的终身扫描,仅扫描该仓库的历史 Star 和 Fork。
- 每日计划扫描保持不变,继续使用近期互动方法。
终身模式的保障措施:
- 仅适用于明确的一次性针对性请求
- 在扫描开始前受配置的仓库大小限制约束
- 比每日扫描更慢且 API 调用量更大
## 项目结构
```
phantomstars/
├── .github/
│ ├── workflows/daily-scan.yml # Runs daily at 07:00 Europe/London
│ └── ISSUE_TEMPLATE/false_positive.yml
├── src/phantomstars/
│ ├── config.py # All constants, no argparse, no env parsing
│ ├── models.py # Frozen dataclasses
│ ├── github_client.py # REST + GraphQL, tenacity retries, rate-limit aware
│ ├── heuristics.py # Per-user composite scoring engine
│ ├── campaigns.py # Timestamp clustering + union-find
│ ├── storage.py # JSONL append + query helpers
│ ├── reporter.py # README dashboard injector
│ ├── notifier.py # GitHub Issues notifier (files on targeted repos)
│ └── main.py # Orchestration entry point
├── tests/
│ ├── conftest.py
│ ├── test_heuristics.py
│ └── test_campaigns.py
├── data/
│ ├── suspects.jsonl # Append-only account findings ledger
│ ├── repos.jsonl # Append-only per-repo intelligence
│ └── allowlist.txt # Accounts excluded from future scans
└── pyproject.toml
```
## 局限性与已知失败模式
- **Events API 上限:** 每个仓库最多 300 个近期事件。一天内获得数千 Star 的仓库覆盖不全。
- **搜索索引延迟:** GitHub 的搜索索引是最终一致的。在扫描边界前几秒创建的仓库可能会被遗漏。
- **启发式漂移:** 机器人运营者会适应。评分权重可能需要定期调整;请调整 `config.py` 中的常量。
- **单个误判:** 一个资料稀疏的新开发者单独可能得到 0.75+ 分。活动成员身份才是高置信度的信号。
- **活动 ID 漂移:** 如果机器人农场的成员在扫描之间发生变化(机器人被暂停、新机器人添加),活动 ID 就会改变。这反映了活动的实际演变,而不是 bug。
- **速率限制:** 使用已认证的 PAT 每小时 5,000 次 API 请求。对于标准的 Trending 页面大小来说完全在限制之内。
- **Issue 被禁用:** 一些目标仓库禁用了 Issue。这些仓库的通知会被静默跳过。
## 误判处理流程
如果你的账户出现在 `data/suspects.jsonl` 中,并且你认为分类不正确:
1. 找到你的条目:`jq 'select(.login == "YOUR_LOGIN")' data/suspects.jsonl`
2. 使用你的登录名、分类、扫描日期和解释[提交一个误判 Issue](../../issues/new?template=false_positive.yml)
3. 报告会被人工审核。经核实的误判会被添加到 `data/allowlist.txt`,并从所有未来扫描中排除。
注意:创建 Issue 不会修改或删除任何现有数据。可疑账户账本是仅追加的。允许列表仅影响未来的扫描。
## 贡献
```
pip install -e ".[dev]"
python -m black .
python -m ruff check .
python -m mypy src
python -m pytest
```
所有四项检查都必须通过才能合并 PR。
## 免责声明
此工具使用官方 GitHub API 对公开 GitHub 数据进行只读分析。在目标仓库上提交的 Issue 包含概率性发现,并明确标注为自动化结果。发现是指标,而非指控。误判存在且属于预期之内。
以 AI 作为编码伙伴构建,以应对部分由 AI 造成的生态系统问题。
## 许可证
Apache 2.0。参见 [LICENSE](LICENSE)
## 作者
由 **tg12** 构建 · [GitHub](https://github.com/tg12)
一个 **[JS Labs](https://labs.jamessawyer.co.uk/)** 项目 · [AI Slop Intelligence Dashboards](https://labs.jamessawyer.co.uk/ai-slop-intelligence-dashboards/)
标签:AI滥用防范, Bot农场检测, GitHub Actions, GitHub安全, Python开发, SEO检索词, 仓库清理, 代码托管平台安全, 信任信号分析, 功能关键词, 协调攻击检测, 反欺诈系统, 威胁情报, 开发者工具, 开源仓库维护, 开源框架, 技术栈, 持续集成, 日常运行, 时序数据库, 社交媒体欺诈, 网络安全, 自动化监控, 自动报告系统, 自动笔记, 虚假互动检测, 账户信誉评分, 逆向工具, 隐私保护, 零基础设施