新增 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: 媒体资源处理
89 lines
2.7 KiB
Python
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 []
|