On *any* event discord.py - discord.py

I want to run a certain block of code for every event. I figured that it would probably be easiest if there was something like on_any_event. However, I can't seem to find anything in the docs or on the web. Does anyone know if there is a way to do this, and if so, how?
Other Info:
discord.py-rewrite
Thanks in advance.

I see two possibilities:
Most of the events in discord.py, afaik, is "handlers" for sockets responses.
You can try to use on_socket_response(message) event. This should be enough for all websocket-based events.
If you need literally any event, you can try overwrite dispatch function in child class, and use this class as your's bot's class.
In example:
from discord.ext import commands
class MyBot(commands.Bot):
def dispatch(self, event_name, *args, **kwargs):
super().dispatch("event", event_name)
super().dispatch(event_name, *args, **kwargs)
bot = MyBot(command_prefix="!")
This will dispatch additional event on any event
#bot.event
async def on_event(event_name):
print(f"{event_name} is dispatched")

Related

Discord.PY Custom Commands - can you define multiple commands/functions in one file?

Silly question, but in Discord PY are you able to define multiple custom commands or functions in one file? I am working on a few custom commands and just noticed that only the one defined first works, the others say command not found. They all work individually, just not when there are multiple commands defined. Is this format just wrong? Not getting any syntax errors and again it was just working until I added the Add/Remove Roles commands, or even just a Test command that does nothing..
Thanks!
My code looks something like this:
imports
define HelperFunctions:
APIInfo, etc
#start bot
bot = commands.Bot(command_prefix='$', intents=intents)
#bot.command()
#define bot commands
async def TestCommand(params):
do stuff
async def AddRoles(params):
do stuff
async def RemoveRoles(params):
do stuff
async def AddToList(params):
do stuff
bot.run()
bot.command() decorator should be used separately for every command.
https://github.com/Rapptz/discord.py/blob/master/examples/basic_bot.py
#define bot commands
#bot.command()
async def TestCommand(params):
do stuff
#bot.command()
async def AddRoles(params):
do stuff
#bot.command()
async def RemoveRoles(params):
do stuff
#bot.command()
async def AddToList(params):
do stuff

I am trying to make my bot be able to respond to a command that starts with a certain set of characters instead of having an exact command

