Skip to content

About

一个脚本,把人批量邀请进 GitHub 组织,顺手把人加进 Team。

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

GitHub 组织 / Team 批量邀请

一个脚本,把人批量邀请进 GitHub 组织,顺手把人加进 Team。 默认不会发任何东西,加 --execute 才真正发送。



使用前提

1. 你得是这个组织的 owner(或至少是该 Team 的 maintainer)。不是 owner 会回 404 / 403。

2. 关掉系统代理(Clash / v2ray 之类的「系统代理」开关)。

实测结论:走系统代理成功率 42%,直连 100%。 代理开着时典型症状是满屏重试,或者 SSL: UNEXPECTED_EOF_WHILE_READING。

3. 建一个 token,把 token 直接写进脚本同目录的 token.txt 第一行,其他什么都不要有。

去 https://github.com/settings/tokens 建token:

token 类型 需要的权限
细粒度(推荐) Organization permissions → Members → Read and write
经典 admin:org

细粒度 token 还要组织那边批准一下:组织 Settings → Personal access tokens → Pending requests。 没批准的话调用会一直失败。 (如果你是owner似乎不需要批准?蛮看一下吧) token.txt 已被 .gitignore 挡住,不会误提交。

环境:Python 3.10+,不需要装任何第三方库。


输入怎么写

脚本只收一个表格文件(.csv 或 .xlsx)。 因为要发出去的一组人必须同时装得下「邮箱」和「GitHub用户名」两项

注:有用户名才能可靠邀请至 Team; 只有邮箱时, 仅可邀请至组织。

示例文件都在 docs/examples/,照着改就行。

表要长什么样

推荐的表结构(列顺序任意,脚本按表头名字认列):

姓名 邮箱 GitHub用户名
张三 zhangsan@example.com zhangsan
李四 lisi@example.com lisi-2026
王五 wangwu@example.com
赵六 zhaoliu

示例文件:收集表示例.csv · 收集表示例.xlsx

  • 姓名:只用来在结果里显示是谁,不参与匹配账号
  • 邮箱 / GitHub用户名:至少要有一个。两个都有最稳
  • 表里可以只填邮箱(示例 只有姓名和邮箱.csv)—— 能发,但只有邮箱时对方必须已把该邮箱验证到自己账号上才点得动邀请, 而且这只能做到邀请到组织,无法实现邀请到team

列名认这些:

用途 认得的表头
姓名 姓名 名字 昵称 name nickname
邮箱 邮箱 电子邮箱 邮件 email mail
GitHub用户名 GitHub用户名 用户名 账号 username login

列顺序完全不用管,只要表头名字在上面这张表里就认得出。比如这样也对 (示例 列顺序不一样.csv):

提交时间 GitHub用户名 邮箱 姓名 备注
2026-03-01 zhangsan zhangsan@example.com 张三 组长

命令怎么写

先看一眼组织现状(只读,不改任何东西):

python github_inviter.py --org <组织名> --status

邀请至组织:

# 1. 先看计划(不发任何东西)
python github_inviter.py --org <组织名> "<收集表.xlsx>"

# 2. 试跑 3 个(这一步会真发邀请)
python github_inviter.py --org <组织名> "<收集表.xlsx>" --execute --limit 3

# 3. 确认没问题,全量
python github_inviter.py --org <组织名> "<收集表.xlsx>" --execute

同时把人加进 Team,加一个 --team:

python github_inviter.py --org <组织名> "<收集表.xlsx>" --team "<Team名>" --execute

表头含义不清晰时的输入规范

表头认不出来时(比如列名写的是「QQ」「微信」),用 --cols 按位置说明, 位置从 1 开始数,不用的列写 -:

--cols "name,email,username"           # 第1列姓名、第2列邮箱、第3列用户名
--cols "username,email"                # 第1列用户名、第2列邮箱
--cols "-,-,-,username,email,name"     # 跳过前三列

Note

--cols 只负责「哪一列是什么」,表头行照旧会被自动跳过。 表头认得出来时, 加了 --cols 也不用再写别的:

# 表头是 提交时间 | GitHub用户名 | 邮箱 | 姓名 | 备注  —— 认得出来, 第1行自动跳过
python github_inviter.py --org <组织名> "<收集表.xlsx>" \
--cols "-,username,email,name"
  

表头识别失败时的补救措施

只有当表头也认不出来时(比如列名是「QQ」「微信」),脚本没法判断第 1 行是不是表头。 这时有两条路,随便哪条都行:

