如何使用 discord.py 跟踪入会申请并提醒审核人员?
可以通过原始 Gateway 事件尝试实现提醒
如果使用 Discord 自带的 Apply to Join 入会申请,而 discord.py 尚未封装对应的高级事件,可以尝试监听原始 Gateway 消息,向工作人员频道发送 @ 提醒。
查阅时,discord.py 的入会申请支持 PR #10497 仍处于 Open 状态,稳定版 API 文档也没有对应的 join_request 接口。因此,不能直接把开发中 PR 的事件名称当作已发布 API 使用,但这也不意味着机器人完全无法收到申请事件。
为什么不能用成员加入事件代替
你的目标是在申请等待审核时通知工作人员。Discord 的 Apply to Join 要求申请获批后才能加入服务器,等待中的申请人无法查看服务器内容。这个阶段与普通成员加入不同。Discord 入会申请说明
几个容易混淆的事件和属性分别用于不同的情况:
on_member_join:成员加入服务器,不能直接代表提交了入会申请。Member.pending:成员是否还在等待完成 Membership Screening。on_member_update:可用于观察pending等成员属性变化,不能用来读取入会申请表。
例如,before.pending and not after.pending 表示成员完成了规则筛选,但不能据此判断有新的人工审核申请。discord.py 成员事件与属性文档
接入申请事件前需要确认什么
Discord 的入会申请文档草案列出了以下 Gateway 事件:
GUILD_JOIN_REQUEST_CREATEGUILD_JOIN_REQUEST_UPDATEGUILD_JOIN_REQUEST_DELETE
草案要求机器人具备 KICK_MEMBERS 权限,服务器也需要启用相应的申请审核功能;接收创建事件还涉及 GUILD_MODERATION intent。这份文档的 PR #8531 查阅时尚未合并,接入时应再次确认 PR 状态和实际事件结构。
准备步骤如下:
- 确认服务器使用的是 Apply to Join。
- 为机器人配置所需的
KICK_MEMBERS权限,并在代码中开启intents.moderation。 - 为通知频道配置查看频道和发送消息的权限。
- 确保工作人员角色允许被提及,或者机器人具有相应的提及权限。
- 用测试申请确认实际收到的事件名称、申请 ID 和状态字段。
不要把 CREATE 一律视为“已提交待审核”。文档草案区分了 STARTED 和 SUBMITTED 等状态,提醒逻辑应筛选 SUBMITTED,并留意状态通过更新事件发生变化的情况。
使用 discord.py 监听原始事件
on_socket_raw_receive 可以收到尚未由库解析的 WebSocket 消息,但必须在创建客户端时开启 enable_debug_events=True。discord.py 原始消息事件文档
下面的示例从事件数据的 request 对象读取申请信息。使用前,需要将这一结构与你实际收到的载荷核对。示例使用提供 Intents.moderation 的 discord.py 2.x,代码尚未在目标服务器实测。
import json
import logging
import os
import discord
GUILD_ID = 123456789012345678
STAFF_CHANNEL_ID = 234567890123456789
STAFF_ROLE_ID = 345678901234567890
intents = discord.Intents.none()
intents.guilds = True
intents.moderation = True
client = discord.Client(
intents=intents,
enable_debug_events=True,
)
# 演示用去重集合;重启后会清空。
notified = set()
@client.event
async def on_socket_raw_receive(msg):
try:
payload = json.loads(msg)
except (json.JSONDecodeError, UnicodeDecodeError, TypeError):
return
if payload.get('op') != 0:
return
if payload.get('t') not in {
'GUILD_JOIN_REQUEST_CREATE',
'GUILD_JOIN_REQUEST_UPDATE',
}:
return
data = payload.get('d') or {}
if str(data.get('guild_id')) != str(GUILD_ID):
return
request = data.get('request')
if not isinstance(request, dict):
logging.warning('申请事件结构不符合预期,请核对载荷字段')
return
if request.get('status') != 'SUBMITTED':
return
request_id = request.get('id')
if request_id is None:
logging.warning('申请事件缺少预期的申请 ID 字段')
return
key = (GUILD_ID, str(request_id))
if key in notified:
return
try:
channel = client.get_channel(STAFF_CHANNEL_ID)
if channel is None:
channel = await client.fetch_channel(STAFF_CHANNEL_ID)
if not isinstance(channel, discord.TextChannel):
logging.error('通知频道必须是服务器文字频道')
return
if channel.guild.id != GUILD_ID:
logging.error('通知频道不属于目标服务器')
return
await channel.send(
f'<@&{STAFF_ROLE_ID}> 有新的入会申请待审核,请查看申请列表。',
allowed_mentions=discord.AllowedMentions(
everyone=False,
users=False,
roles=[discord.Object(id=STAFF_ROLE_ID)],
replied_user=False,
),
)
except discord.HTTPException:
logging.exception('发送入会申请提醒失败')
return
notified.add(key)
client.run(os.environ['DISCORD_BOT_TOKEN'])
替换三个 ID,并通过环境变量提供机器人 Token。已有机器人项目可以把监听逻辑整合到现有客户端中,创建客户端时保留 enable_debug_events=True。
如果收不到事件,监听原始消息也无法补出申请数据。先检查申请功能、权限和 intent,再确认 Discord 是否向该机器人下发相应事件。排查载荷时只记录必要字段,避免将申请回答完整写入普通日志。
怎样减少重复提醒和漏报
示例只演示在线通知,长期运行还需要补充两点:
- 持久化去重和重试:用服务器 ID、申请 ID 保存通知状态,并在并发处理时避免同一申请重复发送。发送失败后需要重试;示例中的集合会随进程重启而清空。
- 补偿离线期间的申请:实时事件无法单独保证离线期间零漏报。如果接入时可用的申请列表接口允许 Bot 查询,可以在启动后或定期核对待审核列表;调用方式、分页和权限应以当时可用的文档为准。
如果暂时无法稳定获取原生申请事件,可以先设置定时提醒,让工作人员检查 Discord 申请列表。也可以改用由你控制的外部申请表,在提交成功后调用机器人或 webhook 通知工作人员。这会改变申请入口,需要单独安排审核流程。
不要通过自动化普通用户账号来补足 Bot 接口能力;Discord 的开发文档明确限制这类 self-bot 用法。Discord 账号与 Bot 使用说明
备注:内容仅供参考。
