Webhook 身份代发
Discord 机器人只以自己的身份发言——一个身份,机器人自己的名字和头像。YouTeacher 的 Discord 桥接需要的恰好相反:每一条从桥接发出去的消息,都应该看起来像是那个真正写下它的 YouTeacher 用户发的,带着他自己的显示名和他自己的头像。桥接靠 Discord webhook 来做到这一点——webhook 允许单一发送方在每条消息上覆盖名字和头像。
每个频道一个受管 webhook。 桥接第一次往某个频道发消息时,会先找它已经拥有的那个 webhook——靠一个固定名字 YouTeacher Bridge 来识别——只有在找不到时才新建一个。这个 webhook 按频道缓存,所以查找只发生一次。此后该频道里所有用户的消息都走同一个共享 webhook 发出,但每次 send 都用该用户自己的显示名和头像去覆盖,于是尽管底层发送方只有一个 webhook,Discord 显示出来却是每条消息各有各的作者。没有头像的用户回退到一个默认头像,于是他在这里和在网页端看起来一致。发出的消息关闭了提及解析,所以文本不会仅仅因为被转发这个动作就变成一个真的 @ 提醒。
一个归属存储记住每条消息属于谁。 通过 webhook 发送会丢掉回指 YouTeacher 用户的那条线——Discord 只知道"某个 webhook 发了这条"。所以每次发送成功后,桥接都会以新消息的 id 为键,记下它属于哪个 YouTeacher 用户(以及所用的名字和头像)。这个存储是内存里的,30 天过期,定期清理。它有意不做复制:机器人以单一实例服务单一 guild,没有第二份副本需要同步。
归属校验施加在编辑和删除上——不只是发送。 因为大家共享一个频道 webhook,单凭 webhook token 说不清哪条消息是谁的。在编辑或删除之前,桥接会做两道检查。第一:目标消息必须确实是由这个桥接的 webhook 发出的——来自别处的消息一律拒绝。第二:那条消息的归属记录必须指向正在发起请求的同一个 YouTeacher 用户。只有两者都成立,编辑或删除才会执行。这是承重的安全性质——它阻止一个用户去编辑或删除另一个用户的消息,哪怕在 Discord 那一层他们都躲在同一个发送方后面。
回复被编码进消息正文,而不是回复字段。 Discord 正常的回复链接(message_reference)在通过 webhook 发送时会被悄悄丢弃,所以桥接用不了它。取而代之,它在正文前面加一行小小的、Discord 原生的 markdown:一行不起眼的引用,写明在回复谁,外加一个可点击、跳到原消息的链接。Discord 自己的客户端会把它渲染成类似回复的 UI,而桥接在读取时又把这同一行前缀解析出来,还原成 YouTeacher 视图用的结构化引用——于是两个界面达成一致,且以消息正文本身作为唯一真相来源。编辑一条回复时会把前缀重新贴回去,所以一次编辑不会悄悄把回复降格成普通消息。转发的消息用的是同样的前缀手法,而一种更老的标记格式在读取时仍被识别,好让早期版本发出的消息继续正常渲染。
相关
- youteacher_discord ——这条代发路径所在的 Discord 桥接