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.
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 made recently a discord bot for small server with friends. It is designed to answer when mentioned, depending on user asking. But the problem is, when someone mention bot from a phone app, bot is just not responding. What could be the problem?
Code:
import discord
from discord.ext import commands
from discord.ext.commands import Bot
import asyncio
bot = commands.Bot(command_prefix = '=')
reaction = "🤡"
#bot.event
async def on_ready():
print('Bot is ready.')
#bot.listen()
async def on_message(message):
if str(message.author) in ["USER#ID"]:
await message.add_reaction(emoji=reaction)
#bot.listen()
async def on_message(message):
mention = f'<#!{BOT-DEV-ID}>'
if mention in message.content:
if str(message.author) in ["user1#id"]:
await message.channel.send("Answer1")
else:
await message.channel.send("Answer2")
bot.run("TOKEN")
One thing to keep in mind is that if you have multiple functions with the same name, it will only ever call on the last one. In your case, you have two on_message functions. The use of listeners is right, you just need to tell it what to listen for, and call the function something else. As your code is now, it would never add "🤡" since that function is defined first and overwritten when bot reaches the 2nd on_message function.
The message object contains a lot of information that we can use. Link to docs
message.mentions gives a list of all users that have been mentioned in the message.
#bot.listen("on_message") # Call the function something else, but make it listen to "on_message" events
async def function1(message):
reaction = "🤡"
if str(message.author.id) in ["user_id"]:
await message.add_reaction(emoji=reaction)
#bot.listen("on_message")
async def function2(message):
if bot.user in message.mentions: # Checks if bot is in list of mentioned users
if str(message.author.id) in ["user_id"]: # message.author != message.author.id
await message.channel.send("Answer1")
else:
await message.channel.send("Answer2")
If you don't want the bot to react if multiple users are mentioned, you can add this first:
if len(message.mentions)==1:
A good tip during debugging is to use print() So that you can see in the terminal what your bot is actually working with.
if you print(message.author) you will see username#discriminator, not user_id
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)
I recently had the idea of DMing my bot commands. For example, a command which would unban me from every server that the bot is on.
Unfortunately I don't have any starting point for the command because I'm not even sure DMing commands is possible.
Keywords like discord.py, command or DM are so common in google that finding any good info on the topic is very hard.
I'm looking for a way for the bot to receive DMs as commands and only accept them from me (my ID is stored in the variable ownerID if anyone wants to share any code).
While I'm mostly looking for the above, some code for the DM unban command would also be very helpful.
EDIT: I was asked to show some example code from my bot. Here is the code for the command number which generates a random number and sends it in a message. I hope this gives you an idea of how my bot is made:
#BSL.command(pass_context = True)
async def number(ctx, minInt, maxInt):
if ctx.message.author.server_permissions.send_messages or ctx.message.author.id == ownerID:
maxInt = int(maxInt)
minInt = int(minInt)
randomInt = random.randint(minInt, maxInt)
randomInt = str(randomInt)
await BSL.send_message(ctx.message.channel, 'Your random number is: ' + randomInt)
else:
await BSL.send_message(ctx.message.channel, 'Sorry, you do not have the permissions to do that #{}!'.format(ctx.message.author))
You can send commands in private messages. Something like
#BSL.command(pass_context=True)
async def unban(ctx):
if ctx.message.channel.is_private and ctx.message.author.id == ownerID:
owner = await BSL.get_user_info(ownerID)
await BSL.say('Ok {}, unbanning you.'.format(owner.name))
for server in BSL.servers:
try:
await BSL.unban(server, owner) # I think having the same name should be fine. If you see weird errors this may be why.
await BSL.say('Unbanned from {}'.format(server.name))
except discord.HTTPException as e:
await BSL.say('Unbanning failed in {} because\n{}'.format(server.name, e.text))
except discord.Forbidden:
await BSL.say('Forbidden to unban in {}'.format(server.name))
else:
if ctx.message.author.id != ownerID:
await BSL.say('You are not my owner')
if not ctx.message.channel.is_private:
await BSL.say('This is a public channel')
should work. I'm not sure what happens if you try to unban a user who is not banned, that may be what raises HTTPException
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.