Discord.py constantly edit message - python

Ok so I've created a bot with python and have set it so its always up and everything, but I want to make a message that it constantly sets to the current user count in the server.
I have no idea as to how I would do this, and appreciate any help I could get. I have got the message id, the channel id, and the guild id, I just need to know how to edit messages and how to do it every 10 seconds or so.
Thanks for the help!

from discord.ext import commands
from typing import Union
import asyncio
import discord
_CH = Union[discord.TextChannel, discord.VoiceChannel, discord.CategoryChannel]
_C = commands.Context
class StatusCog(commands.Cog):
INTERVAL = 60
TEMPLATE = 'Online: {}, All: {}'
def __init__(self, bot: commands.Bot):
self.bot = bot
self.running = False
self.STATUS_DICT = {'online': 0, 'offline': 0, 'idle': 0, 'dnd': 0}
#commands.group(invoke_without_command=True)
async def count(self, ctx: _C):
pass
#count.command()
async def start(self, ctx: _C, channel: _CH):
self.running = True
while self.running:
status = self.STATUS_DICT.copy()
for member in ctx.guild.members:
s = member.status
for key in status.keys():
if getattr(s, key, False):
status[key] += 1
break
await channel.edit(name=self.TEMPLATE[:].format(list(status.values())))
await asyncio.sleep(self.INTERVAL)
#count.command()
async def stop(self, ctx: _C):
self.running = False
def setup(bot: commands.Bot):
bot.add_cog(Cog(bot))

Related

Nextcord send a message to a specific channel (discord.py fork)

So, I've been trying to make a suggestion discord bot, so it sends a message to a specific channel when prompted and verified with a button, but I can't seem to make it work.
My code is here:
import nextcord
import os
import requests
import asyncio
import random
import catapi
import json
import nest_asyncio
nest_asyncio.apply()
apicat = catapi.CatApi(api_key=os.environ['CAT_API_KEY'])
loop = asyncio.new_event_loop()
intents = nextcord.Intents.default()
intents.members = True
client = commands.Bot(command_prefix = '$', intents=intents)
colors = [0x00ffee,0x32a852,0x4287f5,0xcc2af5,0xf7e7a6,0xdea4a2]
def run_coro(coroutine):
return loop.run_until_complete(coroutine)
class YesOrNo(nextcord.ui.View):
def __init__(self):
super().__init__()
self.value = None
#nextcord.ui.button(emoji="✔", style=nextcord.ButtonStyle.success)
async def yes(self, button: nextcord.ui.Button, interaction: Interaction):
channel = client.get_channel(950111018405748746)
embed = nextcord.Embed(title="Suggestion was sent!", color=0x51ff00)
await channel.send("test")
await interaction.edit(embed=embed, view=None)
self.value = True
self.stop()
#nextcord.ui.button(emoji="✖️", style=nextcord.ButtonStyle.danger)
async def no(self, button: nextcord.ui.Button, interaction: Interaction):
embed = nextcord.Embed(title="Suggestion was discarded!", color=0xff0000)
await interaction.edit(embed=embed, view=None)
self.value = False
self.stop()
But I receive this error:
AttributeError: 'YesOrNo' object has no attribute 'client'
Any ideas how to fix it?
I've tried changing all the clients and stuff to bot, I've tried putting client inside the init, super init, class, ui.button, async def, and I STILL DO NOT KNOW WHAT'S WRONG!
Pass client and then define it with self.client, like this:
class YesOrNo(nextcord.ui.View):
#pass `client` here
↓
def __init__(self):
super().__init__()
self.value = None
self.client = client # ← then define self.client
Your code will look like this:
class YesOrNo(nextcord.ui.View):
def __init__(client, self):
super().__init__()
self.value = None
self.client = client
#nextcord.ui.button(emoji="✔", style=nextcord.ButtonStyle.success)
async def yes(self, button: nextcord.ui.Button, interaction: Interaction):
channel = client.get_channel(950111018405748746)
embed = nextcord.Embed(title="Suggestion was sent!", color=0x51ff00)
await channel.send("test")
await interaction.edit(embed=embed, view=None)
self.value = True
self.stop()
#nextcord.ui.button(emoji="✖️", style=nextcord.ButtonStyle.danger)
async def no(self, button: nextcord.ui.Button, interaction: Interaction):
embed = nextcord.Embed(title="Suggestion was discarded!", color=0xff0000)
await interaction.edit(embed=embed, view=None)
self.value = False
self.stop()
This is probably it.
• Sxviaat

