ForcePilot/backend/package/yuxi/channel/extensions/twitter/auth.py
Kris 1c590097be feat(channel): 添加 Twitter 和 Viber 渠道扩展
新增 Twitter 和 Viber 两个渠道扩展。

Twitter 渠道扩展功能模块:
- auth: OAuth 认证管理
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- outbound: 外发消息管理
- streaming: 流式消息处理
- pairing: 用户配对与绑定
- security: 安全校验
- dedupe: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- session: 会话管理
- tweets: 推文管理
- social: 社交互动
- reactions: 表情反应
- media: 媒体资源处理

Viber 渠道扩展功能模块:
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- outbound: 外发消息管理
- streaming: 流式消息处理
- pairing: 用户配对与绑定
- security: 安全校验
- dedupe: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- rate_limiter: 速率限制
- media: 媒体资源处理
2026-05-21 11:57:22 +08:00

89 lines
2.7 KiB
Python

from __future__ import annotations
import asyncio
import logging
import tweepy
logger = logging.getLogger(__name__)
X_API_V2_BASE = "https://api.x.com"
X_UPLOAD_BASE = "https://upload.twitter.com"
def create_tweepy_client(account: dict) -> tweepy.Client:
return tweepy.Client(
consumer_key=account["api_key"],
consumer_secret=account["api_secret"],
access_token=account["access_token"],
access_token_secret=account["access_secret"],
return_type=dict,
wait_on_rate_limit=False,
)
async def verify_credentials(
account: dict, timeout_seconds: float = 5.0
) -> dict | None:
try:
client = create_tweepy_client(account)
response = await asyncio.to_thread(
client.get_me,
user_fields=["id", "username", "name", "profile_image_url"],
)
data = response.get("data", {})
if data:
return {
"user_id": data.get("id", ""),
"username": data.get("username", ""),
"name": data.get("name", ""),
"profile_image_url": data.get("profile_image_url", ""),
}
except tweepy.Unauthorized:
logger.warning("X API: OAuth 1.0a credentials invalid or revoked")
except tweepy.TooManyRequests:
logger.warning("X API: rate limited during credential verification")
except Exception:
logger.exception("X API: credential verification failed")
return None
async def probe(account: dict, timeout_seconds: float = 5.0) -> bool:
user = await verify_credentials(account, timeout_seconds)
return user is not None
async def get_user_by_username(account: dict, username: str) -> dict | None:
client = create_tweepy_client(account)
try:
response = await asyncio.to_thread(
client.get_user,
username=username,
user_fields=[
"id",
"username",
"name",
"description",
"public_metrics",
"profile_image_url",
],
)
return response.get("data", {})
except Exception as e:
logger.warning("Twitter get_user_by_username error: %s", e)
return None
async def get_users_by_ids(account: dict, user_ids: list[str]) -> list[dict]:
client = create_tweepy_client(account)
try:
response = await asyncio.to_thread(
client.get_users,
ids=user_ids,
user_fields=["id", "username", "name", "profile_image_url"],
)
return response.get("data", [])
except Exception as e:
logger.warning("Twitter get_users_by_ids error: %s", e)
return []