so, im trying to make a multiple status for my discord bot, but i cant find anything.
also im trying to make the status show how many times two commands have been used globally
this is what ive tried for multiple status
async def change_playing():
threading.Timer(10, change_playing).start()
await test_bot.change_presence(game=discord.Game(name='Currently on ' + str(len(client.servers)) +
' servers'))
threading.Timer(10, change_playing).start()
await test_bot.change_presence(game=discord.Game(name='Say shelp'))```
i found it, but thanks for the help
guilds = len(client.guilds)
users = len(client.users)
await client.wait_until_ready()
statuses = ["Shelp", f"im in {guilds} guilds| watching {users} users", "with knives"]
while True:
game = discord.Game(random.choice(statuses))
await client.change_presence(status=discord.Status, activity=game)
await asyncio.sleep(10)
client.loop.create_task(roll_presence())```
You could add this to your bot with the discord.ext.tasks decorator that was made for the very purpose of repeating an action every n seconds.
The docs for it are here.
You could do something like
import discord
from discord.ext import tasks
SAYHELP = discord.Game(name="Say shelp")
SERVER_COUNT = discord.Game(name=f'Currently on {len(client.servers)} servers'))
#tasks.loop(seconds=10)
async def change_playing():
if test_bot.activity is SAYHELP:
test_bot.change_presence(game=SERVER_COUNT)
else
test_bot.change_presence(game=SAYHELP)
async def on_ready(self, *args, **kwargs):
change_playing.start()
super().__on_ready__(*args, **kwargs)
Just don't forget to update the SERVER_COUNT activity whenever the bot gets added to another server. There's this event that gets fired then.
As for the command usage counter, there's a couple approaches to this that depend entirely on how you're instantiating your bot.
If you're just using the discord.ext.commands.Bot class as-is, you'll want to define an integer for that in your top-level code and increment that every time the relevant command is called.
If you're subclassing the bot, however, then you can define self.commandcounter in your bot and have the commands update that instead.
If you're also using cogs, then make sure the cog has a reference back to the bot so you can increment self.bot.commandcounter.
You could then have the #task decorated function set that as presence as well. Might need to add another check for the current activity though.
The docs for dpy are a good place to start looking if you can't get something to work.
Related
New to coding. I want to prevent my bot from responding to itself/other bots, and I'm trying to invoke a cog_check but can't make it work properly. Help would be greatly appreciated.
class CommandsMisc(commands.Cog):
def __init__(self, client):
self.client = client
async def cog_check(self, ctx):
if ctx.author.bot: # also tried: if ctx.author == self.client.user:
await exit()
#commands.Cog.listener("on_message") # example cog command
async def print(self, message):
if '!guide' in message.content:
await message.reply(Guide_Text)
else:
pass
Since some people seem to lack reading comprehension skills, my question is how can I best utilize "cog_check" in Discord.py so that all subsequent commands/listeners in the cog will check whether the message.author is the bot and then won't execute if it is?
It seems like what you want is a custom command decorator. You can do this as follows:
def is_not_bot():
async def predicate(ctx):
return not ctx.author.bot
return commands.check(predicate)
You place this in your cog class, then on any command inside the cog you can use the decorator #is_not_bot()
What does this do, exactly? Well, we create a function which can be used as a command decorator (#is_not_bot()). Inside this, we create a function called predicate. This predicate will return True if the user is not a bot. We then call return commands.check(predicate) inside the parent function. This will allow us to use this as a decorator.
discord.py docs reference
what you can have is a on_message event, which can do checks on the message before processing the command, something like this
bot = commands.Bot(command_prefix="!", intents=discord.Intents.all())
# or if you made your subclass of Bot or its variants
class CustomBot(commands.Bot):
def __init__(self):
...
bot = CustomBot()
#bot.event
async def on_message(message):
if message.author.bot:
return # this is a bot, so we can early exit
# any other checks
await bot.process_commands(message)
exit() will just make your entire program close. cog_check should return a Boolean that indicates if the command should run or not. In your case, you don't return anything if it's okay, so it returns None which is falsey, so your check always fails.
Make it return True if it should succeed, and False if it should fail.
Edit
The ext.commands framework already prevents bots from running your commands, so this is not something you should even have to worry about.
The reason your cog_check still doesn't do anything is because you don't have any commands. You're using an on_message listener to manually parse messages, instead of creating commands. Your "example cog command" is not a command, it's just an event listener. Those are two very different concepts.
cog_check is only invoked when actual commands are run, not before dispatching events like on_message, so this doesn't have any effect here. If you parse commands in on_message, checks aren't used at all.
See the docs for ext.commands for how to create commands: https://discordpy.readthedocs.io/en/stable/ext/commands/index.html
I am creating a Discord bot and am not experiencing any success creating custom commands with a command_prefix=‘!’.
E.g. I want to create, using the !-sign as the prefix, multiple commands that can even accept other arguments if you know exactly what you want within that command. Something like this:
!creeds
-or if you know what creed, you could use “!creeds Nicene” or “!creeds Athanasian” or if you want a list just use the base command “!creeds” and it will provide a list.
I have been told to use cogs and have not had any success with those, yet. Though I know I will need a database (using SQLite3) which I am also working on.
#jh316, all I have is this so far:
#client.event #basic event using a !command.
async def on_message(msg):
if msg.author != client.user:
if msg.content.lower().startswith("!hi"):
await msg.channel.send(f"Hi, {msg.author.display_name}!")
Something like this? https://onecompiler.com/python/3yc5xaqce
def purify(string):
return string.lower().strip()
def handleMessage(msg):
prefix = "!"
delimiter = ":"
if msg[0:1] != "!":
print("This is not a command")
#exit
splitMessage = msg[1:len(msg)].split(":")
cmd = purify(splitMessage[0])
param = "" if len(splitMessage) <= 1 else purify(splitMessage[1])
response = executeCommand(cmd, param)
#Now use the response
print(response)
def executeCommand(command, parameter):
#Actually use command here...
response = "Sorry, we don't have that one!"
if command == "creed":
if parameter == "nicene":
response = "We believe in one God, the Father almighty, maker of heaven and earth, of all things visible and invisible..."
elif parameter == "apostles":
response = "I believe in God, the Father almighty, creator of heaven and earth."
return response
#Examples
handleMessage("!creed: Nicene")
handleMessage("!creed: Apostles")
handleMessage("!creed: Athanasian")
Obviously you would want to remove the examples and put a single handleMessage reference in your discord client event message handler. Does this help?
Anyway, I love the idea!
Are you using a Client or a Bot? You should be importing from discord.ext.commands import Bot. The Bot class has a decorator to create commands:
from discord import Intents
from discord.ext.commands import Bot
TOKEN = "your secret bot token from the Discord developer page"
bot = Bot(command_prefix="!", intents=Intents.all())
#bot.command()
async def settings(context):
await context.reply("You executed command `settings`!")
#bot.command()
async def hello(context):
await context.reply("You executed command `hello`!")
bot.run(TOKEN)
Now your users will be able to type !settings and !hello in Discord to trigger these commands.
Be careful though, because if you define your own on_message like you did in your code, these commands will not automatically be called. You have to invoke them yourself:
#bot.event
async def on_message(message):
context = await bot.get_context(message)
await bot.invoke(context)
There is plenty of information online about how these commands work and how you can pass in arguments and cast them to the correct type automatically. This tutorial is one of the first Google Search results when I search "discord.py commands", and it looks like a decent tutorial that you could read.
I created a discord bot in python that runs some other code if a user sends it a message. The problem I'm having is that while the code is being run, the bot will not respond to any other messages. How can I make it so that while the bot is running code behind the scenes it can still respond to other messages?
Code looks like this:
from secrets import TOKEN
import discord
def code_that_takes_time():
items = []
for x in range(10):
item = do_something() #get request in my case. takes 20s per iteration
items.append(item)
return items
#client.event
async def on_message(message):
user_message = str(message.content)
send = message.channel.send
if message.author == client.user:
return
if user_message.startswith('$do'):
result = code_that_takes_time()
send(result)
if user_message.startswith('hello'):
send('hello, nice to meet you')
client.run(TOKEN)
I want it so that the bot can still respond to the 'hello' message while it's running code_that_takes_time and then send the result whenever it finishes.
Thanks in advance for the help and ideas.
anything that has a chance of blocking, such as your code_that_takes_time should ideally be written as a co-routine so that it will sleep and let other code execute while it is waiting. I'm relatively new to asyncio myself, but I believe the syntax is to use async on your code_that_takes_time, i.e.:
async def code_that_takes_time:
items = []
for x in range(10):
item = do_something() #get request in my case. takes 20s per iteration
items.append(item)
return items
then make sure to call it with the await keyword (if you try to call a coroutine normally, the call just retrieves and returns the coroutine object), e.g.:
#put any needed arguments in the parentheses as with a normal call
await code_that_takes_time()
I wish I could be more help than that, but I'm also a newb with this stuff and don't want to mislead.
I'm trying to write a Discord bot, where if I run a certain command, a part of the program will run, but the other part won't.
For example: In my case, when they type in "?m", they will get a message saying "xy", but then for 90 seconds, they won't be able to call that command again. If they do call it in this 90 second period, another command called "?x" will run.
Can someone help me out?
You can do it using bot.invoke() method:
#bot.command()
async def x(ctx):
pass # whatever here
#bot.command()
async def m(ctx):
if something:
ctx.command = bot.get_command("x")
await bot.invoke(ctx) # invoke a command called x
With Python's time module, you can check how long it's been since the command was last successfully called. time.time() returns the current time (expressed as a number of seconds since January 1st, 1970).
Calling one command from within another can be as simple as calling it with await, like any other coroutine, and passing the same context to it.
import time
from discord.ext.commands import Bot
from tokens import TOKEN
bot = Bot(command_prefix='?')
# Set the initial time as 0 (effectly 1970) so it was definitely long
# ago for the command to now be called.
last_called = 0
#bot.command()
async def hi(ctx):
# Compare the current time to when the command was last successfully
# called.
global last_called
now = time.time()
if now - last_called <= 90:
await not_now(ctx)
else:
last_called = now
await ctx.send('Hello')
#bot.command()
async def not_now(ctx):
await ctx.send('Please wait a minute.')
bot.run(TOKEN)
Using global variables is not generally the best idea, but unfortunately subclassing Bot for tidier encapsulation of data into a single object is quite clunky.
I am making a bot and want it to ping a user every ten minutes. Here is what I have right now
#client.event
while True:
channel = client.get_channel(717346417592762457)
await message.channel.send(<#!561464966427705374>)
time.sleep(600)
But this results in a lot of errors.
One easy way to do it would be to use discord's tasks extension. You can easily set the amount of time for each loop. The only main drawback is that the loop restarts every time your bot restarts, or rather everytime you run the command, in this example. In the example below, I added the method to start the loop in a command, but you can have it start in an on_ready event or similar.
# New Import
from discord.ext import tasks, commands
# ... Other code ...
#tasks.loop(minutes=10) # #tasks.loop(seconds=600) <-- does the same amount of time
async def ping_loop():
channel = client.get_channel(717346417592762457)
await channel.send("<#!561464966427705374>")
# 1. Don't use message.channel, as you only defined channel and this isn't an event
# 2. As mentioned by Tim Roberts in the comments
# add speech marks to your send() since it's a string
# somewhere else in your code
#client.command()
async def loop(ctx):
try:
ping_loop.start() # start the loop if it isn't currently running
await ctx.send("Started pinging <#!561464966427705374>")
except: # happens if the loop is already running
ping_loop.cancel() # if so, cancel the loop
await ctx.send("Stopped pinging <#!561464966427705374>")
For the specific errors you mentioned:
#client.event
async def somefunction(ctx):
# you need to define a function to call (pass to discord.py)
# also the function has to be async, for the usage of `await`
while True:
channel = client.get_channel(717346417592762457)
await message.channel.send(<#!561464966427705374>)
# not sure if you can mention user like this
await asyncio.sleep(600)
# time.sleep(600) # prefer non-blocking asyncio sleep. make sure `asyncio` is imported
To mention a user, you need to first get user with
user_instance = ctx.guild.get_member(YOUR_USER_discord_id)
Note: You may need to enable the privilege to use the .get_member() function. If you have not enabled it yet, just follow the instructions in the exception.
Then you can sent message with
await ctx.send(f"THE_TEXT_YOU_WANT_TO_SPAM {user_instance.mention}!")
Also, if you are using it in a bot command like me, you may want to add some validation, so that not everyone can cast/invoke the command. I did this with has_role():
#commands.has_role(YOUR_ROLE_ID_THAT_CAN_CAST_THIS_COMMAND)
So in the end, it looks like this:
#bot.command('name': 'spam', 'help':'use at # to mention a user')
#commands.has_role(YOUR_ROLE_ID_THAT_CAN_CAST_THIS_COMMAND)
async def spamMention(ctx, spam_duration_second: int = 3):
user_instance = ctx.guild.get_member(YOUR_USER_discord_id)
for cntr in range(spam_duration_second):
await ctx.send(f"THE_TEXT_YOU_WANT_TO_SPAM {user_instance.mention}!")
await asyncio.sleep(THE_INTERVAL_YOU_WANT_IN_SECOND)
return
It's part of my code so I know it works (my python version: 3.7.9).