discord.py - edit the interaction message after a timeout in discord.ui.Select

How can I access the interaction message and edit it?
discord.ui.Select
class SearchMenu(discord.ui.Select):
def __init__(self, ctx, bot, data):
self.ctx = ctx
self.bot = bot
self.data = data
self.player = Player
values = []
for index, track in enumerate(self.data[:9]):
values.append(
discord.SelectOption(
label=track.title,
value=index + 1,
description=track.author,
emoji=f"{index + 1}\U0000fe0f\U000020e3"
)
)
values.append(discord.SelectOption(label='Cancel', description='Exit the search menu.', emoji="🔒"))
super().__init__(placeholder='Click on the Dropdown.', min_values=1, max_values=1, options=values)
async def callback(self, interaction: discord.Interaction):
if self.values[0] == "Cancel":
embed = Embed(emoji=self.ctx.emoji.whitecheck, description="This interaction has been deleted.")
return await interaction.message.edit(embed=embed, view=None)
discord.ui.View
class SearchMenuView(discord.ui.View):
def __init__(self, options, ctx, bot):
super().__init__(timeout=60.0)
self.ctx = ctx
self.add_item(SearchMenu(ctx, bot, options))
async def interaction_check(self, interaction: discord.Interaction):
if interaction.user != self.ctx.author:
embed = Embed(description=f"Sorry, but this interaction can only be used by {self.ctx.author.name}.")
await interaction.response.send_message(embed=embed, ephemeral=True)
return False
else:
return True
async def on_timeout(self):
embed = Embed(emoji=self.ctx.emoji.whitecross, description="Interaction has timed out. Please try again.")
await self.message.edit(embed=embed, view=None)
If I try to edit the interaction like this I am getting
-> AttributeError: 'SearchMenuView' object has no attribute 'message'
After 60 seconds the original message should be replaced with the embed in the timeout.
You're trying to ask the View to send a message, which is not a method in discord.ui.View.
You could defer the response and don't let it timeout and allow the user to try again?
async def interaction_check(self, interaction: discord.Interaction):
if interaction.user != self.ctx.author:
embed = Embed(description=f"Sorry, but this interaction can only be used by {self.ctx.author.name}.")
await interaction.channel.send(embed=embed, delete_after=60)
await interaction.response.defer()
return True
view = MyView()
view.message = await channel.send('...', view=view)
After that you can use self.message in on_timeout (or somewhere else you don't have access to interaction.message) to edit it.
Source: https://discord.com/channels/336642139381301249/669155775700271126/860883838657495040

python aiogram how to stop an asynchronous loop

Simple example
import asyncio
import logging
from aiogram import Bot, Dispatcher, types
logging.basicConfig(level=logging.INFO)
token = 'token'
bot = Bot(token=token)
dp = Dispatcher(bot=bot)
#dp.callback_query_handler(text='stoploop')
async def stop_loop(query: types.CallbackQuery):
# TODO how to stop test loop?
await query.message.edit_text('stop')
#dp.callback_query_handler(text='test')
async def start_loop(query: types.CallbackQuery):
a = 100
while True:
a -= 1
markup = types.InlineKeyboardMarkup()
markup.add(types.InlineKeyboardButton('<<<Stop And Back To Home', callback_data='stoploop'))
await query.message.edit_text(str(a),reply_markup=markup)
await asyncio.sleep(1)
#dp.message_handler(commands='start')
async def start_cmd_handler(message: types.Message):
markup = types.InlineKeyboardMarkup()
markup.add(
types.InlineKeyboardButton('start loop', callback_data='test')
)
await message.reply('test', reply_markup=markup)
async def main():
try:
await dp.start_polling()
finally:
await bot.close()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
When I click start_loop, the tg message box on my page starts to display a countdown. When I click stop, how can I stop the previous countdown?
I use id(query) to confirm that the query instance sent twice is not the same. After I execute the stop_loop function, start_loop will still execute and change the content of the message.
Can someone tell me how to stop it?
I used redis to solve it, but I don't know if this is the most appropriate way. If there is a more suitable way, please let me know
To manage your loop you should take it outside the handlers and just get in from any storage (dict is used for example).
Basic example of the loop
loops = {}
class Loop:
def __init__(self, user_id):
self.user_id = user_id
self._active = False
self._stopped = True
loops[self.user_id] = self
#classmethod
def get_loop(cls, user_id):
return loops.get(user_id, cls(user_id))
#property
def is_running(self):
return not self._stopped
async def start(self):
self._active = True
asyncio.create_task(self._run_loop())
async def _run_loop(self):
while self._active:
await bot.send_message(self.user_id, 'loop is running')
await asyncio.sleep(5)
self._stopped = True
async def stop(self):
self._active = False
while not self._stopped:
await asyncio.sleep(1)
So then:
#dp.callback_query_handler(text='start')
async def start_loop(query: CallbackQuery):
user = query.from_user
loop = Loop.get_loop(user.id)
if loop.is_running:
return await query.answer('Loop is already running')
loop.start()
await query.answer('Started!')
#dp.callback_query_handler(text='stop')
async def stop_loop(query: CallbackQuery):
user = query.from_user
loop = Loop.get_loop(user.id)
await query.answer('Stopping...')
await loop.stop()
await bot.send_message(user.id, 'Loop successfully stopped.')

Toggling command access - Discord.py

im pretty new to python so i thought a good learning project would be to create a discord bot for a personal server, i have a few commands that only i as the owner of the bot can access but i would like to be able to toggle that access with a command so that my friends can also use them but i have a problem, this is the piece of the code giving me an error:
MyID = '<#xxxxxxxxxxxxxxxx>'
FFA = False
class TestCommands(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_ready(self):
def IDCheck(ctx):
return ctx.message.author.id == xxxxxxxxxxxxxxxxxxxxxx
#commands.command()
async def ToggleFFA(ctx):
if FFA == False:
FFA = True
print (FFA)
await message.channel.send('All user can now use owner locked commands')
if FFA == True:
FFA = False
print (FFA)
await message.channel.send('All user can no longer use owner locked commands')
###########################################################################
#commands.command()
if FFA == False:
#commands.check(IDCheck)
async def FFATest(self, ctx, *, question):
loopnumber = 0
while spamnumber < int(question):
await ctx.send('Test' + MyID);
await asyncio.sleep(1)
loopnumber += 1
print ({loopnumber})
if FFA == True:
async def LoopTest(self, ctx, *, question):
loopnumber = 0
while loopnumber < int(question):
await ctx.send('Test' + MyID);
await asyncio.sleep(1)
loopnumber+= 1
print ({loopnumber})
###########################################################################
i get an invalid syntax error within the highlighted piece of code. If anyone knows a simpler way of toggling the access or a way that i can correct the error i would really appreciate it.
thanks in advance.
You can specify a bot_check_once method that will be used as a check on all commands from the bot. We can then have an attribute of the cog that controls which mode we are in:
class MyCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.owner_only = True
async def bot_check_once(self, ctx):
app = await self.bot.application_info()
if self.owner_only:
return ctx.author == app.owner
else:
return True
#commands.command()
async def all_users(self, ctx):
self.owner_only = False
#commands.command()
async def just_owner(self, ctx):
self.owner_only = True
Well you could add a check method outside. Here is an example.
def FFA_Enabled(ctx):
global FFA
if commands.is_owner():
return True
else:
return FFA
#commands.check(FFA_enabled):
async def SomeCommand(self, ctx:Context):
ctx.send("Message")
This Should work
if you don't know what ctx:Context means
It derives ctx from the type Context(i use it for autofill) if you wanna you is i suggest you type this:
from discord.ext.commands import Context
You can use a LIST for this, Inside that you can store the USER ID and Status ID.
Note: This is a just snippet to give you an idea, The ID Will reset when the script is restarted, I recommend you to save it in a file and load it from there.
You can also use a function to return True/False based on the USER ID instead of writing a bunch of code in each command.
users = []
status = 'Global'
#commands.is_owner()
#commands.command()
async def add_user(self,ctx,user:discord.User):
global users
id = user.id
await ctx.send(f'{user.name} has been added into the mod list.')
return users.append(id)
#commands.is_owner()
#commands.command()
async def change_status(self,ctx):
global status
if status == 'Global':
status = 'Local'
elif status == 'Local':
status = 'Global'
await ctx.send(f'Status has been changed to {status}')
return status
#commands.command()
async def test_command(self,ctx):
global status
global users
#IF Status is Local
if status == 'Local':
if ctx.user.id in users:
#ALLOW THE USERS' WHOS' ID IS IN THE LIST
pass
else:
#EXIT THE FUNCTION IF USER NOT IN THE LIST
return
#IF The status is Global
else:
#ALLOW THE COMMAND IF IT'S GLOBAL
pass

Missing 1 required positional argument: 'number'

Hi I'm having an issue running a asyncio loop it's asking for a missing 1 required positional argument: 'number'.
Here is what I'm working with:
async def purge_modlog(ctx, number):
tomorrow = datetime.now()+timedelta(days=1)
midnight = datetime(year=tomorrow.year, month=tomorrow.month,
day=tomorrow.day, hour=20, minute=35, second=0)
number = int(number)
server = before.server
db = fileIO(self.direct, "load")
if not server.id in db:
return
channel = db[server.id]["Channel"]
if number > 99 or number < 1:
await ctx.send("I can only delete messages within a range of 1 - 99", delete_after=10)
else:
author = ctx.message.author
authorID = author.id
mgs = []
number = int(number)
channel = modlog
async for x in bot.logs_from((channel), limit = int(number+1)):
mgs.append(x)
await asyncio.sleep((midnight - datetime.now()).seconds)
print("Deleting modlog messages 14 day or older")
await asyncio.sleep(5)
await delete_messages(mgs)
await ctx.send('Success!', delete_after=4)
await asyncio.sleep(86400) # Wait 24 hours
def check_folder():
if not os.path.exists('data/modlogset'):
print('Creating data/modlogset folder...')
os.makedirs('data/modlogset')
def check_file():
f = 'data/modlogset/settings.json'
if not fileIO(f, 'check'):
print('Creating default settings.json...')
fileIO(f, 'save', {})
def setup(bot):
check_folder()
check_file()
q = ModLog(bot)
loop = asyncio.get_event_loop()
loop.create_task(q.purge_modlog())
bot.add_cog(q)
To loop the event under def def setup(bot): You can see
loop = asyncio.get_event_loop()
loop.create_task(q.purge_modlog())
This is supposed to loop the event (q.purge_modlog()) I'm not sure what I'm doing wrong here. I have already tried the follow (q.purge_modlog(ctx, number))
line 686, in setup
loop.create_task(q.purge_modlog(ctx, number))
NameError: name 'ctx' is not defined
If anyone could help me that would be great. To add this is a module.
I have corrected some mistakes here. Try to read through the discord.py documentation on purging messages.
class ModLog:
def __init__(self, bot):
self.bot = bot
async def on_ready(self):
await self.bot.wait_until_ready()
for server in self.bot.servers:
channel = self.bot.get_channel("channel ID here")
if channel:
self.bot.loop.create_task(self.modlog_purge(channel))
async def modlog_purge(self, channel):
while True:
now = datetime.utcnow()
two_weeks_ago = now - timedelta(days=14)
await self.bot.purge_from(channel, before=two_weeks_ago)
await asyncio.sleep(86400)
def setup(bot):
q = ModLog(bot)
bot.add_cog(q)
class ModLog:
def __init__(self, bot):
self.bot = bot
async def on_ready(self):
await self.bot.wait_until_ready()
for guild in self.bot.guilds:
channel = await self.get_channel(guild)
if channel:
self.bot.loop.create_task(self.modlog_purge(channel))
async def get_channel(guild):
# Whatever your logic for getting the Channel for a given guild is
async def modlog_purge(self, channel):
while True:
now = datetime.utcnow()
two_weeks_ago = now - timedelta(days=14)
await channel.purge(before=two_weeks_ago)
await asyncio.sleep(86400)
def setup(bot):
q = ModLog(bot)
bot.add_cog(q)
Here's how I would structure this (I'm not on a computer with discord.py at the moment, so there may be some errors). We have an on_ready event that kicks off background tasks for each server that has a channel we want to maintain (this could instead loop through a list of channels, or something similar).
The actual purge is all taken care of by the TextChannel.purge coroutine. We just pass it a datetime object, and it deletes 100 messages from before that date (this is configurable). It then sleeps for a day and repeats.

Categories

Resources