So I need my bot to print in which channel it sents a message. This is my code:
#client.command()
async def test(ctx):
await ctx.reply('hello', mention_author=True)
channel = #what goes here????
print('i said hello in', (channel))
I've tried all of these:
user = bot.get_user(user_id)
message.author.username
username = client.get_user(user_id)
The things you tried are referencing user id. I have not used discord.py in a while, but you will have to reference the channel id and not the user id. Maybe try:
ctx.message.channel.id
I can't remember if this returns an object or a string so you may have to format further to get the string.
Related
#pytest.mark.asyncio
async def timecheck():
tm = time.localtime()
if tm[6] == 6:
converter = MemberConverter()
member = await converter.convert(ctx, 'KexoTV#8921')
ctx.author = member
invitelink = await ctx.channel.create_invite(max_uses=1, unique=True)
await ctx.author.send(invitelink)
#tasks.loop(seconds=60)
async def mainloop():
asyncio.create_task(timecheck())
mainloop.start()
Problem i have is that i don't have ctx defined, i can't put in into def input because the bot will give errors like ctx is not defined
You don't need the ctx object to send messages. Theres multiple other ways, like fetching a guild, channel or user by id, and then sending a message to them. All you need is your Client to already be on a server with who you want to dm to. In your case:
user = client.get_user(id) # get_user is synchronous
await user.send(inviteLink)
Of course, to do that, you need to save the id of who you want to send the message to.
Thanks a lot Thörni, it didn't work but it helped me to find the correct commands and that is:
user = await client.fetch_user(id)
await user.send(inviteLink)
Outcome
To snipe messages sent in X channel instead of all the channels within the Discord guild. That is, it should only track message deletions in that one channel (identified by its ID), and only respond to the !snipe command in that same channel. The current code I have here snipes every message sent within the Discord guild.
Question
How can I snipe messages sent in X channel instead of the entire guild?
I mostly intend to run this bot in one guild. However, it would be nice if it could scale to multiple guilds if needed.
The code I have so far is below.
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)
I'm going to suggest two slightly different solutions, because the code can be simpler if you're only running this bot on one guild. What's common to both is that you need to check in what channel messages are deleted, and in what channel the !snipe command is sent.
Single-Guild Version
If you're only monitoring/sniping one channel on one guild, then you can only ever have one deleted message to keep track of. Thus, you don't need a dictionary like in your posted code; you can just keep a single message or None.
You're already importing your token from a separate file, so you might as well put the channel ID (which is an int, unlike the bot token) there too for convenience. Note that, by convention, constants (variables you don't intend to change) are usually named in all caps in Python. tokens.py would look something like this:
TOKEN = 'string of characters here'
CHANNEL_ID = 123456789 # actually a 17- or 18-digit integer
And the bot itself:
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)
Multi-Guild Version
If you're monitoring one channel each in multiple guilds, then you need to keep track of them separately. Handily, channel IDs are globally unique, not just within a single guild. So you can keep track of them by ID alone, without having to include the guild ID as well.
You could keep them in a list, but I recommend a set, because checking whether something is in a set or not is faster. Comments to help yourself remember which one is which are probably also a good idea.
TOKEN = 'string of characters here'
# Not a dictionary, even though it uses {}
CHANNEL_IDS = {
# That one guild
123456789,
# The other guild
987654322,
}
Then instead of checking against the single channel ID, you check if it's in the set of multiple IDs.
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)
Note: bare except clauses, like in your original code, are not generally a good idea. Here we only want to catch KeyError, which is what is raised if the requested key isn't in the dictionary.
You could, optionally, implement the same logic in a different way:
message = client.sniped_messages.get(ctx.channel.id)
if message is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
A dictionary's .get() method returns the corresponding item just like normal indexing. However, if there is no such key, instead of raising an exception, it returns a default value (which is None if you don't specify one in the call to get).
If you're using Python 3.8+, the first two lines could also be combined using an assignment expression (using the "walrus operator"), which assigns and checks all at once:
if (message := client.sniped_messages.get(ctx.channel.id)) is None:
await ctx.channel.send("Couldn't find a message to snipe!")
return
These alternative options are mentioned for completeness; all of these ways of doing it are perfectly fine.
I've been trying to send the same message to all the guilds where my bot is, but nothing has worked and I haven't been able to find something similar in the docs. Is this posible? How can I do It?
Edit: ok, lets's imagine that what my bot does is checking a news webpage like BBC or any other. The bot would be checking the web every five minutes and sending links of the news to those guilds where .start (for example) was executed. The way I've implemented this, was by a command that generates a single loop for each guild, the idea was to have just one loop for every guild.
for guild in bot.guilds:
await guild.text_channels[0].send(<message>)
This will get the first text channel found in the guild and send a message to it.
You can get the bot from a context variable by typing ctx.bot.
class send_all(commands.Cog):
def __init__(self,bot):
self.bot = bot
#commands.command()
async def send(self,ctx,*message_to_send):
guild = ctx.message.guild
output = ' '
author = ctx.message.author
for word in message_to_send:
output += word
output += ' '
for member in self.bot.get_all_members():
try:
embed = discord.Embed(title="",colour = discord.Colour.green())
embed.add_field(name="**From server:**",value= guild.name)
embed.add_field(name = "**From Mod/Admin:**",value = author.name)
embed.add_field(name="**Message:**",value = output)
# await ctx.send(embed=embed)
await member.send(embed=embed)
except (discord.HTTPException, discord.Forbidden,AttributeError):
continue
I try to make my bot to get message from DM using this syntax:
for wolf in wolf_list_id:
poll_message = await self.client.get_message(wolf, react_message.id)
The wolf contains the id of user, but the get_message syntax can't take the id from wolf. Any ideas?
like said the doc, the client.get_message methods must take as parameter an channel object and an id.
In case of DM channel, you can pass an user or member object.
For get an user by id, you can use client.get_user_info methods:
user = await client.get_user_info("123456789")
And after, with your userobject, you can get message with ID
message = await client.get_message(user, "135792468")
So, for fix your code, if wolf is a string id, you can use this following code:
for wolf in wolf_list_id:
user = await self.client.get_user_info(wolf)
poll_message = await self.client.get_message(user, react_message.id)
I'm trying to code a simple bot using discord.py, so I started with the fun commands to get familiar with the library.
import discord
client = discord.Client()
#client.event
async def on_message(message):
# we do not want the bot to reply to itself
if message.author == client.user:
return
if message.content.startswith('!hug'):
await message.channel.send(f"hugs {message.author.mention}")
if message.content.startswith('!best'):
user_id = "201909896357216256"
user_mention = ??? # How to convert user_id into a mention
await message.channel.send(f'{user_mention} is the best')
So I finally figured out how to do this after few days of trial and error hoping others would benefit from this and have less pain than I actually had..
The solution was ultimately easy..
if message.content.startswith('!best'):
myid = '<#201909896357216256>'
await client.send_message(message.channel, ' : %s is the best ' % myid)
Updated answer for discord.py 1.x - 2.x (2021):
Some of the other solutions are now obsolete since discord.py's syntaxes has changed and the old versions no longer works.
If you only have the user ID, then it's:
user_id = "201909896357216256"
await message.channel.send(f"<#{user_id}> is the best")
If you have the user/member object, then it's:
await message.channel.send(f"{user.mention} is the best")
If you only have the user name and discriminator (the bot and user must share a server and members cache must be toggled on):
user = discord.utils.get(client.users, name="USERNAME", discriminator="1234")
if user is None:
print("User not found")
else:
await message.channel.send(f"{user.mention} is the best")
Replace message.channel if you have a ctx when using commands instead of on_message.
If you're working on commands, you're best to use discord.py's built in command functions, your hug command will become:
import discord
from discord.ext import commands
#commands.command(pass_context=True)
async def hug(self, ctx):
await self.bot.say("hugs {}".format(ctx.message.author.mention))
This is assuming you've done something like this at the start of your code:
def __init__(self):
self.bot = discord.Client(#blah)
From a User object, use the attribute User.mention to get a string that represents a mention for the user. To get a user object from their ID, you need Client.get_user_info(id). To get the a user from a username ('ZERO') and discriminator ('#6885') use the utility function discord.utils.get(iterable, **attrs). In context:
if message.content.startswith('!best'):
user = discord.utils.get(message.server.members, name = 'ZERO', discriminator = 6885)
# user = client.get_user_info(id) is used to get User from ID, but OP doesn't need that
await client.send_message(message.channel, user.mention + ' mentioned')
If you just want to respond from the on_message callback, you can grab the mention string from the author like so:
#bot.event
async def on_message(message):
# No infinite bot loops
if message.author == bot.user or message.author.bot:
return
mention = message.author.mention
response = f"hey {mention}, you're great!"
await message.channel.send(response)
While OP's issue is long resolved (and likely forgotten) -- If you're building a Discord bot in Python, it's still a bit difficult to find this information - hopefully this helps someone.
If you're trying to use the #bot.command method - the following will work (in python3):
#bot.command(name='ping', help='Ping the bot to text name')
async def ping(ctx):
await ctx.send('Pong ' + format(ctx.author))
print("debug: " + dir(ctx.author))
If you want to display the nickname of the "author" (who called the command) you can use this instead":
#bot.command(name='ping', help='Ping the bot to text name')
async def ping(ctx):
# await ctx.send('Pong {0}'.format(ctx.author))
await ctx.send('Pong ' + format(ctx.author.display_name))
print("debug: " + dir(ctx.author))
Another helpful tip:
You can use dir(ctx.author) to see the attributes of the ctx.author object.