一个用于家庭生日提醒的微信机器人项目。
- Fork 本仓库
- 配置 GitHub Secrets:ILINK_TOKEN、TO_USER_ID、CONTEXT_TOKEN
- 可选配置 BIRTHDAYS_JSON
- 手动运行 Actions 测试
默认采用开源友好的数据策略:
- 仓库只提交示例数据(
birthdays.example.json) - 真实数据不入库(
birthdays.json已被.gitignore忽略) - GitHub Actions 可通过
BIRTHDAYS_JSONSecret 注入真实数据
同时提供两种 Bot 凭证初始化方式:
- GitHub Pages 可视化配置页(实验功能,走 Cloudflare 中转代理)
- 本地 CLI 脚本(推荐,更安全)
功能概览:
- 按北京时间计算今天是否有人生日(支持阳历和农历)
- 有人生日时发送生日提醒文案
- 没人生日时发送一条“机器人运行正常”的心跳消息
- 每次发送前先调用 getupdates 尝试拿最新 context_token
- 使用 Actions Cache 持久化 runtime state(context_token 与 get_updates_buf)
- 可选:配置 AI Key 后自动生成祝福文案(支持 DeepSeek / 阿里通义兼容接口)
- AI 调用基于 OpenAI SDK,通过 baseURL + model 适配不同厂商
src 目录按职责拆分:
- src/index.ts:主流程编排
- src/birthday.ts:生日计算、文案构建
- src/ilink.ts:iLink API 调用、上下文更新、runtime cache 读写
- src/env.ts:环境变量读取
- src/types.ts:类型定义
- birthdays.example.json:开源示例数据
- birthdays.schema.json:字段规范(JSON Schema)
- docs/setup.html:GitHub Pages 可视化初始化页面
- scripts/get-token.ts:本地 CLI 获取凭证脚本
⚠ 安全提示:网页方式为实验功能,当前通过 Cloudflare Worker 中转代理访问 iLink 接口。
如果你对凭证安全要求更高,建议优先使用下方「方式二:本地 CLI(推荐)」在本机终端完成初始化。
- 在仓库 Settings -> Pages 中启用 Pages
- 选择 Deploy from branch,分支选
master,目录选docs - 打开页面 Birthday Bot Setup
- 按页面提示:扫码登录 -> 微信给 Bot 发“绑定我” -> 复制结果
页面会生成:
- ILINK_TOKEN
- TO_USER_ID
- CONTEXT_TOKEN
- BOT_BASE_URL
安全提醒:
- 不要截图或外传这些凭证
- 不要提交到仓库
- 页面只在浏览器内存展示,刷新后清空
本地 CLI 不依赖浏览器与中转代理,安全性更高,建议优先使用:
bun run get-token
脚本会输出同样的凭证结果,按提示填入 Secrets。
使用 Bun:
bun install
至少需要以下变量:
- ILINK_TOKEN:iLink Bot Token
- TO_USER_ID:要发送到的用户 ID
- CONTEXT_TOKEN:初始上下文 Token(首次需要先给 Bot 发一条消息拿到)
PowerShell 示例:
$env:ILINK_TOKEN="xxx"
$env:TO_USER_ID="xxx"
$env:CONTEXT_TOKEN="xxx"
首次使用先复制示例文件:
cp birthdays.example.json birthdays.json
然后编辑 birthdays.json 填入你自己的真实数据。
bun run start
定时任务文件:
当前配置:
- 每天北京时间 08:30 运行(cron 使用 UTC 表达)
- Job 环境变量设置为 Asia/Shanghai
- 使用 Actions Cache 保存 .runtime/state.json
请在仓库中配置:
- ILINK_TOKEN
- TO_USER_ID
- CONTEXT_TOKEN
可选(推荐用于 Actions 注入真实生日数据):
- BIRTHDAYS_JSON
说明:
- 若配置了
BIRTHDAYS_JSON,程序优先使用该 Secret 的 JSON 内容 - 若未配置
BIRTHDAYS_JSON,程序会读取仓库根目录的birthdays.json
可选(用于配置页/脚本输出的 base 地址,通常可不填):
- BOT_BASE_URL
可选(开启自动 AI 生成时使用):
- AI_API_KEY
- AI_BASE_URL(可选,默认 https://api.deepseek.com/v1)
- AI_MODEL(可选,默认 deepseek-chat)
Context Token 是微信通过 iLink 协议返回的会话凭证,有效期约 24 小时。
重要:首次接入时,无法主动获取 Context Token。只有当用户向 Bot 发送消息后,该消息中才会包含 Context Token。获得后应立即缓存,后续可使用该 Token 在 24 小时有效期内主动向用户发送消息。
补充说明:
bot_token、to_user_id可长期持久化context_token约 24 小时有效,过期后通常需要用户再次发送消息刷新get_updates_buf是消息同步游标,不会延长context_token有效期- 若超过 24 小时无互动,主动发送可能失败(常见返回
ret=-2)
请不要公开 ILINK_TOKEN、TO_USER_ID、CONTEXT_TOKEN、BIRTHDAYS_JSON。
如果误提交,请立即重新扫码生成凭证并清理仓库历史。
示例:
- DeepSeek:
- AI_API_KEY=你的 deepseek key
- AI_BASE_URL=https://api.deepseek.com/v1(可选)
- AI_MODEL=deepseek-chat(可选)
- 阿里通义:
- AI_API_KEY=你的 dashscope key
- AI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
- AI_MODEL=qwen-plus(可选)
流程如下:
- 启动后先调用 getupdates(5 秒超时)
- 若拉到新消息,提取最新 context_token
- 若没拉到,则继续使用 runtime cache 或 Secrets 里的 CONTEXT_TOKEN
- 每次运行都会把最新 context_token 与 get_updates_buf 写入 .runtime/state.json
- workflow 使用 Actions Cache 还原/保存 .runtime,实现跨次运行持久化
这样下次运行会优先用上次缓存值,不需要额外 GH_PAT。
- 仓库只提交
birthdays.example.json与birthdays.schema.json - 本地使用
birthdays.json保存真实数据,不要提交 - GitHub Actions 使用
BIRTHDAYS_JSONSecret 注入真实数据 - 运行缓存继续使用 Actions Cache(
.runtime)
每个成员字段顺序建议固定为:
- id
- parentId
- spouseId
- familyName
- generation
- name
- nickName(可选)
- relation
- birthYear
- gender
- birthdayType
- birthMonth
- birthDay
说明:
字段详解:
- id:成员唯一标识,建议全局唯一且稳定(例如
m001、child_zhangsan)。用于程序内部关联,不要重复。 - parentId:父级成员的 id,用于表达家族层级关系。没有上级可留空字符串。
- spouseId:配偶成员的 id,用于建立夫妻关联。没有配偶可留空字符串。
- familyName:姓氏或家族名(如“张”“李”),用于展示和检索。
- generation:辈分/代际标记(如“爷爷辈”“父母辈”“子女辈”或数字代际),建议全库口径一致。
- name:姓名(常用名),用于生日提醒主展示字段。
- nickName(可选):昵称或小名(如“外婆”“小宝”),用于文案更口语化展示。
- relation:与维护者关系(如“父亲”“母亲”“儿子”“女儿”“朋友”),用于提醒文案称谓。
- birthYear:出生年份(四位数字,如
1992)。若不确定可按实际情况填近似值或约定占位值。 - gender:性别标记,建议使用固定枚举(如
male/female/unknown)以便后续文案逻辑稳定。 - birthdayType:生日历法类型,支持
lunar(农历)和solar(阳历)。 - birthMonth:出生月份(1-12 的整数,不补零),与
birthdayType对应的历法保持一致。 - birthDay:出生日期(1-31 的整数,不补零),与
birthdayType对应的历法保持一致。
匹配规则:
- 当
birthdayType=solar时,系统按公历birthMonth + birthDay判断是否当天生日。 - 当
birthdayType=lunar时,系统按农历birthMonth + birthDay判断是否当天生日。
- 没人生日为什么也收到消息?
这是设计行为,用于确认定时任务和发送链路正常。
- 报错缺少 CONTEXT_TOKEN 怎么办?
先给 Bot 发一条消息,再把拿到的 CONTEXT_TOKEN 配置到 Secret 或本地环境变量。
- 为什么要保存 get_updates_buf?
它是增量拉取游标,不保存会反复读取旧消息。
- 配了 AI_KEY 但还是发了“复制给 AI”的 prompt?
代表 AI 接口调用失败或返回为空,系统会自动回退到手动模式,避免当天消息中断。
sendmessage的 HTTP 200 不等于消息已送达
- 需同时检查返回里的
ret ret=0才是成功ret=-2常见于context_token失效
context_token不是永久有效
- 必须每次运行先
getupdates刷新 - 刷新失败时回退到缓存 token
get_updates_buf必须持久化
- 它是增量游标(cursor)
- 不持久化会重复读取历史消息,导致上下文混乱
- iLink 更像 IM 增量同步协议,不是简单 webhook
- 建议按“轻量 IM 客户端”思路处理
getupdates/cursor/session
- GitHub Actions 的时间与触发特性
cron使用 UTC,需要换算北京时间schedule可能有分钟级延迟,不保证绝对准点
- 开源场景推荐
- 真实生日数据放本地文件或
BIRTHDAYS_JSONSecret - 运行时会话状态放 Actions Cache(
.runtime/state.json)
