如何截取来自特定频道的消息 (discord.py)
How to snipe messages from a specific channel (discord.py)
结果
狙击在 X 频道而不是 Discord 公会内的所有频道发送的消息。也就是说,它应该只跟踪那个频道(由其 ID 标识)中的消息删除,并且只响应同一频道中的 !snipe
命令。我这里的当前代码会截断 Discord 公会内发送的每条消息。
问题
如何屏蔽在 X 频道而不是整个公会中发送的消息?
我主要打算 运行 这个机器人在一个公会中。但是,如果需要的话它可以扩展到多个公会就好了。
我目前的代码如下。
import discord
from discord.ext import commands
from tokens import token
client = commands.Bot(command_prefix="!", self_bot=False)
client.sniped_messages = {}
@client.event
async def on_ready():
print("Your bot is ready.")
@client.event
async def on_message_delete(message):
print(f'sniped message {message}')
client.sniped_messages[message.guild.id] = (
message.content, message.author, message.channel.name, message.created_at)
@client.command()
async def snipe(ctx):
try:
contents, author, channel_name, time = client.sniped_messages[ctx.guild.id]
except:
await ctx.channel.send("Couldn't find a message to snipe!")
return
embed = discord.Embed(description=contents,
color=discord.Color.purple(), timestamp=time)
embed.set_author(
name=f"{author.name}#{author.discriminator}", icon_url=author.avatar_url)
embed.set_footer(text=f"Deleted in : #{channel_name}")
await ctx.channel.send(embed=embed)
client.run(token, bot=True)
我将建议两个略有不同的解决方案,因为如果您只是 运行 这个机器人在一个公会中,代码会更简单。两者的共同点是需要查看删除了哪些频道的消息,和在哪个频道发送了!snipe
命令。
单公会版
如果你只是monitoring/sniping一个公会的一个频道,那么你只能一个删除的消息来跟踪。因此,您不需要像发布的代码中那样的字典;你可以只保留一条消息或 None
.
您已经从一个单独的文件中导入了您的令牌,因此为了方便起见,您也可以将频道 ID(与机器人令牌不同,它是一个 int
)也放在那里。请注意,按照惯例,常量(您不打算更改的变量)通常在 Python 中全部大写。 tokens.py
看起来像这样:
TOKEN = 'string of characters here'
CHANNEL_ID = 123456789 # actually a 17- or 18-digit integer
以及机器人本身:
import discord
from discord.ext import commands
from tokens import TOKEN, CHANNEL_ID
client = commands.Bot(command_prefix='!')
client.sniped_message = None
@client.event
async def on_ready():
print("Your bot is ready.")
@client.event
async def on_message_delete(message):
# Make sure it's in the watched channel, and not one of the bot's own
# messages.
if message.channel.id == CHANNEL_ID and message.author != client.user:
print(f'sniped message: {message}')
client.sniped_message = message
@client.command()
async def snipe(ctx):
# Only respond to the command in the watched channel.
if ctx.channel.id != CHANNEL_ID:
return
if client.sniped_message is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
message = client.sniped_message
embed = discord.Embed(
description=message.content,
color=discord.Color.purple(),
timestamp=message.created_at
)
embed.set_author(
name=f"{message.author.name}#{message.author.discriminator}",
icon_url=message.author.avatar_url
)
embed.set_footer(text=f"Deleted in: #{message.channel.name}")
await ctx.channel.send(embed=embed)
client.run(TOKEN)
多公会版本
如果您在多个公会中分别监控一个频道,那么您需要分别跟踪它们。方便的是,频道 ID 是 globally unique,而不仅仅是在一个公会内。因此,您可以仅通过 ID 来跟踪它们,而不必同时包含公会 ID。
您可以将它们保存在列表中,但我建议使用 set,因为检查某物是否在集合中会更快。帮助自己记住哪个是哪个的评论可能也是个好主意。
TOKEN = 'string of characters here'
# Not a dictionary, even though it uses {}
CHANNEL_IDS = {
# That one guild
123456789,
# The other guild
987654322,
}
然后检查它是否在多个ID的集合中,而不是检查单个频道ID。
import discord
from discord.ext import commands
from tokens import TOKEN, CHANNEL_IDS
client = commands.Bot(command_prefix='!')
client.sniped_messages = {}
@client.event
async def on_ready():
print("Your bot is ready.")
@client.event
async def on_message_delete(message):
# Make sure it's in a watched channel, and not one of the bot's own
# messages.
if message.channel.id in CHANNEL_IDS and message.author != client.user:
print(f'sniped message: {message}')
client.sniped_messages[message.channel.id] = message
@client.command()
async def snipe(ctx):
# Only respond to the command in a watched channel.
if ctx.channel.id not in CHANNEL_IDS:
return
try:
message = client.sniped_messages[ctx.channel.id]
# See note below
except KeyError:
await ctx.channel.send("Couldn't find a message to snipe!")
return
embed = discord.Embed(
description=message.content,
color=discord.Color.purple(),
timestamp=message.created_at
)
embed.set_author(
name=f"{message.author.name}#{message.author.discriminator}",
icon_url=message.author.avatar_url
)
embed.set_footer(text=f"Deleted in: #{message.channel.name}")
await ctx.channel.send(embed=embed)
client.run(TOKEN)
注意:,就像在您的原始代码中一样,通常不是一个好主意。在这里,我们只想捕获 KeyError
,如果请求的键不在字典中,则会引发此问题。
您可以选择以不同的方式实现相同的逻辑:
message = client.sniped_messages.get(ctx.channel.id)
if message is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
字典的.get()
方法returns对应的项目就像正常的索引一样。但是,如果没有这样的键,它不会引发异常,而是 returns 默认值(如果您在 get
的调用中未指定默认值,则默认值为 None
)。
如果您使用的是 Python 3.8+,前两行也可以使用 assignment expression 组合(使用“海象运算符”),它会同时赋值和检查:
if (message := client.sniped_messages.get(ctx.channel.id)) is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
为了完整起见,提到了这些备选方案;所有这些方法都很好。
结果
狙击在 X 频道而不是 Discord 公会内的所有频道发送的消息。也就是说,它应该只跟踪那个频道(由其 ID 标识)中的消息删除,并且只响应同一频道中的 !snipe
命令。我这里的当前代码会截断 Discord 公会内发送的每条消息。
问题
如何屏蔽在 X 频道而不是整个公会中发送的消息?
我主要打算 运行 这个机器人在一个公会中。但是,如果需要的话它可以扩展到多个公会就好了。
我目前的代码如下。
import discord
from discord.ext import commands
from tokens import token
client = commands.Bot(command_prefix="!", self_bot=False)
client.sniped_messages = {}
@client.event
async def on_ready():
print("Your bot is ready.")
@client.event
async def on_message_delete(message):
print(f'sniped message {message}')
client.sniped_messages[message.guild.id] = (
message.content, message.author, message.channel.name, message.created_at)
@client.command()
async def snipe(ctx):
try:
contents, author, channel_name, time = client.sniped_messages[ctx.guild.id]
except:
await ctx.channel.send("Couldn't find a message to snipe!")
return
embed = discord.Embed(description=contents,
color=discord.Color.purple(), timestamp=time)
embed.set_author(
name=f"{author.name}#{author.discriminator}", icon_url=author.avatar_url)
embed.set_footer(text=f"Deleted in : #{channel_name}")
await ctx.channel.send(embed=embed)
client.run(token, bot=True)
我将建议两个略有不同的解决方案,因为如果您只是 运行 这个机器人在一个公会中,代码会更简单。两者的共同点是需要查看删除了哪些频道的消息,和在哪个频道发送了!snipe
命令。
单公会版
如果你只是monitoring/sniping一个公会的一个频道,那么你只能一个删除的消息来跟踪。因此,您不需要像发布的代码中那样的字典;你可以只保留一条消息或 None
.
您已经从一个单独的文件中导入了您的令牌,因此为了方便起见,您也可以将频道 ID(与机器人令牌不同,它是一个 int
)也放在那里。请注意,按照惯例,常量(您不打算更改的变量)通常在 Python 中全部大写。 tokens.py
看起来像这样:
TOKEN = 'string of characters here'
CHANNEL_ID = 123456789 # actually a 17- or 18-digit integer
以及机器人本身:
import discord
from discord.ext import commands
from tokens import TOKEN, CHANNEL_ID
client = commands.Bot(command_prefix='!')
client.sniped_message = None
@client.event
async def on_ready():
print("Your bot is ready.")
@client.event
async def on_message_delete(message):
# Make sure it's in the watched channel, and not one of the bot's own
# messages.
if message.channel.id == CHANNEL_ID and message.author != client.user:
print(f'sniped message: {message}')
client.sniped_message = message
@client.command()
async def snipe(ctx):
# Only respond to the command in the watched channel.
if ctx.channel.id != CHANNEL_ID:
return
if client.sniped_message is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
message = client.sniped_message
embed = discord.Embed(
description=message.content,
color=discord.Color.purple(),
timestamp=message.created_at
)
embed.set_author(
name=f"{message.author.name}#{message.author.discriminator}",
icon_url=message.author.avatar_url
)
embed.set_footer(text=f"Deleted in: #{message.channel.name}")
await ctx.channel.send(embed=embed)
client.run(TOKEN)
多公会版本
如果您在多个公会中分别监控一个频道,那么您需要分别跟踪它们。方便的是,频道 ID 是 globally unique,而不仅仅是在一个公会内。因此,您可以仅通过 ID 来跟踪它们,而不必同时包含公会 ID。
您可以将它们保存在列表中,但我建议使用 set,因为检查某物是否在集合中会更快。帮助自己记住哪个是哪个的评论可能也是个好主意。
TOKEN = 'string of characters here'
# Not a dictionary, even though it uses {}
CHANNEL_IDS = {
# That one guild
123456789,
# The other guild
987654322,
}
然后检查它是否在多个ID的集合中,而不是检查单个频道ID。
import discord
from discord.ext import commands
from tokens import TOKEN, CHANNEL_IDS
client = commands.Bot(command_prefix='!')
client.sniped_messages = {}
@client.event
async def on_ready():
print("Your bot is ready.")
@client.event
async def on_message_delete(message):
# Make sure it's in a watched channel, and not one of the bot's own
# messages.
if message.channel.id in CHANNEL_IDS and message.author != client.user:
print(f'sniped message: {message}')
client.sniped_messages[message.channel.id] = message
@client.command()
async def snipe(ctx):
# Only respond to the command in a watched channel.
if ctx.channel.id not in CHANNEL_IDS:
return
try:
message = client.sniped_messages[ctx.channel.id]
# See note below
except KeyError:
await ctx.channel.send("Couldn't find a message to snipe!")
return
embed = discord.Embed(
description=message.content,
color=discord.Color.purple(),
timestamp=message.created_at
)
embed.set_author(
name=f"{message.author.name}#{message.author.discriminator}",
icon_url=message.author.avatar_url
)
embed.set_footer(text=f"Deleted in: #{message.channel.name}")
await ctx.channel.send(embed=embed)
client.run(TOKEN)
注意:KeyError
,如果请求的键不在字典中,则会引发此问题。
您可以选择以不同的方式实现相同的逻辑:
message = client.sniped_messages.get(ctx.channel.id)
if message is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
字典的.get()
方法returns对应的项目就像正常的索引一样。但是,如果没有这样的键,它不会引发异常,而是 returns 默认值(如果您在 get
的调用中未指定默认值,则默认值为 None
)。
如果您使用的是 Python 3.8+,前两行也可以使用 assignment expression 组合(使用“海象运算符”),它会同时赋值和检查:
if (message := client.sniped_messages.get(ctx.channel.id)) is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
为了完整起见,提到了这些备选方案;所有这些方法都很好。