
国际版通讯工具接入摘要在全球化协作与出海业务快速发展的今天将 Slack、Telegram、Discord 等国际主流 IMInstant Messaging工具接入自有系统已经成为许多团队构建通知、客服、社区运营能力的基础需求。然而这三类工具在 API 设计、鉴权方式、消息模型、Webhook 机制乃至网络可达性上存在显著差异开发者如果直接套用同一套接入思路往往会踩不少坑。本文将从协议与鉴权、消息发送、事件接收、富文本与交互组件、以及工程化落地五个维度系统对比 Slack、Telegram、Discord 的支持差异并给出完整可运行的代码示例帮助你在实际项目中做出合理的技术选型与实现。引言假设你正在为一家出海 SaaS 产品开发一套多渠道通知中心需要把系统告警、订单状态、用户反馈推送到客户使用的 IM 工具里。你很快会发现Slack 是典型的企业级 Bot OAuth 应用模型权限粒度细、审核严格Telegram 是Bot API 简单 Token模型上手极快但功能边界受官方 Bot 平台限制Discord 则是Gateway 长连接 REST API双通道模型事件驱动能力最强但心智负担也最高。如果只写一份通用适配器就想覆盖三者通常会在鉴权刷新、消息格式、事件去重等环节翻车。因此理解它们背后的设计哲学差异比记住几个 API 端点更重要。下面进入正文。一、鉴权与接入模型对比1.1 三种工具的鉴权方式工具鉴权方式凭证形态典型有效期SlackOAuth 2.0 Bot Tokenxoxb-/xoxp-Token长期有效可撤销TelegramBot Token123456:ABC-DEF...长期有效DiscordBot Token Application IDBot token长期有效可重置1.2 SlackOAuth 安装流程Slack 应用需要先创建 App、配置 Scopes再通过 OAuth 让工作区管理员授权安装最终拿到 Bot Token。# slack_oauth.py# 演示 Slack OAuth v2 安装流程简化版importosimportrequestsfromflaskimportFlask,request,redirect appFlask(__name__)CLIENT_IDos.environ[SLACK_CLIENT_ID]CLIENT_SECRETos.environ[SLACK_CLIENT_SECRET]REDIRECT_URIos.environ[SLACK_REDIRECT_URI]# 需要的最小权限发消息 读取频道SCOPESchat:write,channels:readapp.route(/slack/install)definstall():跳转到 Slack 授权页url(https://slack.com/oauth/v2/authorizef?client_id{CLIENT_ID}fscope{SCOPES}fredirect_uri{REDIRECT_URI})returnredirect(url)app.route(/slack/oauth/callback)defoauth_callback():接收 Slack 回调换取 access_tokencoderequest.args.get(code)resprequests.post(https://slack.com/api/oauth.v2.access,data{client_id:CLIENT_ID,client_secret:CLIENT_SECRET,code:code,redirect_uri:REDIRECT_URI,},timeout10,)dataresp.json()ifnotdata.get(ok):returnfOAuth 失败:{data},400bot_tokendata[access_token]# xoxb- 开头team_iddata[team][id]# 实际项目应持久化 bot_token 与 team_id 的映射print(f安装成功 team{team_id}token{bot_token[:12]}...)return安装成功可以关闭此页面if__name____main__:app.run(port3000)1.3 TelegramToken 即一切Telegram 的接入极其简单向BotFather申请一个 Bot 后即可拿到 Token所有 API 调用都只需在 URL 中带上 Token。# telegram_basic.py# Telegram Bot 最小可用示例获取自身信息importosimportrequests BOT_TOKENos.environ[TELEGRAM_BOT_TOKEN]BASEfhttps://api.telegram.org/bot{BOT_TOKEN}defget_me():resprequests.get(f{BASE}/getMe,timeout10)returnresp.json()if__name____main__:print(get_me())1.4 DiscordToken Application IDDiscord 的 Bot Token 需要配合 Application ID 使用且调用 REST API 时必须在请求头中加上Authorization: Bot token。# discord_basic.py# 获取 Discord Bot 自身信息importosimportrequests BOT_TOKENos.environ[DISCORD_BOT_TOKEN]HEADERS{Authorization:fBot{BOT_TOKEN}}defget_me():resprequests.get(https://discord.com/api/v10/users/me,headersHEADERS,timeout10,)returnresp.json()if__name____main__:print(get_me())小结Slack 的接入门槛最高需要 OAuth 与 Scope 配置Telegram 最低一个 Token 走天下Discord 居中Token 应用配置。二、消息发送 API 的差异2.1 端点与请求体对比工具发送端点频道标识请求体关键字段SlackPOST /api/chat.postMessagechannelID 或名称text,blocksTelegramPOST /bottoken/sendMessagechat_idtext,parse_modeDiscordPOST /channels/{id}/messagesURL 中的channel_idcontent,embeds2.2 统一封装示例下面给出一个简单的多渠道发送器把三种工具的发送逻辑抽象成同一接口# notifier.py# 统一的多渠道消息发送器importosimportrequestsfromabcimportABC,abstractmethod TIMEOUT10classNotifier(ABC):通知器抽象基类abstractmethoddefsend(self,target:str,text:str)-dict:...classSlackNotifier(Notifier):def__init__(self,bot_token:str):self.tokenbot_token self.headers{Authorization:fBearer{bot_token},Content-Type:application/json; charsetutf-8,}defsend(self,target:str,text:str)-dict:# target 为 channel ID例如 C01234567resprequests.post(https://slack.com/api/chat.postMessage,headersself.headers,json{channel:target,text:text},timeoutTIMEOUT,)dataresp.json()ifnotdata.get(ok):raiseRuntimeError(fSlack 发送失败:{data})returndataclassTelegramNotifier(Notifier):def__init__(self,bot_token:str):self.basefhttps://api.telegram.org/bot{bot_token}defsend(self,target:str,text:str)-dict:# target 为 chat_id例如 -1001234567890resprequests.post(f{self.base}/sendMessage,json{chat_id:target,text:text,parse_mode:HTML},timeoutTIMEOUT,)dataresp.json()ifnotdata.get(ok):raiseRuntimeError(fTelegram 发送失败:{data})returndataclassDiscordNotifier(Notifier):def__init__(self,bot_token:str):self.headers{Authorization:fBot{bot_token},Content-Type:application/json,}defsend(self,target:str,text:str)-dict:# target 为 channel_idresprequests.post(fhttps://discord.com/api/v10/channels/{target}/messages,headersself.headers,json{content:text},timeoutTIMEOUT,)ifresp.status_code300:raiseRuntimeError(fDiscord 发送失败:{resp.status_code}{resp.text})returnresp.json()if__name____main__:slackSlackNotifier(os.environ[SLACK_BOT_TOKEN])slack.send(os.environ[SLACK_CHANNEL_ID],Hello from Slack!)tgTelegramNotifier(os.environ[TELEGRAM_BOT_TOKEN])tg.send(os.environ[TELEGRAM_CHAT_ID],bHello/b from Telegram!)dcDiscordNotifier(os.environ[DISCORD_BOT_TOKEN])dc.send(os.environ[DISCORD_CHANNEL_ID],Hello from Discord!)关键差异Slack 返回ok: false表示失败HTTP 状态码恒为 200必须判断ok字段。Telegram 同样返回ok字段但 HTTP 状态码会随错误变化。Discord 严格使用 HTTP 状态码2xx 即成功且对频率限制429返回Retry-After头。三、事件接收机制对比3.1 三种模型SlackEvents APIHTTP Webhook 推送 Socket ModeWebSocket 长连接。TelegramWebhookHTTPS 回调或 Long PollinggetUpdates。DiscordGatewayWebSocket 长连接为主Interaction EndpointHTTP为辅。3.2 Slack Events API 校验Slack 要求对请求签名做验证防止伪造# slack_events.py# 校验 Slack Events API 请求签名importhmacimporthashlibimporttimeimportosfromflaskimportFlask,request,jsonify appFlask(__name__)SIGNING_SECRETos.environ[SLACK_SIGNING_SECRET].encode()defverify_slack_signature(req)-bool:timestampreq.headers.get(X-Slack-Request-Timestamp,)signaturereq.headers.get(X-Slack-Signature,)# 防止重放攻击时间戳与当前时间差超过 5 分钟直接拒绝ifabs(time.time()-int(timestamp))60*5:returnFalsebasestringfv0:{timestamp}:{req.get_data(as_textTrue)}.encode()my_sigv0hmac.new(SIGNING_SECRET,basestring,hashlib.sha256).hexdigest()returnhmac.compare_digest(my_sig,signature)app.route(/slack/events,methods[POST])defslack_events():ifnotverify_slack_signature(request):returninvalid signature,403payloadrequest.get_json(forceTrue)# URL 验证握手ifpayload.get(type)url_verification:returnjsonify({challenge:payload[challenge]})eventpayload.get(event,{})print(收到 Slack 事件:,event.get(type),event.get(text))return,2003.3 Telegram Webhook 设置# telegram_webhook.py# 设置 Telegram Webhook 并处理消息importosimportrequestsfromflaskimportFlask,request BOT_TOKENos.environ[TELEGRAM_BOT_TOKEN]BASEfhttps://api.telegram.org/bot{BOT_TOKEN}appFlask(__name__)defset_webhook(public_url:str):将 Webhook 指向我们的 HTTPS 服务resprequests.post(f{BASE}/setWebhook,json{url:f{public_url}/telegram/webhook},timeout10,)returnresp.json()app.route(/telegram/webhook,methods[POST])deftelegram_webhook():updaterequest.get_json(forceTrue)messageupdate.get(message)orupdate.get(edited_message)ifmessage:chat_idmessage[chat][id]textmessage.get(text,)# 回显消息requests.post(f{BASE}/sendMessage,json{chat_id:chat_id,text:f你说了:{text}},timeout10,)return,200if__name____main__:print(set_webhook(https://your-domain.example.com))app.run(port3000)3.4 Discord Gateway 长连接Discord 的事件通过 WebSocket 推送需要处理心跳、重连和sequence序号# discord_gateway.py# Discord Gateway 最小示例接收消息事件importosimportjsonimportasyncioimportwebsockets BOT_TOKENos.environ[DISCORD_BOT_TOKEN]GATEWAY_URLwss://gateway.discord.gg/?v10encodingjson# Gateway OpcodeOP_DISPATCH0OP_HEARTBEAT1OP_IDENTIFY2OP_HELLO10OP_HEARTBEAT_ACK11asyncdefrun_bot():asyncwithwebsockets.connect(GATEWAY_URL)asws:hellojson.loads(awaitws.recv())heartbeat_intervalhello[d][heartbeat_interval]/1000asyncdefheartbeat():whileTrue:awaitws.send(json.dumps({op:OP_HEARTBEAT,d:None}))awaitasyncio.sleep(heartbeat_interval)asyncio.create_task(heartbeat())# 发送 Identify声明需要的 intents# GUILDS(1) | GUILD_MESSAGES(512) | MESSAGE_CONTENT(32768)awaitws.send(json.dumps({op:OP_IDENTIFY,d:{token:BOT_TOKEN,intents:1|512|32768,properties:{$os:linux,$browser:demo,$device:demo},},}))asyncforrawinws:payloadjson.loads(raw)ifpayload[op]OP_DISPATCHandpayload[t]MESSAGE_CREATE:msgpayload[d]print(f[Discord]{msg[author][username]}:{msg.get(content)})if__name____main__:asyncio.run(run_bot())关键差异Slack 事件需要签名校验Telegram 可选用secret_token头校验Discord Gateway 通过 Token 鉴权。Telegram 的 Long Polling 模式在无公网 IP 时非常方便Slack 的 Socket Mode 类似。Discord 的intents机制要求显式声明订阅哪些事件否则收不到消息内容。四、富文本与交互组件4.1 消息格式支持工具富文本方案交互组件SlackBlock KitJSON按钮、下拉、模态框、Home TabTelegramHTML / MarkdownV2 Inline Keyboard按钮、回调查询、Web AppDiscordEmbeds Components按钮、下拉、模态框4.2 Slack Block Kit 示例# slack_blocks.py# 使用 Block Kit 发送带按钮的卡片importosimportrequests BOT_TOKENos.environ[SLACK_BOT_TOKEN]CHANNELos.environ[SLACK_CHANNEL_ID]blocks[{type:section,text:{type:mrkdwn,text:*新订单*\n订单号: #12345},},{type:actions,elements:[{type:button,text:{type:plain_text,text:确认},style:primary,action_id:order_confirm,value:12345,},{type:button,text:{type:plain_text,text:取消},style:danger,action_id:order_cancel,value:12345,},],},]resprequests.post(https://slack.com/api/chat.postMessage,headers{Authorization:fBearer{BOT_TOKEN}},json{channel:CHANNEL,blocks:blocks,text:新订单 #12345},timeout10,)print(resp.json())4.3 Telegram Inline Keyboard 示例# telegram_inline.py# 发送带 Inline Keyboard 的消息importosimportrequests BOT_TOKENos.environ[TELEGRAM_BOT_TOKEN]CHAT_IDos.environ[TELEGRAM_CHAT_ID]payload{chat_id:CHAT_ID,text:b新订单/b\n订单号: code#12345/code,parse_mode:HTML,reply_markup:{inline_keyboard:[[{text:✅ 确认,callback_data:order_confirm:12345},{text: