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)
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 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.
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.
So currently I am trying to create a blacklist (basically a warn command) command where server owners can use the command to publish bans they dealt with. I tried to rewrite my original warn command but the problem with that is the output isn't how I wanted it to be and if I did want to fix it, I had to rewrite a majority of the code which is why I wanted to make a new fresh command for it.
So the problem I am facing with making the command is taking in multiple arguments.
Eg: (Process of blacklisting)
User: >blacklist (user/id)
BOT: Gamertag?
User: *Sends an Xbox username*
BOT: Banned from?
User: *Sends server name*
BOT: Reason for the ban?
User: *Sends ban reason*
This is an example of the conversation the bot and user would be going through. The main thing is being able to save the user's responses and send everything as an embed.
I currently have this:
#commands.command()
async def blacklist(self, ctx):
embed = discord.Embed(title = "Blacklist Report")
#Then something here to collect responses
Note: I can create the embed just fine but just need help collecting responses from the user. Any help would be amazing!
I decided to send you 2 functions I always use to help you out
def check_message(author: discord.User, channel: discord.TextChannel, forbid: bool = False):
def check(message: discord.Message):
if forbid and message.channel.id == channel.id and message.author.id != author.id and message.author.id != bot.user.id:
embed = discord.Embed(title='**ERROR**', description=f'Only {author.mention} can send message here', colour=discord.Colour.red())
asyncio.ensure_future(message.delete())
asyncio.ensure_future(channel.send(embed=embed, delete_after=20))
return message.author.id == author.id and channel.id == message.channel.id
return check
async def get_answer(ctx: discord.TextChannel, user: discord.User, embed: discord.Embed, timeout: Optional[int] = None, delete_after: bool = False) -> Optional[str]:
message: discord.Message = await ctx.send(embed=embed)
try:
respond: discord.Message = await bot.wait_for('message', check=check_message(user, ctx), timeout=timeout)
if delete_after:
await try_delete(message)
await try_delete(respond)
return respond.content
except asyncio.TimeoutError:
if delete_after:
await try_delete(message)
return None
You don't need to even call check_message, You just need to await the get_answer and get what you want as a string
Here is an example:
#commands.command('example')
async def example(self, ctx):
embed = discord.Embed(title='What is your name?')
response = await get_answer(ctx.channel, ctx.author, embed)
print(f'His name was {response}') # or any other thing you want to use it for
The timeout allows you to close the process after some seconds without response and continue the code, In that case you will get None as the output of this function.
The delete_after allows you to remove both the question message and the response one after timeout or reciving a response to keep chat clear.
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.