I am trying to make my bot be able to respond to a command that starts with a certain set of characters instead of having an exact command. I figured out how to use it with client.event but I can't figure out how to do it using client.command.
As an example let's say I want it to send a message that says "Hello!" when the command ".hi" is sent. I want it to work even if a user sends ".hii" or maybe ".hi!". I can get this to work by using the startswith command. Below is my working client.event code and my broken client.command code:
client.event code:
import discord
client = discord.Client()
#client.event
async def on_message(message):
if message.content.lower().startswith('.hi'):
await message.channel.send('Hello!')
client.command code:
import discord
from discord.ext import commands
client = commands.Bot(command_prefix = '.', case_insensitive=True)
#client.command()
async def hi(message):
if message.content.startswith('hi'):
print('Hello!')
If you are using the command, you could specify the aliases of the command like this:
#client.command(aliases=['hii', 'hi!'])
async def hi(ctx):
print('Hello!')
https://discordpy.readthedocs.io/en/latest/ext/commands/api.html?highlight=aliases#discord.ext.commands.Command.aliases
Also your command is broken because you need to pass in ctx as a parameter for a command, not message.
Your #client.command() won't work because you never passed in ctx, by the way, to send a message you might want to use await ctx.send("Hello!").
The "full command" would look something like this, though its not really what you want as you would have to type .hi hi
#client.command()
async def hi(ctx, message):
if message.startswith('hi'):
await ctx.send('Hello!')
If you actually want to do that you should try using your #client.event (with on_message, basically the one you already have) as I dont think that you can do that unless you use aliases (Which is not a good option though):
#client.command(aliases=['hi!', 'hiiiiii', 'hello'])
async def hi(ctx):
await ctx.send('Hello!')
In conclusion, if you really want to do something like this, use your exsistent #client.event with on_message
(Just as #moinierer3000 said)

How can I make it so only a certain role can use a command with discord.py?

I'm attempting to make a command that only works with a certain role. I'm not 100% sure how to do this and I cannot find a solution anywhere else. My code stops short because I'm not very familiar with coding quite yet, but here it is:
#bot.command()
async def sign(ctx, member: discordMember):
if ctx.author.server_roles
From here I am completely lost and have no idea what to do.
The most efficient way to make it so a command can be used only with a certain role is the .has_role() decorator. You may put there a string with the role name (case sensitive) or the role ID (recommended), more info can be found in the documentation Here is an example:
#bot.command()
#commands.has_role("Administrator")
async def foo(ctx)
await ctx.send("bar")
If you would like to make it so the user can use this command only when he has any role then .has_any_role() would be the way to go, it takes strings or integers as well. You can find more info on it here. Here is a quick example on how that would work:
#bot.command()
#commands.has_any_role("Administrators", "Moderators", 492212595072434186)
async def foo(ctx):
await ctx.send("bar")
Happy Coding!
Usually when someone tries to execute such command with a .has_role decorator the discord.ext.commands.MissingRole is raised, so handling it would be something like this :
#bot.command()
#commands.has_any_role("Administrators", "Moderators", 492212595072434186)
async def foo(ctx):
try:
await ctx.send("bar")
except commands.MissingRole:
await ctx.send("lol")
Of course if you have a lot of commands, then I would recommend using a global error handler.

Use asyncio with neovim remote plugins

I want to write a vim plugin that listens to Server-sent events. Since I am most fluent with python3 and use neovim, I figured it would be a good idea to use the neovim remote plugin API.
Obviously, listening for messages from the network must not be blocking, so asyncio must be involved somehow. But I was not able to figure out how to combine the two. Somewhere I would have to run an event loop. However, pynvim already runs its own event loop, so I probably should hook into that.
#pynvim.plugin
class MyPlugin:
def __init__(self, nvim):
self.nvim = nvim
#pynvim.command('Connect', nargs='1')
async def connect(self, args):
url = base_url + args[0]
async with sse_client.EventSource(url) as event_source:
for raw in event_source:
try:
msg = json.loads(raw)
except json.JSONDecodeError:
continue
do_something(msg)
This example doesn't work. The Connect command is not available in neovim (it was before I made it async).
Not sure if this is the best answer, but this is what I have found to work:
asyncio seems to keep a reference to the current loop, so asyncio.ensure_future() can be used to schedule async code. However, that async code will crash if it tries to access vim internals. In order to do that, you need to call yet another callback with nvim.async_call().
#pynvim.plugin
class MyPlugin:
def __init__(self, nvim):
self.nvim = nvim
async def _connect(self, url):
async with sse_client.EventSource(url) as event_source:
for raw in event_source:
try:
msg = json.loads(raw)
except json.JSONDecodeError:
continue
self.nvim.async_call(do_something, msg)
#pynvim.command('Connect', nargs='1')
def connect(self, args):
url = base_url + args[0]
asyncio.ensure_future(self._connect(url))

Bot wont properly print member_count

So I'm new to Python and I decided to take the plunge and make a Discord bot for personal use in my server. I like the idea of having full control over what features my bot will have so I'm slowly building the bot. Currently I want my bot to show the current number of members in the server when called with a command
import discord
from discord.ext import commands
#botbot.command()
async def server(ctx):
guild = discord.Guild.member_count
await ctx.send(guild)
I know I'm most likely way off with my code here.
While the bot is sending a message into the chat, its formatted as:
<property object at 0x046036C0>
While I would like to have it say something like "This server has {some number} members."
Any advice is really appreciated!
Thank you for your time.
Edit: botbot is the name for my bot, just so thats clear.
discord.Guild is the class. You want to get the guild the command is in from the invocation context:
#botbot.command()
async def server(ctx):
await ctx.send(f"This server has {ctx.guild.member_count} members.")
The method member_count is not really what you want in this occasion, the guild object has a list called members, so it's as easy as getting the length of that list like so:
#botbot.command()
async def server(ctx):
guild = len(discord.guild.members)
await ctx.send(guild)
EDIT: And indeed you have a typo with using "Guild" instead of "guild"

Resources