When I create a button and handle the callback or send a message and await a reaction in discord with my python bot, this seems to be limited in time. Sometimes after ~ 1hour, the bot doesn't register reactions anymore. For sure once I restart the bot, the connection is lost and it won't register the interaction anymore.
However, I have seen bots in discord that always react to a button, no matter how long ago that button was created. Is there a way to achieve this? Do I have to periodically "reconnect" the bot to the buttons it created?
Simple example:
class ButtonView(disnake.ui.View):
def __init__(self):
super().__init__(timeout=None)
#disnake.ui.button(label="Hi", style=ButtonStyle.red)
async def first_button(
self, button: disnake.ui.Button, interaction: disnake.MessageInteraction
):
await interaction.response.send_message("Button clicked.")
class Test(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
#commands.slash_command()
async def test(self, inter):
await inter.send("Button!", view=ButtonView())
-> In this example the bot won't react to the button click anymore after some time has passed or I restarted the bot.
You can do like this :
import disnake
from disnake.ext import commands
# Define a simple View that persists between bot restarts
# In order a view to persist between restarts it needs to meet the following conditions:
# 1) The timeout of the View has to be set to None
# 2) Every item in the View has to have a custom_id set
# It is recommended that the custom_id be sufficiently unique to
# prevent conflicts with other buttons the bot sends.
# For this example the custom_id is prefixed with the name of the bot.
# Note that custom_ids can only be up to 100 characters long.
class PersistentView(disnake.ui.View):
def __init__(self):
super().__init__(timeout=None)
#disnake.ui.button(
label="Green", style=disnake.ButtonStyle.green, custom_id="persistent_view:green"
)
async def green(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):
await interaction.response.send_message("This is green.", ephemeral=True)
#disnake.ui.button(label="Red", style=disnake.ButtonStyle.red, custom_id="persistent_view:red")
async def red(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):
await interaction.response.send_message("This is red.", ephemeral=True)
#disnake.ui.button(
label="Grey", style=disnake.ButtonStyle.grey, custom_id="persistent_view:grey"
)
async def grey(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):
await interaction.response.send_message("This is grey.", ephemeral=True)
class PersistentViewBot(commands.Bot):
def __init__(self):
super().__init__(command_prefix=commands.when_mentioned)
self.persistent_views_added = False
async def on_ready(self):
if not self.persistent_views_added:
# Register the persistent view for listening here.
# Note that this does not send the view to any message.
# In order to do this you need to first send a message with the View, which is shown below.
# If you have the message_id you can also pass it as a keyword argument, but for this example
# we don't have one.
self.add_view(PersistentView())
self.persistent_views_added = True
print(f"Logged in as {self.user} (ID: {self.user.id})")
print("------")
bot = PersistentViewBot()
#bot.command()
#commands.is_owner()
async def prepare(ctx: commands.Context):
"""Starts a persistent view."""
# In order for a persistent view to be listened to, it needs to be sent to an actual message.
# Call this method once just to store it somewhere.
# In a more complicated program you might fetch the message_id from a database for use later.
# However this is outside of the scope of this simple example.
await ctx.send("What's your favourite colour?", view=PersistentView())
bot.run("token")
This code comes from disnake repository
I think it's most likely that your internet has a connection issue, and that you're disconnecting without noticing.
That was happening to me, so I added on_disconnect and on_resumed bot events that were just simple print statements so that I would be able to check on if that was the source of the issue.
bot: commands.Bot = commands.Bot(command_prefix='.', intents=intents)
bot.time = time.time()
#bot.event
async def on_ready():
print(f"Logged in as {bot.user} (ID: {bot.user.id})")
print('TesterBot is ready starting at ' + time.ctime(bot.time))
#bot.event
async def on_disconnect():
uptimedelta = time.time() - bot.time
print('TesterBot is disconnected at ' + time.ctime(bot.time) + '. Testerbot has been up for ' + str(datetime.timedelta(seconds=uptimedelta)))
#bot.event
async def on_resumed():
uptimedelta = time.time() - bot.time
print("TesterBot reconnected " + time.ctime(bot.time) + '. Testerbot has been up for ' + str(datetime.timedelta(seconds=uptimedelta)))
#bot.event
async def on_connect():
print('TesterBot is connected starting at ' + time.ctime(time.time()))
Just basic stuff like that helped reveal that the problem was that my machine was dropping the connection. It was a physical issue with my internet, not a coding error or misunderstanding of the api or library.
await inter.response.defer(enter image description here)
Related
I would like to create a bot which can delete number of recently chat and history chat
import discord
import random
class MyClient(discord.Client):
async def on_ready(self):
print('Logged on as {0}!'.format(self.user))
async def on_message(self, message):
channel = message.channel.name
restricted_channels = ["command-bot"]
prefix = "-" # Replace with your prefix
# If the message starts with the prefix
if message.content.startswith(prefix):
if channel in restricted_channels:
command = message.content[len(prefix):]
if command.startswith("clear"):
await message.delete()
I have try this
if command.startswith("clear"):
await message.delete()
But it only delete the chat which have command "clear"
First off, personally, I would change the structure/ layout of your code. This is so that it is easier to read, and easier to change and add commands/ different functions to. This is the way I have my bot setup and how I have seen many other bots setup as well:
import discord
client = commands.Bot(command_prefix='your prefix', intents=discord.Intents.all()) # creates client
#client.event # used commonly for different events such as on_ready, on_command_error, etc...
async def on_ready():
print('your bot is online') # basic on_ready event and print statement so you can see when your bot has gone online
Now that we've gone through that part, let's get onto the purge/ clear command you're trying to make. I have one in my bots which generally looks something like this:
#client.command() # from our client declaration earlier on at the start
#commands.has_permissions(moderate_members=True) # for if you want only certain server members to be able to clear messages. You can delete this if you'd like
async def purge(ctx, amount=2): # ctx is context. This is declared so that it will delete messages in the channel which it is used in. The default amount if none is specified in the command is set to 2 so that it will delete the command call message and the message before it
await ctx.channel.purge(limit=amount) # ctx.channel.purge is used to clear/ delete the amount of messages the user requests to be cleared/ deleted within a specific channel
Hope this helps! If you have any questions or issues just let me know and I'll help the best I can
So ive successfuly created a welcome message for users when they join the server which functions properly but, I want to make a command which allows me to turn the Welcome event off or on How can I do this.
#commands.Cog.listener()
async def on_member_join(self, member):
guild = discord.utils.get(member.guild.name)
channel = discord.utils.get(member.guild.channels, name='general')
await channel.send(f"{member.mention} welcome to **{guild}**! Dont be useless.")
#client.command()
async def turnoff(ctx):
sendwelcome = False
And then at the start of what you have just put
if sendwelcome = False:
return
I'm working on a Discord bot using Pycord 2.0.0b4 and am trying to implement buttons with a 3 second cooldown per user.
I want the bot to send a cooldown message if someone tries clicking the button during the cooldown (e.g. try again in x seconds), or respond with the embed embed1 otherwise.
Here's what I'm working with:
class yeetView(discord.ui.View):
#discord.ui.button(label='Yeet Again', style=discord.ButtonStyle.primary, emoji='🚀')
async def button_callback(self, button, interaction):
embed1 = await reYeetAlgo(interaction, interaction.user.id)
await interaction.response.send_message(embed=embed1, view=yeetView())
# Button click awaits some other async function reYeetAlgo, which returns Embed object embed1, which is then sent on Discord as an embed along with another button to run the function again if the user so chooses.
I have cooldowns on other commands with a similar cooldown time using #commands.cooldown(1, 3, commands.BucketType.user), but the decorator only works because they are commands. So I'm looking for something that applies the same thing but to buttons/interactions. Thanks
It is pretty easy
class yeetView(discord.ui.View):
def __init__(self) -> None:
super().__init__(timeout = None)
self.cooldown = commands.CooldownMapping.from_cooldown(1, 60,
commands.BucketType.member)
#discord.ui.button(label='Yeet Again', style=discord.ButtonStyle.primary, emoji='🚀')
async def button_callback(self, button, interaction):
bucket = self.cooldown.get_bucket(interaction.message)
retry = bucket.update_rate_limit()
if retry:
return await interaction.response.send_message(f"Slow down! Try
again in {round(retry, 1)} seconds.", ephemeral = True)
embed1 = await reYeetAlgo(interaction, interaction.user.id)
await interaction.response.send_message(embed=embed1, view=yeetView())
I'm trying to make my bot to send a message to a specified channel, when I start livestreaming on twitch. so far I'm fiddling with getting the right "activity" from my status, which represents, that I'm streaming. This is what I got so far :
class Stream(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.Cog.listener()
async def on_ready(self):
print("Streamingstatus loaded")
#commands.Cog.listener()
async def on_member_update(self, before, after):
channel = self.bot.get_channel (751954378772185149)
if after.activity is not None and after.activity.name == '#Twitch or so?' and after.id == 254375451084980224 and guild.id == 670015807551438874:
print(before.activity)
print(after.activity)
if after.activity.title.startswith(#streaming) and before.activity.title != after.activity.title:
await channel.send("Spleens went live on twitch right now! Watch him making a fool out of himself!")
Thanks in advance for any help!^^
Your issue is that you are checking activity.name while the correct would be to check activity.type. Also to check if a user is streaming you want to check if activity.type is discord.ActivityType.streaming. I am not completely sure what you are trying to achieve in some lines of your code, but to check if the user is streaming just do this:
#commands.Cog.listener()
async def on_member_update(self, before, after):
try:
activity_type = after.activity.type
except:
pass
# Here we check if the user is streaming
if activitity_type is discord.ActivityType.streaming:
# Do X if he is streaming
else:
# Do Y if he is not streaming
When I'm playing with my bot I do everything in a "bottesting" channel only I have access to, I'm trying to figure out how to get the bot on load to purge or delete all the messages currently in that certain channel
I've tried to make the bot.say(!clear) its own command to clear the channel but it won't recognize its output, I currently have to manually do the clear command I have set up for others to use
#bot.event
async def on_ready():
#bot announces to me when its online and posts in bottesting channel
await bot.send_message(discord.Object(id=bottesting), 'Im Back ' + myid)
print('<',datetime.datetime.now(),'>',NaMe," Launched")
This is what my current on_ready looks like and as of now it just posts that it is online and tags me in the post and the channel as well as outputs to cmd with print.
Here's one way you could turn clear into a seperate coroutine that can be called from both places:
#bot.command(pass_context=True)
#has_permissions(administrator=True, manage_messages=True, manage_roles=True)
async def clear(ctx, amount: int=1):
await _clear(ctx.message.channel, amount) # Technically you could apply the decorators directly
# to the _clear callback, but that's a little complicated
async def _clear(channel, amount=1):
messages = []
async for message in bot.logs_from(channel, limit=amount+1):
messages.append(message)
await bot.delete_messages(messages)
#bot.event
async def on_ready():
channel = bot.get_channel("123")
await _clear(channel, 100)