Implementing a queue function in discord.py - discord.py

I'm trying to implement a command with discord.py that allows users to enter a queue. Once the queue hits 6 total users, it closes. Right now I'm just testing how to have the queue increment. Currently the queue returns back to the default count = 0, if count != 6. However, I'm not sure how to run the queue command again without it running through the entire function. Basically, once a user starts the queue command I need it to save their spot in the queue while also allowing more users to enter. All while avoiding returning to the beginning of the function.
I know it seems simple but I can't wrap my head around how to do it. I tried converting the members who queued into an integer to compare the total value of members queued with the 6 user limit, but can't parse "message.author" to integer.
#client.command()
async def queue(ctx):
count = 0
while count <= 6:
await ctx.send('Added to the queue!' f'{ctx.author.mention}')
count += 1
#member = ctx.author.mention
while count != 6:
return
else:
await ctx.send('Queue full')
Thanks for the help.

calling count = 0 in the beginning will reset your queue to 0 every time the command is called
What you want to do is save the queue state in memory or on disk.
The dirty way to solve it in this scenario is with a global variable, but you generally want to avoid those. here's why
qcount = 0
#client.command()
async def queue(ctx):
global qcount
if qcount <= 6:
qcount += 1
await ctx.send('Added to the queue!' f'{ctx.author.mention}')
else:
await ctx.send('Queue full')
What you really want to do is pack the bot into a class (Cog) and start the queue in its init:
class Botname(commands.Cog):
def __init__(self, client):
self.client = client
self.qcount = 0
#commands.command()
async def queue(self, ctx):
if self.qcount <= 6:
self.qcount += 1
await ctx.send('Added to the queue!' f'{ctx.author.mention}')
else:
await ctx.send('Queue full')
return
if __name__ == '__main__':
client.add_cog(Botname(client))
client.run(TOKEN)
or alternatively you can use a SQL database to store the queue value

You can simply have a variable outside of your function and increment it inside the function as follows
count = 0
#client.command()
async def queue(ctx):
global count
if count < 6: # Smaller than 6 so that only 6 people can enter
await ctx.send('Added to the queue!' f'{ctx.author.mention}')
count += 1
#member = ctx.author.mention
else:
await ctx.send('Queue full')
You could also look into discord's api wait_for that can be useful if your program should "wait" for certain event to happen after a command is triggered

Related

How to change channel permissions without having a message sent first?

