Ruby China API → 筛选精华帖 → 拼成 Tweet → Twitter API 发帖 → Git 文件记录已发内容 → GitHub Actions 每 2 小时执行一次。它不是常驻服务器也不是 webhook Bot。核心架构GitHub Actions每 2 小时触发一次│▼relay.rb│┌───────────┴───────────┐▼ ▼Ruby China API finished.log获取 excellent topics 已经发过的 topic id│ │└───────────┬───────────┘▼过滤重复帖子│▼获取作者 Twitter ID│▼拼接 Tweet│▼Twitter::REST::Client│▼发布到 Twitter│▼更新 finished.log│▼Git commit push 回仓库仓库本身说明它的目标就是把 Ruby China 的优秀帖子relay到 Ruby China Twitter。定时任务完全由 GitHub Actions 承担.github/workflows/tweet.yml 里最关键的是schedule:cron: “0 */2 * * *”也就是每两个小时执行一次。同时只要 main 分支发生 push也会执行。Action 启动一个 Ubuntu runner配置 Ruby 2.7安装依赖然后ruby relay.rb因此它根本不需要自己购买服务器、跑 Sidekiq、Redis 或 cron 服务。依赖也极少Gemfile 实际只有两个主要包gem ‘twitter’, ‘~ 7.0.0’gem ‘faraday’, ‘~ 1.0.0’Faraday 负责 HTTP 请求twitter gem 负责 Twitter API。从 Ruby China 拉精华文章核心代码都在只有 47 行的 relay.rb。它请求Faraday.get(“https://ruby-china.org/api/v3/topics.json”,{ type: “excellent” })也就是说GET /api/v3/topics.json?typeexcellent获取 Ruby China 的精华主题。然后topics JSON.parse(resp.body)[“topics”]拿到 topic 列表。finished.log 就是它的“数据库”项目没有 PostgreSQL也没有 SQLite。启动时finished File.readlines(“finished.log”).map(:chomp).map(:to_i)里面就是这种数据40583404814047140414…41713每一个数字就是一个已经处理过的 Ruby China topic_id。因此过滤新文章只需要filtered topics.reject do |topic|finished.include? topic[“id”]end换句话说API 返回417134175541768finished.log41713结果4175541768简单得近乎没有基础设施。再查询作者资料对于每篇新文章它继续请求https://ruby-china.org/api/v3/users/#{login}.json获取作者资料。然后检查user[“twitter”]如果作者填写了 Twitter作者Twitter否则直接使用 Ruby China 用户名。自动生成 Tweet最终格式大概是文章标题 by 作者https://ruby-china.org/topics/41755对应代码#{topic[‘title’]} by #{user_name}https://ruby-china.org/topics/#{topic[‘id’]}这里没有 AI没有摘要模型也没有复杂模板系统。本质就是字符串拼接。Twitter API 发出去Twitter 客户端通过 GitHub Secrets 注入五个凭证CONSUMER_KEYCONSUMER_SECRETBEARER_TOKENACCESS_TOKENACCESS_TOKEN_SECRETRuby 中初始化client Twitter::REST::Client.new do |config|…end然后真正发 Tweet 的代码只有formatted.each do |tweet|client.update(tweet)end也就是说整个“Twitter Bot”的核心发布动作就是这一句。最有意思的是它怎么保存状态GitHub Actions 的机器每次都是临时的。所以runner A↓finished.log 修改↓runner A 销毁正常情况下状态就丢了。作者采用了一个很巧的办法把 Git 仓库本身当数据库。relay.rb 修改finished.logAction 随后git add --allgit commit -m “Update Tweets”最后通过ad-m/github-push-action把 finished.log push 回 main。所以下一次 GitHub Actioncheckout repo↓拿到最新版 finished.log↓知道哪些文章已经 Tweet这其实是这个项目最值得借鉴的地方。整个程序可以压缩成这样的伪代码posted_ids read(“finished.log”)topics GET(“ruby-china.org/api/v3/topics.json?typeexcellent”)new_topics topics.reject {|topic| posted_ids.include?(topic.id)}new_topics.each do |topic|author GET(“/api/v3/users/#{topic.user.login}.json”)tweet “”#{topic.title} by #{author.twitter}https://ruby-china.org/topics/#{topic.id}“”twitter.post(tweet)posted_ids topic.idendwrite(“finished.log”, posted_ids)然后 GitHub Actions每 2 小时↓checkout↓bundle install↓ruby relay.rb↓git commit finished.log↓git push一个值得注意的实现问题原代码实际上是先把 topic ID 写入 finished.log再调用 client.update 发推。也就是标记“已经完成”↓保存 finished.log↓Twitter API 发帖如果 Twitter API 此时失败finished.log 已经认为发过但实际上 Twitter 没发成功下一轮就不会自动重试。更可靠的实现应该改成Twitter 发帖成功↓记录 topic_id↓保存状态或者维护pendingpostedfailed三个状态。另外这个仓库的 tweet scheduled workflow 目前已经被 GitHub 标记为 Disabled页面显示原因是仓库至少 60 天没有活动因此定时 workflow 自动停止。这不影响理解它原本的设计。所以如果一句话概括这个项目它其实不是传统意义上的 Twitter Bot而是一个用 GitHub Actions 当 cron、用 Ruby China API 当数据源、用 finished.log Git commit 当数据库的无服务器 RSS/内容同步器。如果想研究它并自己做一个类似的进一步拆成一个 2026 年版本的「网站/博客 → X 自动发布」实现方案包括用 X API v2、GitHub Actions、去重、失败重试大概 100 行 Python/Ruby 就能重写出来。