How to randomize tasks.loop timer in discord.py - python

i tried this:
timer = 15
random_timer = timer
channel = 108728307283
#tasks.loop(seconds = random_timer)
async def hello():
await channel.send("Hello!")
random_timer = timer + random.randint(1, 20)
hello.start()
My guess is that while a loop is running, it only looks at the value when it is run for the first time, so how do I set this random_timer value back to seconds without stopping the loop? In short, what should I do to send a message to a channel in a random time between 15-35 times?

To specify the total amount of iterations before exiting the loop, we can pass count to #tasks.loop()
#tasks.loop(seconds = random_timer, count=random.randint(15,35))
To change the interval timing of our loop during iteration, You can use function.change_interval()
#tasks.loop(seconds = random_timer, count=random.randint(15,35))
async def hello():
await channel.send("Hello!")
hello.change_interval(seconds=timer + random.randint(1, 20))

Related

How can I run a while loop which edits a aiosqlite database but is not in a async function

if True:
import shortcuts.startupload
from shortcuts.startupload import *
import threading
import time
**def background():
while True:
if 1 == 1:
if 1 == 1:
gwtimes = await aiosqlite.connect('gwtimes.db')
async with gwtimes.cursor() as gwcursor:
await gwcursor.execute("UPDATE gwtimes SET time=time+50")
await gwtimes.commit()
async with gwtimes.cursor() as gwcursor:
await gwcursor.execute("SELECT time FROM gwtimes")
number = await gwcursor.fetchone()
print(number)**
def foreground():
(other code)
b = threading.Thread(name='background', target=background)
f = threading.Thread(name='foreground', target=foreground)
b.start()
f.start()
bot.run(hidden token)
Above is most of the whole thing
I need it to (in the background) add 50 to all values in the column every 1 second and then print the time so I can test its working, however I dont get how to use async and await with this as the error is saying:
gwtimes = await aiosqlite.connect('gwtimes.db')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: 'await' outside async function```

Discord Bot Looping Task from Command

Good afternoon all,
I should preface this post with the statement that I am pretty new to Python and know enough to get myself into trouble, but not enough to always get out of trouble...this is one of those instances I can't get out.
I am attempting to create a Discord Bot using discord.py that has the ultimate goal of being started from a command in Discord, starts counting the days since the start command was issued, and sends a message to Discord every morning with the count. I also would have a command to reset the count and another to cancel the counter.
For testing purposes, I have created code to count minutes instead of days and a task loop of only 10 seconds rather than a full day.
My problem is that I am attempting to use a discord.ext task for the loop, but I clearly don't know how to use it properly, and my research online has not made it any more clear to me. I'm hoping some folks here can steer me in the right direction. I have supplied my code below.
What I expect to happen when I execute my code:
I issue the $start arg command and the bot sends a message of "It has been X minutes"
Every 10 seconds the bot sends the same message until 1 minute has passed, then message reads "It has been X+1 minutes"
Continue looping until canceled
What actually happens:
I issue the $start arg command and the bot sends a message of "It has been X minutes"
Nothing else. No other messages or anything happens in Discord, no errors shown in the console.
The code is currently being hosted on replit.com, hence the keep_alive function using UpTimeRobot to keep the bot alive.
Something to note, I originally used asyncio.sleep() to just wait to send the new message. This worked fine for shorter periods (like a few minutes to hours) but even with UpTimeRobot doing its thing, I can't get 100% uptime of the Bot and I think whenever the bot went offline for a few minutes, it stopped my counter loop and that was that. This is what lead me to look into Tasks, as I read they can be used to continue loops on reconnection if the bot goes offline for a bit.
import discord
import os
import keep_alive
from datetime import date, datetime, time, timedelta
from discord.ext import commands, tasks
bot = commands.Bot(command_prefix="$")
init_count = 0
count = 0
channel_id = My_Channel_ID_Here
#bot.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
#tasks.loop(seconds=10, count=None, reconnect=True)
async def min_counter():
global count
global init_count
count = init_count
today = datetime.today()
now = datetime.today()
channel = bot.get_channel(channel_id)
if today.minute != now.minute:
# increment count by 1
count = count + 1
today = now
print('Count: ' + str(count))
await channel.send('It Has Been ' + str(count) + ' Minutes.') # Send message of minutes count
#bot.command() # Command to start the counter
async def start (ctx, arg: int): # arg is initial number of minutes to start with
global init_count
init_count = arg
await ctx.send('It Has Been ' + str(init_count) + ' Minutes.') # Send message of minutes count
min_counter.start()
#bot.command() # Command to reset counter
async def reset (ctx):
global count
count = 0
await ctx.send('It Has Been 0 Minutes.') # Send message that count is not zero
#bot.command() # Command to stop the counter
async def stop (ctx):
min_counter.cancel()
await ctx.send('Counter Stopped.')
keep_alive.keep_alive() # keep alive function call
bot.run(os.getenv('TOKEN')) # Discord bot private token call
Upon quick inspection you are looping the method min_counter() but every time it is called you are changing the values of now and today to datetime.today().
So when you go to compare the values here:
if today.minute != now.minute:
# increment count by 1
count = count + 1
today = now
print('Count: ' + str(count))
await channel.send('It Has Been ' + str(count) + ' Minutes.') # Send message of minutes count
This will always evaluate to False.
Easy Fix
Although I don't like the use of globals too much, here is how we could fix this keeping the style you have now.
Start the counter and initialize a variable named start_time in global scope:
#bot.command() # Command to start the counter
async def start (ctx, arg: int): # arg is initial number of minutes to start with
global init_count, start_time
start_time = datetime.today()
await ctx.send('It Has Been ' + str(init_count) + ' Minutes.') # Send message of minutes count
min_counter.start()
Then check if start_time is equal to now in the loop:
#tasks.loop(seconds=10, count=None, reconnect=True)
async def min_counter():
global count, start_time, init_count
now = datetime.today()
channel = bot.get_channel(channel_id)
if start_time != now:
# increment count by 1
count = count + 1
print('Count: ' + str(count))
await channel.send('It Has Been ' + str(count) + ' Minutes.') # Send message of minutes count
Let me know if everything works for you, I'd be happy to help further if needed.

Issues with making a simple loading icon inside an async event loop

I'm making a bot for a discord server and have a function that takes a bit of time to run. I want to add a spinning loading icon next to the status message like this Doing something: <spinning icon>. It edits the original message to loop through these messages:
Doing something: \
Doing something: |
Doing something: /
Doing something: -
I tried using a separate thread to update the message like this:
async def loadingBar(ctx, message : discord.Message):
loadingMessage0 = "{0}: \\".format(message)
loadingMessage1 = "{0}: |".format(message)
loadingMessage2 = "{0}: /".format(message)
loadingMessage3 = "{0}: -".format(message)
index = 0
while True:
if(index == 0):
await message.edit(contents = loadingMessage0)
index = 1
elif(index == 1):
await message.edit(contents = loadingMessage1)
index = 2
elif(index == 2):
await message.edit(contents = loadingMessage2)
index = 3
elif(index == 1):
await message.edit(contents = loadingMessage1)
index = 0
farther down, the bot command that starts the process...
#bot.command()
async def downloadSong(ctx, url : str, songname : str):
#Other code that doesn't matter
message = await ctx.send("Downloading audio")
_thread = threading.Thread(target=asyncio.run, args=(loadingBar(ctx, message),))
_thread.start()
#Function that takes a while
#Some way to kill thread, never got this far
However, I get the error Task <Task pending coro=<loadingBar() running at bot.py:20> cb=[_run_until_complete_cb() at /Users/user/.pyenv/versions/3.7.3/lib/python3.7/asyncio/base_events.py:158]> got Future <Future pending> attached to a different loop. I'm new to async programming and the discord libraries; Is there a better way to do this and if not what am I doing wrong?
Firstly, you should add a delay between iterations inside the while loop, use asyncio.sleep for this.
Secondly - asyncio and threading doesn't really work together, there's also no point in using threading here since it defeats the whole purpose of asyncio, use asyncio.create_task to run the coroutine "in the background", you can asign it to a variable and then call the cancel method to stop the task.
import asyncio
async def loadingBar(ctx, message : discord.Message):
loadingMessage0 = "{0}: \\".format(message)
loadingMessage1 = "{0}: |".format(message)
loadingMessage2 = "{0}: /".format(message)
loadingMessage3 = "{0}: -".format(message)
index = 0
while True:
if(index == 0):
await message.edit(contents = loadingMessage0)
index = 1
elif(index == 1):
await message.edit(contents = loadingMessage1)
index = 2
elif(index == 2):
await message.edit(contents = loadingMessage2)
index = 3
elif(index == 1):
await message.edit(contents = loadingMessage1)
index = 0
await asyncio.sleep(1) # you can edit the message 5 times per 5 seconds
#bot.command()
async def downloadSong(ctx, url : str, songname : str):
message = await ctx.send("Downloading audio")
task = asyncio.create_task(loadingBar(ctx, message)) # starting the coroutine "in the background"
# Function that takes a while
task.cancel() # stopping the background task

Python Asyncio - running multiple infinite loops with different "pauses"

Im trying to figure out how to run multiple infinite loops with asyncio - each loop with it's own delays:
import asyncio
async def do_something(delay, message):
await asyncio.sleep(delay)
print(message)
def main():
loop = asyncio.get_event_loop()
loop.create_task(do_something(1, "delay equals 1"))
loop.create_task(do_something(3, "delay equals 3"))
loop.run_forever()
if __name__ == '__main__':
try:
main()
except Exception as f:
print('main error: ', f)
It returns:
delay equals 1
delay equals 3
and I would suspect it to return:
delay equals 1
delay equals 1
delay equals 1
delay equals 3
delay equals 1
delay equals 1
delay equals 3
(or similar)
How should I modify this simple routine?
SOLUTION
async def do_something(delay, message):
while True:
await asyncio.sleep(delay)
print(message)
There's no reason why a simple task would loop forever.
Depending on what you eventually want to do, you can add a while True: in the async functions, or have them schedule another task at the end.

Starting a new async function while the old one is still running

The all function creates a list of stocks who have no available shares. It then runs alert, a looping function, to alert me when the the stock becomes available. I need to run all these alert loops simultaneously but all waits for the first stock to become available before starting the next loop.
I've tried using threading to create a thread for each stock but I cannot await a Thread.start()
async def all(self, ctx):
stocks = requests.get(f'https://api.torn.com/torn/?
selections=stocks&key={api}').json()['stocks']
zero = []
acronymz = []
for items in stocks:
if stocks[items]['available_shares'] == 0:
zero.append(items)
acronymz.append(stocks[items]['acronym'])
await ctx.send(f'Zero: {zero}')
for acronyms in zero:
print(acronyms)
# Thread(target=alert, args=(ctx, acronyms)).start()
await alert(ctx, acronyms)
# await asyncio.sleep(0.5)
async def alert(ctx, items):
stocks = requests.get(f'https://api.torn.com/torn/?selections=stocks&key={api}').json()['stocks'][items]
if stocks['available_shares'] == 0:
await ctx.send(f'I am now watching {stocks["acronym"]}. I will let you know when there are shares available!')
while stocks['available_shares'] == 0:
stocks = requests.get(f'https://api.torn.com/torn/?selections=stocks&key={api}').json()['stocks'][items]
print(stocks)
await asyncio.sleep(5)
await ctx.send(f'There are {stocks["available_shares"]} in {stocks["acronym"]}')
stocks = https://pastebin.com/FhuR4d4R ["stocks"]
You can schedule tasks for the event loop without awaiting them immediately. Here's an example using asyncio.gather
await asyncio.gather(*(alert(ctx, acronyms) for acronyms in zero))

Categories

Resources