news 2026/9/17 4:51:47

Friend 仓库中的 Twitter/X Omi 插件实战:OAuth2 授权、Railway 部署与对话式推文管理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Friend 仓库中的 Twitter/X Omi 插件实战:OAuth2 授权、Railway 部署与对话式推文管理

Friend 仓库中的 Twitter/X Omi 插件实战:OAuth2 授权、Railway 部署与对话式推文管理

【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend

本文围绕 Friend 开源仓库中的omi-twitter-chat-tools-app插件(文档见 README.md),完整讲解如何把 Twitter/X 账号接入 Omi 对话助手:从创建 Twitter Developer App、通过 PKCE OAuth2 完成用户授权,到在 Railway 上部署 FastAPI 服务、配置 Omi App 的三个关键 URL,再到对话中发推、刷时间线、搜索、点赞、转发等十大 Chat Tools 的调用方式。读完本文,你将掌握一套可直接复制上线的"OAuth 授权 + Chat Tools Manifest + 对话式操作"插件实现方案,并理解其底层源码的运行原理。

插件定位与功能总览

omi-twitter-chat-tools-app是 plugins 目录下 28 个独立部署的omi-*-app插件服务之一。与依赖plugins/main.py单体入口的旧插件不同,它拥有独立的main.py、依赖清单与部署描述文件(Procfile / railway.toml),可独立于单体和其他插件部署与扩容。

该插件通过 Omi 对话完成以下 Twitter/X 账号管理能力:

  • 发推(Post Tweets):向粉丝发布更新,支持指定回复目标推文
  • 查看时间线(View Timeline):读取用户首页 feed
  • 查看我的推文(Get Your Tweets):查看最近发布记录
  • 查看提及(Get Mentions):查看谁提到了你
  • 搜索推文(Search Tweets):按关键词检索任何话题
  • 点赞 / 取消点赞(Like / Unlike):与内容互动
  • 转发(Retweet):把推文分享给粉丝
  • 删除推文(Delete Tweets):移除自己的推文
  • 查看资料(View Profiles):查询任意 Twitter 用户信息

这些能力在源码中一一对应为 10 个 Chat Tool 端点,全部声明在/.well-known/omi-tools.json清单中(见下文"Chat Tools Manifest"小节)。

整体架构:一条对话请求的完整链路

从用户对话到推文落地的数据流如下:

  1. 用户在 Omi 对话中说"发一条推文:xxx",Omi 客户端根据应用的Chat Tools Manifest URL拉取工具清单;
  2. Omi 匹配到post_tweet工具,向插件的/tools/post_tweet端点发起 POST 请求,body 中携带uid(Omi 用户 ID)与参数text
  3. 插件从存储中取出该用户的 Twitter OAuth2 token,必要时自动刷新,然后调用 Twitter API v2;
  4. 插件将 Twitter 返回结果格式化为可读文本,通过ChatToolResponse模型返回给 Omi,由对话助手展示给用户。

整条链路的核心实现在 main.py(FastAPI 应用,约 1300 行)、db.py(token 与状态存储)、models.py(响应模型)三个文件中。

第一步:创建 Twitter Developer App

1. 进入 Twitter Developer Portal

在 Twitter Developer Portal 的 Projects & Apps 页面新建 Project 与 App(或复用现有应用),然后进入User authentication settings配置:

  • App permissions(应用权限):选择Read and write。该权限组合与源码中申请的 OAuth scopes 对应——见 main.py 中TWITTER_SCOPES
TWITTER_SCOPES = [ "tweet.read", "tweet.write", "users.read", "follows.read", "like.read", "like.write", "offline.access" ]