办法一:让它问你(最省事,在终端里直接跑就会问)

直接运行上面「邀请至组织」的第 1 条命令(不带 --execute),它会停下来问你:

  表格前 3 行(共 3 行):
    第 1 行: 日期 | QQ | 微信 | 怎么称呼 | 备注
    第 2 行: 2026-03-01 | zhangsan | zhangsan@example.com | 张三 | 组长
    第 3 行: 2026-03-01 | lisi-2026 | lisi@example.com | 李四 |
  这一行数据长这样(共 5 列):
    第 1 列: 日期
    第 2 列: QQ
    ...
  我没认出哪一列是邮箱、哪一列是用户名。请按位置告诉我, 和 --cols 一个写法:
    第1列姓名、第2列用户名、第3列邮箱  就写  name,username,email
    不想用的列写 -                       就写  -,username,email,name
  列的顺序(直接回车 = 按老约定: 第1列用户名、第2列邮箱): -,username,email,name

  数据行范围(例如 2 或 2-10, 写 0 表示全是数据): 2

注意:这种表不能在脚本或管道里非交互地跑 —— 脚本会明说"没法向你提问"并停下, 而不是静默按错的列发出去。要自动化就用下面的办法二。

办法二:在命令里直接说清楚

# 表是: 日期 | QQ | 微信 | 怎么称呼 | 备注  —— 一个列名都认不出
python github_inviter.py --org <组织名> "<收集表.xlsx>" \
  --cols "-,username,email,name,-" --skip-rows 1

关于 --cols 的几点:

  • 项数不必等于列数,写到有用的最后一列就行。例如第 5 列不用时, --cols "-,username,email,name" 和 --cols "-,username,email,name,-" 效果完全一样
  • 但如果末尾要跳过的列后面还有你要用的列,就得把 - 写齐,例如 --cols "-,username,-,email"(第 1、3 列跳过,第 2 列用户名、第 4 列邮箱)

表里没有表头(第一行就是数据)时,用 --no-header,或者直接让脚本问你:

python github_inviter.py --org <组织名> 名单.csv --no-header --cols "name,email,username"

鄙人认为的最佳方法, 就是先按办法一走, 直接跑一次, 然后看脚本是否报错, 报错就按提示走, 之后再输入就按办法二走

常用选项

选项 说明
--org 必填,组织名(URL 里那个 slug)
--execute 真正发送。不加就是 dry-run
--limit N 本次只处理表里第 1~N 行,首次建议 3~5
--team "<名字>" 同时把人加进这个 Team
--status 只读:看组织成员数、待处理邀请、失败邀请及原因
--cols 表头认不出来时,按位置指定数据列
--skip-rows N 跳过表格开头 N 行(比如认不出的表头行)
--no-header 表里没有表头,第一行就是数据
--sheet <名字> xlsx 指定工作表,默认第一个
--delay S 每条间隔秒数,默认 2.0。别调小,会触发限流
--resolve-emails 先用搜索把邮箱反查成用户名,命中更可靠(慢一些)
--no-resume 忽略历史结果重跑全部(会重复发送)

跑到一半断了怎么办

直接再跑一次同样的命令就行。脚本会自动跳过上次已经成功的人,不会重复打扰。 想强制全部重来加 --no-resume(会重复发邮件,慎用)。


四、跑完会看到什么

先是逐行结果(按姓名列出来谁成功、谁失败):

== 逐行结果 (4 人) ==
姓名  邮箱                  用户名      组织邀请        Team              说明
----  --------------------  ----------  --------------  ----------------  ------------------
张三  zhangsan@example.com  zhangsan    组织邀请已发出  已加入 team       已按用户名邀请
李四  lisi@example.com      lisi-2026   组织邀请已发出  待对方接受组织邀请  已按用户名邀请
王五  wangwu@example.com    —           组织邀请已发出  缺用户名          已按邮箱邀请
赵六  —                     zhaoliu     已是组织成员    待对方接受组织邀请  在组织成员列表里

后面还有汇总、失败明细(带处理措施)、以及「待重跑名单」。 汇总那一行(组织邀请: …)里每个数字都有明确含义,末尾的「合计」就是上面的人数:

  • 第一个数字跟着模式走:真发时是「已发出邀请」,dry-run 是「计划邀请」, 加了 --no-preflight 则是「状态未知(未预检)」—— 没查过就老老实实说没查过;
  • 具名的那几个桶相加不到「合计」时,差额在下面的明细行里:缺联系方式、表里重复、 被 --limit 截断 —— 都是跟对方无关的行。

