how to test event in discord.py - events

lets say on_member_join event
#commands.Cog.listener()
async def on_member_join(self, member):
# On member joins we find a channel called general and if it exists,
# send an embed welcoming them to our guild
channel = discord.utils.get(member.guild.text_channels, name="welcome")
if channel:
embed = discord.Embed(
description="Welcome to our guild!",
color=random.choice(self.bot.color_list),
)
embed.set_thumbnail(url=member.avatar_url)
embed.set_author(name=member.name, icon_url=member.avatar_url)
embed.set_footer(text=member.guild, icon_url=member.guild.icon_url)
embed.timestamp = datetime.datetime.utcnow()
await channel.send(embed=embed)
This is my event (I know it works) how can I test it in any way to execute command to emit the event (not manually by adding and removing someone)?
[something like .emit on_member_join #user where arg1 is event and if needed arg2 is mention or id of channel ]
Any command for that ?
something like Discord.js: Manually triggering events but in discord.py not in JavaScript

Currently there isn't official support in the discord.py api to do something like that but you can always make a command in the cog like follows:
#commands.command()
async def test_join(self, ctx, member: discord.Member):
await self.on_member_join(member)
Technically there is a way to probably do it that isn't documented...
Line 131 in here. I will not offer addition support for that because it isn't documented.

Related

how do i display a message if user puts a non existing sub command?

So I made a sub command that just sends a message back. I found that if the user types a non existing command it still displays the message from the sub command. It sounds confusing but here's an example.
User: ;id
Bot: This command help you find the name of anyone in the server!
User: ;id Slayer
Bot: Bob Miller
So while testing I found if the user sends some thing like ;id jgfjkag the bot still sends the original message for ;id which is "This command help you find the name of anyone in the server!". How would I make the bot send a specific message if the user trys to use a non existing sub command? Here's the code:
#commands.group()
async def id(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send("This command help you find the name of anyone in the server! ")
#id.command()
async def Slayer(self, ctx):
await ctx.send("Bob Miller")
#id.command()
async def Skel(self, ctx):
await ctx.send("John Dove")
Check ctx.subcommand_passed first:
#commands.group()
async def id(self, ctx):
if ctx.subcommand_passed is None:
await ctx.send("This command help you find the name of anyone in the server!")
elif ctx.invoked_subcommand is None:
await ctx.send(f"Subcommand '{ctx.subcommand_passed}' does not exist.")
This is my favorite method of doing it:
#commands.group(invoke_without_command=True)
async def group(self, ctx):
await ctx.send_help(ctx.command) # invokes the help command for the group
#group.command()
async def subcommand(self, ctx):
await ctx.send("You passed a valid subcommand!")
The invoke_without_command parameter, when set to true, will only call the command if there are no (valid) subcommands passed. This means that if the code inside if the group() function is called, you can safely assume that they didn't pass a subcommand (or passed an invalid one).
I also like to use ctx.send_help, since that will automatically list subcommands. You could replace this with something like:
await ctx.send("You didn't pass a valid subcommand!")
[EDIT]
More careful reading of your question revealed that the subcommand already has some functionality. This makes it more difficult, but this will work:
#commands.group(invoke_without_command=True, ignore_extra=False)
async def group(self, ctx):
await ctx.send("This is a group command!")
#group.error
async def on_group_error(self, ctx, error):
if isinstance(error, commands.TooManyArguments):
await ctx.send("You passed an invalid subcommand!")
#group.command()
async def subcommand(self, ctx):
await ctx.send("You passed a VALID subcommand!")
Here is what it would look like:
!group
Bot: This is a group command!
!group bla
Bot: You passed an invalid subcommand!
!group subcommand
Bot: You passed a VALID subcommand!
NOTE: The ignore_extra field raises the commands.TooManyArguments error, which then invokes the error handler allowing the bot to send the error message. Unfortunately, the default on_command_error hook will still be called. I would suggest ignoring the commands.TooManyArguments error inside of your main error handler to fix this.

Discord dm bot that can dm back

I am trying to make a donation bot, where in the channel when a person types !donate the bot will Dm them.
I got all that down but I am trying to make so that the person decides on how to donate like !cashapp, !paypal, etc., in the dms. It would send them that specific way to send money, for example once the bot Dms the use user what service they would like to pay with and the user says !cashapp it will send another message with my cashtag, or if they say !paypal it would send my paypal link.
Here's a simple example:
import discord
from discord.ext import commands
client = commands.Bot(command_prefix = "!")
#client.command()
async def donate(ctx, paymentMethod: str):
if (paymentMethod.lower() == "paypal"):
await ctx.send("PayPal link: ...")
elif (paymentMethod.lower() == "cashapp"):
await ctx.send("Cashapp Link: ...")
else:
await ctx.send("The provided payment method is not available.")
#donate.error
async def donate_error(ctx, error):
if isinstance(error, commands.errors.MissingRequiredArgument):
await ctx.send("Incorrect use of command!")
client.run("your bot token here")
Usage:
!donate {payment method} - Without the '{}'
Check the documentation for more information.
You can use user.send to send a message to the user dm and use it in the same way as ctx.send and channel.send.
await ctx.author.send("paypal link: ...") # ctx.author is the member that call the command
And 2 command will have different function so you just need to write both of them in a different way.
If you planning to make it into 1 command you can use if else check
#client.command(pass_context=True)
async def steam(ctx):
if ctx.author.send():
await ctx.send("you not boster")
else:
await ctx.author.send(random.choice(list(open('steam.txt'))))
i want if someone send a dm
its not Returns a dm
someone know how to do this?

get channel name and send a message over at that channel

So I am working on a little project here, and pretty much, I want to have one of those "Please type the name of a channel in this server" feature.
So pretty much, the bot asks for a channel name, and I put in for example "#changelog" - and then it will ask for what it should write in that channel, etc etc.
So need to get the channel id (I am guessing), but I don't want users to write the ID, instead only writing the #server-name. And then whenever I have done that, the bot shall write in that channel.
Here is my current code!
class Changelog(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_ready(self):
print('Changelog is loaded')
#commands.command()
async def clhook(self, ctx):
await ctx.send('Write text-channel: ')
text_channel = await self.client.wait_for("message", check=lambda message: message.author == ctx.author, timeout=300)
clhook = self.client.get_channel(text_channel)
def setup(client):
client.add_cog(Changelog(client))
Edit:
The channel ID shall be saved "forever", meaning that I do not have to re-write the channel name where the message should go!
You can use discord.utils.get() with this example:
text_channel = await self.client.wait_for("message", check=lambda message: message.author == ctx.author, timeout=300)
channel = discord.utils.get(ctx.guild.text_channels, name=text_channel)
await channel.send('Bla Bla')
So when you type (prefix)clhook then only the channel name, for example general, it will send Bla Bla to the channel named general .
There is another way to do this and I think it's simple than the first option, here it is:
#commands.command()
async def clhook(self, ctx, channel: discord.TextChannel):
await channel.send('Bla Bla')
So in this command, usage is changed. You can use that with this: (prefix)clhook #general(mention the channel). I suggest this solution and I think it's more useful.
You can use message.channel_mentions. This will return a list of all channels that were mentioned using the #channel-name notation. That way, you can just use channel.id to get the id of the channel they mentioned.
Don't forget, however, to check if the user did in fact tag a channel (which you can also put in your check). I put it in a separate function to make it a bit more readable for the sake of this reply, but you can fit that in your lambda if you really want to.
Also, make sure to check if it's a Text Channel and not a Voice Channel or Category Channel.
#commands.command()
async def clhook(self, ctx):
def check(self, message):
author_ok = message.author == ctx.author # Sent by the same author
mentioned_channel = len(message.channel_mentions) == 1 and isinstance(message.channel_mentions[0], discord.TextChannel)
return author_ok and mentioned_channel
await ctx.send("Write text-channel: ")
text_channel = await self.client.wait_for("message", check=check)
chlhook = text_channel.channel_mentions[0]
I put two conditions on the mentioned_channel line, because if the first one fails, the second one could cause an IndexError. Alternatively you can also use an if-statement to return sooner at that place to solve the same issue.

Discord.py move_to

I'd like to make a dc bot, and I want to kick members from voice channel by mention the user
#client.command()
async def kick(ctx):
victim = ctx.message.mentions[0]
await move_to(victim,none)
I use this code, but it don't work, it says "'Bot' object has no attribute 'move_members'"
How should I solve this?
You can eject a member from voice by editing their vocie_channel to None. I am also using a converter to get the member:
#client.command()
async def kick(ctx, member: discord.Member):
await member.edit(voice_channel=None)

allocating bot to single channel discord.py

I want to ensure my bot only responds to commands/messages and responds only in 1 specific channel is this possible I have tried multiple variations to no success. even better if I could define it for any event. Anyone got any ideas?
You can check message.channel in the on_message event and if it matches your criteria, in this case a specific channel, then do process_commands.
Below is an example where the !ping command will only work when channel.name is "general".
from discord.ext import commands
client = commands.Bot(command_prefix='!')
#client.command()
async def ping():
await client.say('Pong')
#client.event
async def on_message(message):
if message.channel.name == 'general':
await client.process_commands(message)
client.run('token')

Resources