其中tweet.write支撑发推/回复/转发/删除,like.write支撑点赞,follows.read支撑时间线,offline.access用于换取 refresh token(支撑后续的 token 自动刷新)。

  • Type of App(应用类型):选择Web App
  • Callback URI / Redirect URI:填写你的回调地址(生产环境为 Railway 域名,本地开发为http://localhost:8080/auth/twitter/callback,详见下文)
  • Website URL:填写应用官网地址

完成后复制Client IDClient Secret(Client Secret 仅显示一次,务必妥善保存)。

2. 可选:Twitter API v2 访问层级说明

该插件依赖Twitter API v2,不同 Developer 账号层级的能力与配额不同(以 Twitter 官方当前政策为准,仓库 README 中给出的参考数值如下):

  • Free tier(免费层):每月约 1,500 条推文额度,基础读权限
  • Basic tier(基础层):每月约 3,000 条推文额度,更多读权限
  • Pro tier(专业层):更高额度与更多功能

部分功能(如查看时间线)可能需要更高层级的访问权限。如果调用时报权限错误,应先在 Developer Portal 确认账号层级与 App 权限配置。

第二步:部署到 Railway

Railway 是官方 README 推荐的部署方式,仓库内已备齐部署所需的所有文件:

  • railway.toml:声明 nixpacks 构建器、启动命令、健康检查与重启策略
  • Procfile:等价启动命令uvicorn main:app --host 0.0.0.0 --port $PORT
  • requirements.txt:依赖清单

部署步骤

  1. 在 Railway 新建项目,连接包含本插件目录的 GitHub 仓库(或直接从plugins/omi-twitter-chat-tools-app/目录部署);
  2. 为项目添加一个Redis服务(插件用它持久化 token 与 OAuth state);
  3. 设置如下环境变量:
TWITTER_CLIENT_ID=your_client_id TWITTER_CLIENT_SECRET=your_client_secret TWITTER_REDIRECT_URI=https://your-app.up.railway.app/auth/twitter/callback
  1. 触发部署。Railway 会自动完成:从requirements.txt安装依赖、按 railway.toml 的startCommand启动服务,并注入PORTREDIS_URL环境变量。

部署完成后,回到 Twitter Developer App 的User authentication settings,把回调地址更新为:

https://your-app.up.railway.app/auth/twitter/callback

注意回调地址必须与TWITTER_REDIRECT_URI完全一致,否则 OAuth 授权会失败。

部署配置解读

railway.toml 的关键配置:

[build] builder = "nixpacks" [deploy] startCommand = "uvicorn main:app --host 0.0.0.0 --port $PORT" healthcheckPath = "/health" healthcheckTimeout = 100 restartPolicyType = "on_failure" restartPolicyMaxRetries = 3
  • healthcheckPath = "/health"对应 main.py 中的健康检查端点,返回{"status": "healthy", "service": "twitter-omi"}
  • startCommand读取 Railway 注入的$PORT;main.py 的入口同样支持PORT(默认 8080)与HOST(默认0.0.0.0)环境变量,本地直接python main.py也能以相同方式启动。

第三步:Omi App 配置

在 Omi 平台创建或更新该集成应用时,将以下三个 URL 填入对应字段({{uid}}是 Omi 在跳转时自动替换的用户 ID 占位符):

字段
Setup URLhttps://your-app.up.railway.app/?uid={{uid}}
Setup Completed URLhttps://your-app.up.railway.app/setup/twitter?uid={{uid}}
Chat Tools Manifest URLhttps://your-app.up.railway.app/.well-known/omi-tools.json

三个 URL 各自的作用

  • Setup URL:Omi 引导用户进入插件的设置页。带uid访问/时,若该用户尚未连接 Twitter,页面会渲染"Connect Twitter"按钮;已连接则显示账号状态与"Disconnect"入口(main.py)。
  • Setup Completed URL:Omi 用来轮询/判定用户是否完成授权。对应/setup/twitter端点,返回{"is_setup_completed": true|false}——true表示该uid已存在有效 token。
  • Chat Tools Manifest URL:Omi 发现可用对话工具的关键。对应/.well-known/omi-tools.json端点,返回完整的工具声明 JSON。

API 端点总览

Chat Tools 端点(POST)

端点功能必需参数
/tools/post_tweet发布新推文text(≤280 字符),可选reply_to
/tools/get_timeline获取首页时间线可选max_results(默认 10,上限 100)
/tools/get_my_tweets获取自己的推文可选max_results
/tools/get_mentions获取提及我的推文可选max_results
/tools/search_tweets搜索推文query,可选max_results
/tools/like_tweet点赞推文tweet_id
/tools/unlike_tweet取消点赞tweet_id
/tools/retweet转发推文tweet_id
/tools/delete_tweet删除推文tweet_id
/tools/get_user_profile获取用户资料可选username(缺省返回本人资料)

所有 Chat Tool 请求的 body 都需携带uid字段。若用户未授权,端点统一返回错误提示Please connect your Twitter account first in the app settings.max_results在服务端通过min(body.get("max_results", 10), 100)钳制上限。

OAuth 与设置端点(GET)

端点功能
/首页 / 设置 UI(支持?uid=<uid>
/auth/twitter?uid=<uid>发起 OAuth2 授权流程
/auth/twitter/callbackOAuth 回调(交换 code 为 token)
/setup/twitter?uid=<uid>查询设置状态
/disconnect?uid=<uid>解绑账号(删除 token)
/health健康检查
/.well-known/omi-tools.jsonChat Tools 工具清单

核心源码原理剖析

1. PKCE OAuth2 授权流程

插件采用OAuth2 Authorization Code + PKCE(S256)流程,实现在 main.py 的/auth/twitter与回调端点中:

  1. 生成 128 位随机code_verifier(generate_code_verifier),并以其 SHA-256 摘要生成code_challenge(generate_code_challenge);
  2. 构造state = f"{uid}:{secrets.token_urlsafe(32)}",把 state 写入存储(CSRF 防护),把code_verifier写入用户设置;
  3. 重定向到https://twitter.com/i/oauth2/authorize,携带response_type=codescopestatecode_challengecode_challenge_method=S256
  4. 回调端点校验state与存储值一致后,用code_verifierhttps://api.twitter.com/2/oauth2/token换 token(main.py);
  5. 换到 token 后调用/users/me获取 username 与用户 ID,一并存入存储(main.py)。

2. Token 自动刷新机制

Twitter access token 约 2 小时过期(expires_in默认按 7200 秒处理)。插件在每次请求前通过 get_valid_access_token 检查过期时间:若expires_at距当前不足5 分钟,则调用 refresh_access_token 用 refresh token 换取新 token,并回写存储。这一机制让用户授权一次即可长期使用,无需反复重新授权。

3. 统一 API 请求封装

twitter_api_request 是所有 Twitter API 调用的统一入口:注入Authorization: Bearer <token>请求头,按 GET / POST / DELETE 分发请求;204 No Content视为成功(点赞取消、删除等操作),其他非 2xx 状态码会把响应文本原样返回,供上层生成错误信息。

4. 推文展示格式化

format_tweet 把 Twitter 返回的推文对象格式化为对话友好文本:作者(@username (显示名))、正文、时间戳(%b %d, %Y %I:%M %p格式)、互动数据(Likes / Retweets / Replies)与推文 ID。时间线、提及、搜索结果均复用该函数。

5. 双模式存储:Redis 与文件回退

db.py 提供 Redis 优先、JSON 文件回退的双模式存储:

  • Redis 模式:从REDIS_URL(兼容REDIS_PRIVATE_URL/REDIS_PUBLIC_URL)建立连接,token 以twitter:tokens:{uid}为 key 存储,TTL 90 天;OAuth state 以twitter:oauth_state:{uid}存储,TTL 10 分钟;用户设置以twitter:settings:{uid}存储(db.py);
  • 文件模式:本地未配置 Redis 时,自动回退到data/tokens.jsondata/oauth_states.jsondata/user_settings.json三个 JSON 文件(db.py)。这解释了 README 中REDIS_URL标注"可选(未设置时使用文件存储)"的原因——本地开发无需 Redis 即可运行。

6. 响应模型

所有 Chat Tool 端点统一返回 ChatToolResponse 模型:成功时填充result(Markdown 文本),失败时填充error(人类可读的错误说明),Omi 对话层据此呈现结果或提示。

环境变量总览

变量说明必填
TWITTER_CLIENT_IDTwitter OAuth2 Client ID
TWITTER_CLIENT_SECRETTwitter OAuth2 Client Secret
TWITTER_REDIRECT_URIOAuth 回调 URL
PORT服务端口(默认 8080)
REDIS_URLRedis 连接 URL(未设置时回退文件存储)

补充说明:TWITTER_REDIRECT_URI在源码中的默认值是http://localhost:8080/auth/twitter/callback(main.py),因此未配置时本地开发开箱即用;HOST环境变量可控制监听地址(默认0.0.0.0)。此外,Twitter 端配置的回调地址必须与此变量一致。

本地开发

  1. 复制环境变量模板为.env并填入凭据(模板见.env.example,其中包含TWITTER_CLIENT_IDTWITTER_CLIENT_SECRET等键);
  2. 设置TWITTER_REDIRECT_URI=http://localhost:8080/auth/twitter/callback
  3. 将该回调地址加入 Twitter App 的 Callback URIs 列表;
  4. 安装依赖:pip install -r requirements.txt(依赖版本见 requirements.txt,含 fastapi、uvicorn、python-dotenv、requests、pydantic、redis);
  5. 启动服务:python main.py(内部以uvicorn main:app --host 0.0.0.0 --port 8080 --reload运行)。

本地环境未配置 Redis 时会自动使用文件存储,适合快速联调 OAuth 流程与 Chat Tools 端点。

对话使用示例

以下自然语言指令可直接在 Omi 对话中触发对应工具:

  • "Tweet: Just discovered this amazing AI assistant!" →post_tweet
  • "Show my Twitter timeline" →get_timeline
  • "Search Twitter for AI news" →search_tweets
  • "Like the last tweet" →like_tweet
  • "Who mentioned me on Twitter?" →get_mentions
  • "Show @elonmusk's profile" →get_user_profile
  • "Delete my last tweet" →delete_tweet

这些示例与插件首页(/uid时)渲染的示例命令一致(main.py),可作为设置页面向用户展示的引导文案。

限流与错误处理

Twitter API 存在严格的速率限制,官方 README 给出的参考值如下:

  • 每 24 小时发推数:随账号类型变化
  • 读操作:每 15 分钟 300–900 次请求
  • 搜索:每 15 分钟 180 次请求

插件对限流与错误做了如下处理(可从源码确认):

  • twitter_api_request 对非 2xx 响应返回{"error": <响应体>, "status_code": <状态码>},由各工具端点包装为ChatToolResponse(error=...)返回给用户;
  • token 过期或缺失时给出明确的引导性错误(如"请先在应用设置中连接 Twitter 账号");
  • 发推长度超限在服务端即被拦截(main.py),返回 280 字符上限提示,避免白白消耗 API 配额。

故障排查速查

现象排查方向
授权页报 500 / "credentials not configured"TWITTER_CLIENT_ID未设置,检查环境变量(main.py)
回调报 "State mismatch"OAuth state 过期(10 分钟 TTL)或 uid 不一致,重新发起授权
回调报 "Token exchange failed"回调地址与 Twitter App 配置不一致,或 Client Secret 错误
工具调用报 "Please connect your Twitter account first"用户未完成授权,或 token 已删除
工具调用偶发失败后自动恢复大概率是 token 刷新或限流触顶,查看服务日志中的Twitter API error/Token expired ... refreshing输出

本文所涉及的全部源码均位于 plugins/omi-twitter-chat-tools-app/ 目录,读者可按需深入阅读 main.py、db.py、models.py、railway.toml 与 Procfile,将该插件的部署与开发模式复用到其他社交平台集成场景。

【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 4:49:41

HarmonyOS 6聊天页面开发实战与性能优化

1. 项目背景与核心价值作为一名在移动端开发领域深耕多年的开发者&#xff0c;我见证了HarmonyOS从诞生到成熟的完整历程。HarmonyOS 6作为最新版本&#xff0c;在分布式能力、性能优化和开发体验上都有了显著提升。这次我将通过一个聊天页面实战案例&#xff0c;带大家深入理解…

作者头像 李华
网站建设 2026/9/17 4:48:27

sed -i 安全使用指南:跨平台陷阱、原子性原理与生产避坑实践

1. 为什么你写的sed -i总是报错、备份失效或悄悄改错文件&#xff1f;“sed -i不就是原地替换文本嘛&#xff0c;一行命令搞定”——这是我刚接触 Linux 时最自信的错觉。直到某次线上配置批量更新&#xff0c;用sed -i s/old/new/g *.conf批量修改 Nginx 配置后&#xff0c;三…

作者头像 李华
网站建设 2026/9/17 4:47:57

Galgame短评合集指南:卡片式短评与五维评分体系

短评合集这东西&#xff0c;我是从三年前开始攒的。一开始只是打完一部随手在备忘录里敲两行字&#xff0c;后来越攒越多&#xff0c;干脆整理成一份公开的 Galgame 短评合集&#xff0c;按通关时间倒序排&#xff0c;每隔一段时间做一次增补&#xff0c;这次的“9.12更新”已经…

作者头像 李华
网站建设 2026/9/17 4:45:55

Python资产管理系统实战:从数据模型到状态机与定时任务

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 4:45:42

FPGA信号发生器实战:DDS原理与模拟输出链路设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 4:45:33

DirectX 12资源状态详解:从Resource State到ResourceBarrier实战

很多人一开始接触 DirectX 12 时&#xff0c;最容易被劝退的地方往往不是渲染管线本身&#xff0c;而是“资源状态 Resource State”这套看着没啥存在感、实际上无处不在的状态机。我在 DX11 时代从没被要求手动管理过资源状态&#xff0c;绑定个 SRV 直接采样就完事了&#xf…

作者头像 李华