webhook-impersonation

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 视图用的结构化引用——于是两个界面达成一致,且以消息正文本身作为唯一真相来源。编辑一条回复时会把前缀重新贴回去,所以一次编辑不会悄悄把回复降格成普通消息。转发的消息用的是同样的前缀手法,而一种更老的标记格式在读取时仍被识别,好让早期版本发出的消息继续正常渲染。

相关

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

webhook-impersonation