顺带一提:--no-preflight 的定义是「只看表格解析结果」,所以它优先于 --execute —— 两个一起给时一封都不会发,脚本会明确告诉你(不会不声不响)。

同时在 output/ 下留档:

文件 内容
results-<时间戳>.csv 逐行状态、失败原因、处理措施,Excel 直接打开
results-<时间戳>.jsonl 机器可读,续跑就是靠它记住谁已成功
plan-<时间戳>.csv dry-run 时生成的计划

五、遇到问题怎么解决

发不出去 / 连不上

现象 原因 怎么办
满屏重试、SSL: UNEXPECTED_EOF_WHILE_READING 系统代理 关掉系统代理
404 组织名写错,或 token 不是该组织 owner 核对组织 slug 和 token
403 权限不足 权限没给够,或细粒度 token 没被组织批准 补 Members 写权限 / 去组织批准
找不到 team: xxx Team 名字写错 报错里会列出全部 Team
预检失败 token 无效或没权限 先用 --status 单独测一下

人的状态不对

现象 原因 怎么办
已是组织成员 人本来就在组织里 不用管,接口也不会给他发邮件
已有待处理邀请 之前发过还没接受 不用重发,等对方接受
同上,但对方说没收到 邀请邮件进了垃圾邮件 让对方搜一下 GitHub 的邮件;7 天内有效
用户名不存在 表里那个用户名在 GitHub 上查不到 找本人核对拼写。先试相邻字母打反:user_name 被打成 u5er_na3e 这种情况
一批人 缺用户名,加不了 team 表里只有邮箱 —— Team 接口不认邮箱 补一列 GitHub 用户名再重跑
一批人 待对方接受组织邀请 人还不是组织成员,现在加不进 Team 等对方接受,再跑一次同样的命令就会自动补进 Team
对方说已经进组织了,还显示待接受 表里用户名可能填错了 让对方把自己的 GitHub 用户名发来核对
跳过(上次已成功) 续跑记忆生效了 不用管。想重发加 --no-resume
跳过(--limit 截断) 被 --limit 拦住了,不是失败 不带 --limit 再跑一次

对方点不动邀请链接

这是邮箱邀请的硬限制:只有该邮箱是对方 GitHub 账号上已验证的邮箱,他才点得动。 没验证时接口照样返回成功,所以事后要用 --status 对一遍:

python github_inviter.py --org <组织名> --status

里面列出的「失败邀请」就是对方没接成的。处理办法二选一:

  1. 让对方把该邮箱加到自己的 GitHub 账号并验证;
  2. 改用 GitHub 用户名邀请(表里补上用户名列)——这条路可靠得多。

看到 Invitation expired. User did not accept this invite for 7 days 表示 组织邀请 7 天过期了,需要重新发一次。

表解析得不对

先跑一次 dry-run,看输出里的「列映射」那一行,确认 邮箱=<...> 用户名=<...> 姓名=<...> 指对了列。指错了就停下来用 --cols 改,不要在列映射不对的时候加 --execute。

现象 怎么办
列映射指错了列 用 --cols "name,email,username" 按位置指定
没法向你提问 表头认不出来,但当前不是在终端里跑(脚本/管道/CI)。改用 --cols + --skip-rows 1(或 --no-header)
用了 --cols,结果里混进了表头那一行 加上 --skip-rows 1
解析出的行数比想象中少 完全空白的行会被忽略;重复的人只算一次
中文乱码 脚本会自动试 UTF-8 / GB18030,一般不会;仍乱码就把表另存为 UTF-8
只想要其中几个人 先用 --limit,或者另存一份只含这几个人的表

六、三条要知道的限制

  1. 按邮箱邀请要求该邮箱是对方 GitHub 账号上已验证的邮箱,否则他点不动。 能拿到用户名, 就优先用用户名邀请(脚本默认就这么做),这条路可靠得多。
  2. 组织邀请 7 天过期,到期前催一下没接受的人。
  3. Team 接口只收用户名,不认邮箱,而且对方得先是组织成员。 所以收集表最好带一列 GitHub 用户名。

设计取舍、接口细节、踩坑记录见 ARCHITECTURE.md(面向维护者)。

About

一个脚本,把人批量邀请进 GitHub 组织,顺手把人加进 Team。

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages