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.
Related
I need help making a afk command for my discord server. When the afk command is triggered, my bot doesn't respond with a reasoning when you ping the person whos afk. Also, when you return from being afk and type, the bot doesn't send a message saying "(user) is no longer afk". Please help me and tell me what i'm doing wrong and how can I fix this?
afkdict = {User: "their reason"} # somewhere in the code
#bot.command("afk")
async def afk(ctx, reason=None):
afkdict[ctx.user] = reason
await ctx.send("You are now afk. Beware of the real world!")
#bot.event
async def on_message(message):
afkdict = {user: "their reason"}
# some other checks here
for user, reason in afkdict.items():
if user in message.mentions:
if reason is None:
reason = ""
embed = discord.Embed(title=f"{user} is AFK", color=0xFF0000, description=reason[:2500])
await message.reply()
I was expecting this to work, the way dyno works. When i ran the command i got a message back saying user has no context. I dont know what to do anymore.
I think there's a couple of issues. Firstly, you are redefining afkdict in your on_message function it doesn't matter that you're adding users to it in the afk command. Secondly, when you're doing await message.reply(), you're not actually sending the created embed along with it.
I've resolved those problems and changed the logic slightly. Instead of iterating over the users in the afk_dict and checking if they're mentioned, we're iterating over the mentions and seeing if they're in the afk_dict. I'm also using user.id rather user objects as keys.
# defined somewhere
afk_dict = {}
#bot.command()
async def afk(ctx, reason=None):
afk_dict[ctx.user.id] = reason
await ctx.send("You are now afk. Beware of the real world!")
#bot.event
async def on_message(message):
# do whatever else you're doing here
for user in message.mentions:
if user.id not in afk_dict:
continue
# mentioned used is "afk"
reason = afk_dict[user.id] or ""
embed = discord.Embed(title=f"{user.mention} is AFK", color=0xFF0000, description=reason[:2500])
await message.reply(embed=embed)
It looks like you are missing some pieces in your code. Here is an updated version of the code:
afkdict = {}
#bot.command("afk")
async def afk(ctx, reason=None):
user = ctx.message.author
afkdict[user] = reason
await ctx.send(f"You are now AFK. {'Reason: ' + reason if reason else ''}")
#bot.event
async def on_message(message):
for user, reason in afkdict.items():
if user in message.mentions:
if reason is None:
reason = ""
embed = discord.Embed(title=f"{user} is AFK", color=0xFF0000, description=reason[:2500])
await message.channel.send(embed=embed)
if message.author in afkdict:
afkdict.pop(message.author)
await message.channel.send(f"{message.author} is no longer AFK")
In this code, the afk command will add the user who runs the command to the afkdict dictionary along with the reason for being AFK. The on_message event handler will then check if any of the mentioned users are in the afkdict and if so, it will send an embed with the AFK status and reason. Finally, if the author of the message is in the afkdict, it will remove them from the dictionary and send a message indicating that they are no longer AFK.
I have a function where if you edit something the bot would type out before and after the edited sentence. I also have a command where it google searches question and return the first result from the google search in discord. But when I use these two function/commands together (they are on different files and I was running two files on PyCharm) it would return the result from google but then the function says it's edited when it clearly isn't. The weird thing is it only some times print out the "edited message" and sometime it just doesn't for some unknown reason which is the most confusing part. Also the "edited version" is exactly the same as the "non edited version".
Code for the reacting to edit function
#client.event
async def on_message_edit(before, after):
await before.channel.send(
f'Before: {before.content}\n' f'After: {after.content}'
)
Code to the command function
#bot.command()
async def question(ctx,*, query):
author = ctx.author.mention
await ctx.channel.send(f"Here are the links related to your question {author} !")
async with ctx.typing():
for j in search(query, tld="co.in", num=1, stop=1, pause=1):
await ctx.send(f"\n {j}")
I tried to use the author function, but it just said un unresolved reference.
import discord
from discord.ext import commands
id = id
bot = commands.Bot(command_prefix='!')
#bot.event
async def on_message(message):
if message.author == id:
pass
else:
#bot.event
async def on_message_edit( before, after):
embed = discord.Embed(
timestamp=after.created_at,
description = f"<#!{before.author.id}>**'s message was edited in** <#{before.channel.id}>.",
colour = discord.Colour(0x00FF00)
)
embed.set_author(name=f'{before.author.name}#{before.author.discriminator}', icon_url=before.author.avatar_url)
embed.set_footer(text=f"Author ID:{before.author.id} • Message ID: {before.id}")
embed.add_field(name='Before:', value=before.content + "\u200b", inline=False)
embed.add_field(name="After:", value=after.content + "\u200b", inline=False)
channel = bot.get_channel(channel)
await channel.send(embed=embed)
bot.run('token')
I tried to use this method where if your id is a specific id then it won't return. It still doesn't work, any suggestions as to why?
From the docs:
The following non-exhaustive cases trigger this event:
A message has been pinned or unpinned.
The message content has been changed.
The message has received an embed.
Since this there are cases besides content change that you don't want you'll need to filter them. I'd suggest something like this:
#client.event
async def on_message_edit(before, after):
if before != after:
await before.channel.send(
f'Before: {before.content}\n' f'After: {after.content}'
)
Disclaimer: I didn't test this code and have never used this library. Good luck :)
Update:
You question looks similar to this question
Also, if you'd rather filter on some other attribute, the discord.Message class has various attributes to use. The author attribute looks like one you may wish to use based on your comments.
I need my Discord bot to handle DM's and therefore I wrote this script:
#client.command()
async def dm(ctx, user_id=None, *, args=None):
if user_id != None and args != None:
try:
target = await client.fetch_user(user_id)
await target.send(args)
await ctx.channel.send("'" + args + "' sent to: " + target.name)
except:
await ctx.channel.send("Couldn't dm the given user.")
else:
await ctx.channel.send("You didn't provide a user's id and/or a message.")
My problem now is, until this point in my project I was satisfied by coding if-else-branches into the "on_message()"-function in order to make my bot react to certain commands but now my bot isn't reacting to function calls: When I try to call the "dm"-function (my command prefix is set to ".") it doesn't throw an error in the console it just doesn't work and I'm clueless to what I'm doing wrong here.
Is the code snippet not located in the right place inside my script? What am I missing?
I'd be glad for any form of help. Thanks
As per the docs, you need to add a await client.process_commands(message) at the end of your on_message listener.
https://discordpy.readthedocs.io/en/master/faq.html#why-does-on-message-make-my-commands-stop-working
Edit: Separate question but as I wrote in the comment, here's an example on DM'ing people on a certain time:
from discord.ext import tasks
#tasks.loop(hours=24*7)
async def dm_loop():
user_ids = (123, 456, 789)
for i in user_ids:
user = await client.fetch_user(i)
await user.send("hello")
#client.event
async def on_ready():
dm_loop.start() #remember to add this to your on_ready event
This would make it run on startup and then once a week.
this is my dm code
#client.command()
async def dm(ctx, user_id=None, *, args= None):
if user_id != None and args != None:
try:
target=await client.fetch_user(user_id)
await target.send(args)
await ctx.channel.send("'"+args+"'ได้ถูกส่งไปที่ : " + target.name)
except:
await ctx.channel.send("ไม่สารมารถ dm ได้")
else:
await ctx.channel.send("A user_id / or arguments were not included.")
i wanted to take the response or just like anything that random person dm the bot and display by print('') or ctx.channel.send how do i do that ? please help
I have solve my problem by doing
async def on_message(message):
if message.guild is None and not message.author.bot:
print(f'{message.author } :' + f'ID :{message.author.id} :' + message.content)
await client.process_commands(message)```
Your question is neither well researched, nor explainative to people trying to help you. And unfortunately its unreadable too. You shouldn't expect your question to be answered as noone wants to solve your problem when they have to decode the question by themselves. For future questions, please read How do I ask a good question.
Still, heres your answer:
# Events are not costum commands but specific actions, like on_message
# The on_message event doesnt pass a Context, but only a message object
# For the context, run client.get_context(message)
#client.event
async def on_message(message):
# Message is, like context, a class. This means you can get a variety of information from message,
# like the author name or id
# check that the bot ignores its own messages
if message.author.id != client.user.id:
# Check if the channel is a DM channel
if isinstance(message.channel, discord.channel.DMChannel):
answer = f"{message.author.name}: {message.content}"
# Provide the channelid of the channel you want the message to be sent to.
# You can do that by enabling Developer Mode in Settings -> Appearance
# Then you can right click on any channel or member to copy their id
channelid = 1111 # 18-digit-integer
channel = client.get_channel(channelid)
await channel.send(answer)
how do I get the name of a channel so that this bot will work on any server its put on with no changes to code necessary? ( in the code where I put "what do I put here" is where I want the name to be in a variable)Thanks
from discord.ext.commands import Bot
import time, asyncio
TOKEN = 'Its a secret'
BOT_PREFIX = ["!"]
client = Bot(command_prefix=BOT_PREFIX)
#client.event
async def on_message(message):
if message.author == client.user:
return
#client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
await start()
while True:
currentTime = time.strftime("%M%S", time.gmtime(time.time()))
if currentTime == "30:00":
await start()
await asyncio.sleep(1)
async def start():
mainChannel = #What do i put here?
print(mainChannel.name)
await client.send_message(mainChannel, "Starting countdown", tts = True)
client.run(TOKEN)
Getting channel from ID (Recommended)
First, get the ID of the channel
(Right click the channel and select "Copy ID")
Second, put the ID in the following code:
client.get_channel("ID")
For example:
client.get_channel("182583972662")
Note: The channel ID is a string in discord.py async, and an integer in rewrite
(Thanks to Ari24 for pointing this out)
Getting channel from name (Not reccomended)
First, get the server using either:
server = client.get_server("ID")
OR
for server in client.servers:
if server.name == "Server name":
break
Second, get the channel:
for channel in server.channels:
if channel.name == "Channel name":
break
What not to do
Try to always use the ID for each server, as it is much faster and more efficient.
Try to avoid using discord.utils.get, such as:
discord.utils.get(guild.text_channels, name="Channel name")
Although it does work, it is bad practise as it has to iterate through the entire list of channels. This can be slow and take much more time than using the ID.
From the discord API docs:
discord.utils.get is a helper that returns the first element in the
iterable that meets all the traits passed in attrs
Now in rewrite there's a method called discord.utils.get where you can actually getting discord objects with specific parameters
In your case with a channel name:
import discord
channel = discord.utils.get(guild.text_channels, name="Name of channel")
Should be None if discord couldn't find a textchannel with that name
It is actually really easy:
You can simply do message.channel.name
Example:
print(message.channel.name)
Well, it's simple to do. However your code can be improved by several way. It will be easier to read your code.
to get the channel and send a message to it, use this
ch = client.get_channel(channel id)
await ch.send("message")
few optimizations to your code
from discord.ext import commands
import discord
import time
TOKEN = "token"
client = commands.Client(command_prefix="!")
#client.event
async def on_ready():
print(f"Logged in as {client.user}")
print(f"ID: {client.id}")