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.
Related
I am trying to give users who joined using the disboard invite a 'no-invite' role, because my server gets raided very often. Someone joins trough db, then invites 20 others etc..
This is the code I use:
from discord.ext import commands
import asyncio
from variables import no_invite_role_id, disboard_invite, logs_channel_id
invites = {}
def find_invite_by_code(invite_list, code):
for inv in invite_list:
if inv.code == code:
return inv
class Disboard(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.Cog.listener()
async def on_ready(self):
await self.bot.wait_until_ready()
for guild in self.bot.guilds:
invites[guild.id] = await guild.invites()
#commands.Cog.listener()
async def on_member_join(self, member):
invites_before_join = invites[member.guild.id]
no_invite_role = member.guild.get_role(no_invite_role_id)
logs_channel = self.bot.get_channel(logs_channel_id)
invites_after_join = await member.guild.invites()
for invite in invites_before_join:
if invite.uses < find_invite_by_code(invites_after_join, invite.code).uses:
await logs_channel.send(f"{member.mention}, `{member.id}` a intrat prin disboard")
if disboard_invite in invite.code:
await member.add_roles(no_invite_role, reason="A intrat prin disboard")
await asyncio.sleep(1200)
try:
await member.remove_roles(no_invite_role, reason="Perioada no-invite a expirat")
except:
await logs_channel.send(f"{member} a iesit inainte de expirarea perioadei no-invite")
invites[member.guild.id] = invites_after_join
return
def setup(bot):
bot.add_cog(Disboard(bot))
For some reason, this code sometimes works, sometimes doesn't. 70% of the time it works on my pc, but only 10% on my host.
This is the error that i get when it's not working:
Traceback (most recent call last):
File "/home/container/discord/client.py", line 351, in _run_event
await coro(*args, **kwargs)
File "/home/container/cogs/disboard.py", line 38, in on_member_join
if invite.uses < find_invite_by_code(invites_after_join, invite.code).uses:
AttributeError: 'NoneType' object has no attribute 'uses'
Ignoring exception in on_member_join
*I also have on_ready in another cog, but I don't think that does anything.
I found out how to do it, thanks to https://github.com/GregTCLTK/Discord-Invite-Tracker
# Original code: GregTCLTK 2018-2021.
# Contact Developer on https://discord.gg/nPwjaJk (Skidder#8515 | 401817301919465482)
# Original repo: https://github.com/GregTCLTK/Discord-Invite-Tracker
# Original cog by: Quill (quillfires)
# Edited by Mihai Cristian
from discord.ext import commands
from discord import Embed
import asyncio
disboard_invite = "xxxxx"
no_invite_role_id = 0000000000
class invite_tracker(commands.Cog):
"""
Keep track of your invites
"""
def __init__(self, bot):
self.bot = bot
self.version = "1.0.0"
self.invites = {}
bot.loop.create_task(self.load())
async def load(self):
await self.bot.wait_until_ready()
# load the invites
for guild in self.bot.guilds:
try:
self.invites[guild.id] = await guild.invites()
except:
pass
def find_invite_by_code(self, inv_list, code):
for inv in inv_list:
if inv.code == code:
return inv
#commands.Cog.listener()
async def on_member_join(self, member):
no_invite_role = member.guild.get_role(no_invite_role_id)
try:
invs_before = self.invites[member.guild.id]
invs_after = await member.guild.invites()
self.invites[member.guild.id] = invs_after
for invite in invs_before:
if invite.uses < self.find_invite_by_code(invs_after, invite.code).uses:
if disboard_invite in invite.code:
try:
await member.add_roles(no_invite_role, reason="Joined through disboard")
await asyncio.sleep(1200)
try:
await member.remove_roles(no_invite_role, reason="The no-invite period has expired")
except:
pass
except:
pass
except:
pass
#commands.Cog.listener()
async def on_guild_join(self, guild):
try:
self.invites[guild.id] = await guild.invites()
except:
pass
#commands.Cog.listener()
async def on_guild_remove(self, guild):
try:
self.invites.pop(guild.id)
except:
pass
def setup(bot):
bot.add_cog(invite_tracker(bot))
Hello I'm trying to create a music bot but I want it so I can play music and add additional music to a queue with the same command. I've tried to do this but I can't get it to work. Here's the code with play and queue being two separate commands:
#bot.command(pass_context=True)
async def join(ctx):
await bot.join_voice_channel(bot.get_channel('487315853482786820'))
#bot.command(pass_context=True)
async def leave(ctx):
voice_client = bot.voice_client_in(ctx.message.server)
await voice_client.disconnect()
players = {}
queues = {}
def check_queue(id):
if queues[id] != []:
player = queues[id].pop(0)
players[id] = player
player.start()
#bot.command(pass_context=True)
async def play(ctx, url):
server = ctx.message.server
voice_client = bot.voice_client_in(server)
player = await voice_client.create_ytdl_player(url, after=lambda: check_queue(server.id))
players[server.id] = player
player.start()
#bot.command(pass_context=True)
async def queue(ctx, url):
server = ctx.message.server
voice_client = bot.voice_client_in(server)
player = await voice_client.create_ytdl_player(url, after=lambda: check_queue(server.id))
if server.id in queues:
queues[server.id].append(player)
else:
queues[server.id] = [player]
await bot.say('Video queued.')
#bot.command(pass_context=True)
async def pause(ctx):
id = ctx.message.server.id
players[id].pause()
#bot.command(pass_context=True)
async def stop(ctx):
id = ctx.message.server.id
players[id].stop()
#bot.command(pass_context=True)
async def resume(ctx):
id = ctx.message.server.id
players[id].resume()
You need some sort of queue system where song requests are stored in the queue and a loop that checks if any item is in the queue, grabbing the first item in the queue if it's available. If no songs are in the queue, then the loop waits until a song is added. You can use asyncio.Queue() and asyncio.Event() to do this.
import asyncio
from discord.ext import commands
client = commands.Bot(command_prefix='!')
songs = asyncio.Queue()
play_next_song = asyncio.Event()
#client.event
async def on_ready():
print('client ready')
async def audio_player_task():
while True:
play_next_song.clear()
current = await songs.get()
current.start()
await play_next_song.wait()
def toggle_next():
client.loop.call_soon_threadsafe(play_next_song.set)
#client.command(pass_context=True)
async def play(ctx, url):
if not client.is_voice_connected(ctx.message.server):
voice = await client.join_voice_channel(ctx.message.author.voice_channel)
else:
voice = client.voice_client_in(ctx.message.server)
player = await voice.create_ytdl_player(url, after=toggle_next)
await songs.put(player)
client.loop.create_task(audio_player_task())
client.run('token')
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.')
I am currently working on a MusicPlayer that loops over a function inside the class MusicPlayer(). The problem is it doesn't seem to even be playing. Below is my class and the create_task is inside the __init__ but its like it never gets ran. I have ran loops before but never in a class, is there something I am missing?
MusicPlayer Class:
class MusicPlayer():
__slots__ = ("client", "_guild", "_ctxs", "_channel", "_cog", "np", "volume", "current", "colour")
queue = asyncio.Queue()
next = asyncio.Event()
def __init__(self, ctx, client):
self.client = client
self._guild = ctx.guild
self._ctxs = ctx
self._channel = ctx.channel
self._cog = ctx.cog
self.np = None
self.volume = defaultvolume
self.current = None
self.colour = self.client.defaultcolour
self.client.loop.create_task(self.player_loop())
async def player_loop(self):
print('player_loop ran')
await self.client.wait_until_ready()
while True:
self.next.clear()
try:
async with timeout(300):
self.current = await queue.get()
except asyncio.CancelledError:
return
except asyncio.TimeoutError:
guild = self._guild
vc = guild.voice_client
self.destroy(guild)
if not vc: return
await self._ctxs.send(":point_right: **I disconnected myself from the **`{}`** voice channel as I was not playing audio for 5 minutes!**".format(vc.channel.name))
return
except:
self.destroy(self._guild)
await self._ctxs.send(":thumbsdown: **Error: getting next song failed!** Please retry later!")
return
self._ctxs.voice_client.play(self.current, after=lambda: self.client.loop.call_soon_threadsafe(next.set))
self.current.volume = self.volume
thumbnail = self.current.thumbnail if self.current.thumbnail else self.client.user.avatar_url
self.colour = await self.client.get_average_colour(thumbnail)
embednps = discord.Embed(colour=self.colour)
embednps.add_field(name="Now Playing", value=f"```{self.current.title}```", inline=False)
embednps.add_field(name="Link", value=f"[URL]({self.current.web_url})", inline=True)
embednps.add_field(name="Duration", value=self.client.time_from_seconds(self.current.duration), inline=True)
embednps.add_field(name="Channel", value=f"{self.current.uploader}", inline=False)
embednps.set_thumbnail(url=f"{thumbnail}")
embednps.set_footer(text=f"Requested by {self.current.requester}", icon_url=self.current.requester.avatar_url)
self.np = await self._channel.send(embed=embednps)
await next.wait()
print("Terminated")
# Cleanup player
self.current.cleanup()
self.current = None
Looks like you're using an older revision of this: Basic music with playlist support on Rewrite
You should change the create_task from:
self.client.loop.create_task(self.player_loop())
To:
ctx.bot.loop.create_task(self.player_loop())
I need help to edit the name of the voice channel. I've tried everything in my power to research and find the answer, but nobody has the same problem as me :/
Code:
async def member_count():
activeServers = bot.servers
channel = bot.get_channel(549692930294677525) #Collects the voicechannel id I want to change name on
sum = 0
for s in activeServers:
sum += len(s.members)
await bot.edit_channel(channel, name="MEMBERS: {}".format(int(sum))) #Here am I trying to edit the channels name to "MEMBERS: (number of members in the server atm)"
await asyncio.sleep(1) #Sleeps for one second before running the code again
bot.loop.create_task(member_count()) #Creates a loop for the code above
Error message:
Task exception was never retrieved
future: <Task finished coro=<member_count() done, defined at C:\Users\bjha0\desktop\koding\bot.py:26> exception=AttributeError("'NoneType' object has no attribute 'topic'",)>
Traceback (most recent call last):
File "C:\Users\bjha0\desktop\koding\bot.py", line 32, in member_count
await bot.edit_channel(channel, name="MEMBERS: {}".format(int(sum)))
File "C:\Users\bjha0\AppData\Local\Programs\Python\Python36\lib\site-packages\discord\client.py", line 2007, in edit_channel
options[key] = getattr(channel, key)
AttributeError: 'NoneType' object has no attribute 'topic'
I found this to work perfectly, for me at least.
async 0.16.12:
async def members():
activeServers = bot.servers
channel = bot.get_channel("<channel id here>")
sum = 0
for s in activeServers:
sum += len(s.members)
await bot.edit_channel(channel, name="❎ MEMBERS: {} ❎".format(int(sum)))
await asyncio.sleep(1)
#bot.event
async def on_member_join(member):
await members()
#bot.event
async def on_member_remove(member):
await members()
rewrite 1.3.3:
async def members():
activeGuilds = bot.guilds
channel = bot.get_channel(<channel id here>)
sum = 0
for s in activeGuilds:
sum += len(s.members)
await channel.edit(name='❎ MEMBERS: {} ❎'.format(int(sum)))
await asyncio.sleep(1)
#bot.event
async def on_member_join(member):
await members()
#bot.event
async def on_member_remove(member):
await members()