I have a program that changes what channel members can see at certain times of the day. To do this, I can either change the roles that every member has, or change the permissions of each channel. However, I have looked all over the web and all of the ways for either method require a message to be sent so that the data from that message can be read and put into the function, such as:
#client.command()
async def perm(ctx):
await ctx.channel.set_permissions(ctx.guild.default_role, send_messages=False
Change the permissions of a discord text channel with discord.py
or
async def addrole(ctx):
member = ctx.message.author
role = get(member.server.roles, name="Test")
await bot.add_roles(member, role)
Discord.py | add role to someone
My current program looks like this:
import discord
client = discord.Client()
import datetime
async def live_day(schedule):
current_place = "the void"
while True:
current_time = str((datetime.datetime.now() -datetime.timedelta(hours=7)).time())
int_time = int(current_time[:2] + current_time[3:5])
for x in range(len(schedule)):
try:
start_time = schedule[x][1]
end_time = schedule[x + 1][1]
except IndexError:
end_time = 2400
if current_place != schedule[x][0] and int_time >= start_time and int_time < end_time:
current_place = schedule[x][0]
#Change the channel permissions of the current place to allow viewing
#Change the channel permissions of the last channel to disallow viewing
#client.event
async def on_ready():
print("{0.user} has arrived for duty".format(client))
client.loop.create_task(live_day([
("Home", 0),
("Work", 900),
("Home", 1700),
("Nightclub", 2000),
("Home", 2200),
]))
client.run(my_secret)
Never mind the badly written code, how would I do this or where should I go to figure this out? Any help is appreciated. Thanks!
Edit: I could get the channels individually by using this,
channel1=client.get_channel(channelid)
discord.py, send message by channel id without on_message() event?
but then I can't use this for more than one server. How can I get channels by name?
Note on on_ready():
This function is not guaranteed to be the first event called. Likewise, this function is not guaranteed to only be called once. This library implements reconnection logic and thus will end up calling this event whenever a RESUME request fails.
Using this function may crash the bot.
Instead create background tasks and use wait_until_ready().
You can find examples in https://github.com/Rapptz/discord.py/blob/v1.7.3/examples/background_task.py
If you want to change only one channel's permissions (per guild) this might fit your needs:
#changing "Home" channel visibility, every day at 5 pm.
#client.event
async def on_ready():
while True:
await asyncio.sleep(1)
current_time = datetime.datetime.now()
five_pm = current_time.replace(hour=17, minute=0, second=0)
if current_time == five_pm:
for guild in client.guilds: #looping through all the guilds your bot is in
channel = discord.utils.get(client.get_all_channels(), name="Home") #getting channel by name by using "discord.utils"
await channel.set_permissions(guild.default_role, view_channel=True) #changing permissions
By using the on_ready() event you can loop through the time check without sending any message.
Remember to import discord.utils.

Command to spam something discord-bot

so basically I am trying to make a spam command for my discord bot, which takes in a custom message to spam. Here's the code:
#client.command(name='spam')
async def spam(ctx):
global stop
stop = 0
content = ctx.message.content[11:]
if ctx.author.guild_permissions.administrator or ctx.author.id in admins:
if lock == 1:
await ctx.send('Jesus bot is currently locked.')
elif lock == 0:
await ctx.send('Beginning spam..')
while not stop:
await ctx.send(content)
else:
await ctx.send('Sorry, but you do not have admin permissions in this server, or you are not a verified admin.')
For some reason, whenever I try to use this command, the bot doesn't respond. I'm not sure why this happens, and could use some help please.
Picture of bot not responding:
I have a spam command, but I only use it to mess around with my friends. I would not recommend using this as a public command, as you may get rate limited or banned for abuse or something like that. Anyway here is the code I have used for it.
#commands.command()
#commands.is_owner()
# If you want to use admin only, use this below
# #commands.has_permissions(administrator=True)
async def spam(self, ctx, amount, *, word):
int(amount)
await asyncio.sleep(2)
print(f"Starting to spam {word} in {ctx.guild.name}")
await ctx.message.delete()
await ctx.send(f"{ctx.author.mention}\nPlease note that this will clog up the bot's reaction time")
await asyncio.sleep(3)
count = 0
counting=True
while counting:
await ctx.send(word)
count = count + 1
if count == amount:
await asyncio.sleep(2)
await ctx.send("Spam complete")
print(Fore.GREEN + "Spam complete")
counting = False
At the top of your code, make sure you import asyncio as time.sleep will cause the whole bot to pause. Also the Fore.GREEN stuff is just colorama (import colorama).
Try using tasks instead of asyncio. It is made for such repetetive operations and it is easier and nicer because it is made by discord and is included in discord.ext. Something like this:
from discord.ext import tasks
#client.command(name='spam')
async def spam(ctx):
#get message and do all the ifs that you have there
spamLoop.start()
#client.command(name='stopSpam')
async def spamStop(ctx):
# stop the loop
spamLoop.cancel()
#tasks.loop(seconds=1)
async def spamLoop():
print("The message")
Actually quite a simple way of adding spam
import asyncio
#bot.command(name='spam', help= "Spam to your heart's delight")
async def spam(ctx, thing, amount):
count = 0
while count < int(amount):
await ctx.send(thing)
count += 1
if count < amount:
await asyncio.sleep(1)

Python asyncio - Increase the value of Semaphore

I am making use of aiohttp in one of my projects and would like to limit the number of requests made per second. I am using asyncio.Semaphore to do that. My challenge is I may want to increase/decrease the number of requests allowed per second.
For example:
limit = asyncio.Semaphore(10)
async with limit:
async with aiohttp.request(...)
...
await asyncio.sleep(1)
This works great. That is, it limits that aiohttp.request to 10 concurrent requests in a second. However, I may want to increase and decrease the Semaphore._value. I can do limit._value = 20 but I am not sure if this is the right approach or there is another way to do that.
Accessing the private _value attribute is not the right approach for at least two reasons: one that the attribute is private and can be removed, renamed, or change meaning in a future version without notice, and the other that increasing the limit won't be noticed by a semaphore that already has waiters.
Since asyncio.Semaphore doesn't support modifying the limit dynamically, you have two options: implementing your own Semaphore class that does support it, or not using a Semaphore at all. The latter is probably easier as you can always replace a semaphore-enforced limit with a fixed number of worker tasks that receive jobs through a queue. Assuming you currently have code that looks like this:
async def fetch(limit, arg):
async with limit:
# your actual code here
return result
async def tweak_limit(limit):
# here you'd like to be able to increase the limit
async def main():
limit = asyncio.Semaphore(10)
asyncio.create_task(tweak_limit(limit))
results = await asyncio.gather(*[fetch(limit, x) for x in range(1000)])
You could express it without a semaphore by creating workers in advance and giving them work to do:
async def fetch_task(queue, results):
while True:
arg = await queue.get()
# your actual code here
results.append(result)
queue.task_done()
async def main():
# fill the queue with jobs for the workers
queue = asyncio.Queue()
for x in range(1000):
await queue.put(x)
# create the initial pool of workers
results = []
workers = [asyncio.create_task(fetch_task(queue, results))
for _ in range(10)]
asyncio.create_task(tweak_limit(workers, queue, results))
# wait for workers to process the entire queue
await queue.join()
# finally, cancel the now-idle worker tasks
for w in workers:
w.cancel()
# results are now available
The tweak_limit() function can now increase the limit simply by spawning new workers:
async def tweak_limit(workers, queue, results):
while True:
await asyncio.sleep(1)
if need_more_workers:
workers.append(asyncio.create_task(fetch_task(queue, results)))
Using workers and queues is a more complex solution, you have to think about issues like setup, teardown, exception handling and backpressure, etc.
Semaphore can be implemented with Lock, if you don't mind abit of inefficiency (you will see why), here's a simple implemention for a dynamic-value semaphore:
class DynamicSemaphore:
def __init__(self, value=1):
self._lock = asyncio.Lock()
if value < 0:
raise ValueError("Semaphore initial value must be >= 0")
self.value = value
async def __aenter__(self):
await self.acquire()
return None
async def __aexit__(self, exc_type, exc, tb):
self.release()
def locked(self):
return self.value == 0
async def acquire(self):
async with self._lock:
while self.value <= 0:
await asyncio.sleep(0.1)
self.value -= 1
return True
def release(self):
self.value += 1

I've got a problem where I want to create a voice channel every time 2 people use the same dedicated command, which I managed to do but

the problem is I can't connect the 2 users to it because I don't have the ID of the channel that has been created
Look at this code
async def join(ctx,i=[0],c=[0]):
author = ctx.author
guild = ctx.guild
if i[0] == 0:
channel = await guild.create_voice_channel(f"""Voice chat # {c[0]}""")
i[0] = 0
c[0]+=1
i[0] += 1
if i[0] == 2:
i[0] = 0
print(i[0])
return i[0]
Could anyone potentially help me with this?
For some elaboration - What happens here is, if the user types !join It will create a voice channel, in which i want the user to be sent into immediately, then a different user also uses !join and gets sent into the same channel (which I also covered by counting the number of people joining), so essentially if 2 people use the command, I want them to be sent into a dedicated voice channel, if other 2 users do the same, they also get sent into their own dedicated voice chat on the server. Thanks <3
If a user isn't connected to a voice channel, you can't make him join one.
The only thing you can do is moving someone to another channel with the move_to function.
To track which user is connected to a channel, you could have a dict which takes a channel id as a key and the two users as a value:
channels = {}
async def join(ctx, i=[0], c=[0]):
author = ctx.author
guild = ctx.guild
if i[0] == 0:
channel = await guild.create_voice_channel(f"""Voice chat # {c[0]}""")
channels[channel.id] = [#Your two users]
i[0] = 0
c[0]+=1
i[0] += 1
if i[0] == 2:
i[0] = 0
print(i[0])
return i[0]
A example of how to use move_to:
#commands.command()
async def move(ctx):
channel = bot.get_channel(ID) #ID must be an INT
await ctx.author.move_to(channel)
If you want to change the channel's user limit:
channel = await guild.create_voice_channel(f"""Voice chat # {c[0]}""")
await channel.edit(user_limit=2)
If you want to check how many members are connected:
async def check_channels(ctx):
for channel_id in self.channels.keys():
channel = bot.get_channel(int(channel_id))
if len(channel.members) == 0:
await channel.delete()

Problem with (AttributeError) client.logs_from

i was creating bot for discord channel. But I don't understand where i have wronged or errored in my code
I was do from tutorial but in video this wrong not present. Then I search this problem in another tutorial, but i don't have result pls help
#client.command(pass_context=True, name='clear', aliases=['purgemessages'], no_pm=True)
async def clear(ctx, number):
number = int(number)
if number > 99 or number < 1:
await ctx.send("Sorry comrade>-< \n But i can deleted message within a range of 1 - 99")
else:
author = ctx.message.author
authorID = author.id
mgs = []
number = int(number)
channel = ctx.message.channel
async for x in client.logs_from((channel), limit = int(number)):
mgs.append
await ctx.message.channel.delete_messages(ctx, member, mgs)
await ctx.send("This was deleted ^^")
I want a bot to delete messages
You should use client.purge() to do it.
It seems that you're looking for a way to delete the command caller's messages (a.k.a author).
Here's a quick example using purge() method :
author = ctx.message.author
# check function
def is_caller(message):
if(message.author.id == author.id):
return(True)
else:
return(False)
# delete the author's messages
# the purge method returns a list of deleted messages
deleted_message = await client.purge(
limit = number,
check = is_caller
)
await ctx.send(f"{len(deleted_message)} messages deleted.")
The limit is represented by the number parameter of your command.
Hope it helped !

Resources