I have this command:
if message.content.lower().startswith("!activate"):
if message.author.id not in tempo:
await Bot.send_message(message.channel, "{} used the command".format(message.author.name))
tempo.append(message.author.id)
await asyncio.sleep(720)
tempo.remove(message.author.id)
else:
await Bot.send_message(message.channel, "wait {} hours.".format(asyncio.sleep))
I would like every time the person tried to use the command a second time, show how much time is left before they can use it again, something like: "wait 4 hours and 50 minutes."
what should I do?
You can just store the time and calculate the remaining yourself:
import time
tempo = {} # use a dict to store the time
if message.content.lower().startswith("!activate"):
if message.author.id not in tempo:
await Bot.send_message(message.channel, "{} used the command".format(
message.author.name))
tempo[message.author.id] = time.time() + 720 # store end time
await asyncio.sleep(720)
del tempo[message.author.id]
else:
await Bot.send_message(message.channel, "wait {} seconds.".format(
tempo[message.author.id] - time.time()))
Related
I've got a program that is pulling UPD data being transmitted by my home weather station. One of those values that is a "rain_accum" which is the total rain accumulation in the past minute. When that "rain_accum" is > 0, I want the light to blink. When the rain_accum == 0, I want it to not blink.
I'm fairly new to Python and very new to uasyncio (note: I'm using Micropython, so I may not have all the capablities of asyncio) and I'm just getting lost. I've been messing with this for 3 days and have actually gotten pretty close to getting it working, but the timing was way off the blink would take several seconds and UDP messages were getting missed. I've cut my code back to the bare basics of what I've been trying to do hoping I could get some direction as to where to go with this.
import uasyncio
async def blink():
while True:
print("Here 1")
await uasyncio.sleep(1)
print("Here 2")
await uasyncio.sleep(1)
async def getData():
while True:
print("Getting data")
if True:
await blink()
print("Next loop")
await uasyncio.sleep(5)
try:
uasyncio.run(getData())
except KeyboardInterrupt:
print("Quitting")
In the example in your question, once you call await blink() you never return: your current coroutine has entered an infinite loop.
Take a look at the example from the documentation, which shows how to start two coroutines concurrently. If we were to take that examples and fit it to the example from your question, we might get something like:
import random
import time
import uasyncio
blink_active = False
async def blink():
while True:
if blink_active:
print("Here 1")
await uasyncio.sleep(1)
print("Here 2")
await uasyncio.sleep(1)
else:
await uasyncio.sleep(1)
async def getData():
global blink_active
# Start a concurrent async task
uasyncio.create_task(blink())
last_check = 0
while True:
now = time.time()
if now - last_check > 5:
data = random.randint(0, 10)
print("Got data", data)
if data > 7:
print("Start blinking")
blink_active = True
else:
print("Stop blinking")
blink_active = False
last_check = now
print("Next loop")
await uasyncio.sleep(1)
try:
uasyncio.run(getData())
except KeyboardInterrupt:
print("Quitting")
Here, getData() starts a blink task that will run forever. Whether it "blinks" (or prints out text) is controlled by a global variable, blink_active.
In getData, our loop runs once/second, but we only gather new data every five seconds. Depending on the value we retrieve from our "sensor", we start or stop the blinker by setting blink_active appropriately.
I was able to make a countdown timer that edits itself. However When I do multiple timers, one of the timer will either lag, or they all break.
How do i make it so that when someone starts a timer, that timer will start working on its own and any other action won't affect that timer. This way multiple timers can be made.
To start a timer I would just type //timer (time)
import discord
import os
import asyncio
client = discord.Client()
#client.event
async def on_ready(): #Print when The Code Has connected to Discord
print("{0.user} Has Connected To Discord".format(client))
#client.event
async def on_message(message):
Days = 0
Hours = 0
Minutes = 0
Seconds = 0
ClockH = "00"
ClockM = "00"
ClockS = "00"
if message.author == client.user:
if ("⌛COUNTDOWN⌛") in message.content:
timer = message.content.split()
timer = list(map(int,timer[1:]))
Days = timer[0]
Hours = timer[1]
Minutes = timer[2]
Seconds = timer[3]
Finish = False
while Finish == False:
if Hours <10: ClockH = "0"+str(Hours)
else: ClockH = str(Hours)
if Minutes <10: ClockM = "0"+str(Minutes)
else: ClockM = str(Minutes)
if Seconds <10: ClockS = "0"+str(Seconds)
else: ClockS = str(Seconds)
if Days == 0:
await message.edit(content="{}:{}:{}".format(ClockH,ClockM,ClockS))
else:
await message.edit(content="{} Days | {}:{}:{}".format(Days,ClockH,ClockM,ClockS))
if Minutes>0:
if Seconds>0: Seconds-=1
else:
Minutes-=1
Seconds = 59
elif Seconds>0:
Seconds-=1
else: Finish = True
await asyncio.sleep(1)
return
if "//timer" in message.content:
timer = message.content.split()
countdown = []
timer = timer[1:]
for i in range(len(timer)):
if ("day" in timer[i].lower()):
Days = int(timer[i-1])
if ("hour" in timer[i].lower()) or ("hr" in timer[i].lower()):
Hours = int(timer[i-1])
if "min" in timer[i].lower():
Minutes = timer[i-1]
if "sec" in timer[i].lower():
Seconds = int(timer[i-1])
if ":" in timer[i]:
timer = timer[i]
index = 0
for i in range(3):
if ":" in timer:
index = timer.find(":")
if len(timer[:index]) <= 2:
countdown.append(int(timer[:index]))
timer = timer[index+1:]
else:
Error = "Please Retype Timer"
else: countdown.append(int(timer))
Hours,Minutes,Seconds = countdown
await message.channel.send("⌛COUNTDOWN⌛ {} {} {} {}".format(Days,Hours,Minutes,Seconds))
print(Days, Hours,Minutes,Seconds)
client.run(os.environ['Token'])
It's no longer necessary to edit the timestamp messages yourself, and it's very likely to be considered API abuse when you do this - bots are not supposed to do x action every y time.
Instead, you can use discord's own timer in your messages. To use a timer, you simply put <t:12345:R> with a number there that represents the timestamp. It also looks nicer and can show detailed information when you hover over it.
#client.command()
async def countdown(ctx, hour: int, minute: int, second: int):
total_time = 3600*hour + 60*minute + second
timestamp = int(time.time()) + total_time
await ctx.send(f'<t:{timestamp}:R>')
(To do this without using commands, change ctx.send to message.channel.send, and use your own definition of the total time in seconds.)
Output:
So my discord bot in python has the task to send an embed as soon as the user sends the message "$start". After this the code is starting a while loop and checking if a user has reacted. Simultaneously I want to edit the message of the bot every second so I can display some kind of timer to show the users how much time they have left to react but I dont know how to implement a timer running at the same time. Would it be useful to use multiprocessing for this one?
Here is my very specific code if anyone needs it to answer my question :)
#client.command()
async def start(ctx):
timer = 120
remTime = timer
seedCapital = 10000
sessionEmpty = True
players[ctx.author] = seedCapital
em = discord.Embed(title = "New Poker Session", description = "Waiting for players to join ...\nreact with 💸 to join or ▶️ to start (only host)", color = discord.Color.green())
#em.add_field(name = "2 minutes remaining from now on!", value = "")
botMsg = await ctx.send(embed = em)
await botMsg.add_reaction("💸")
await botMsg.add_reaction("▶️")
while True:
mom0 = time.perf_counter()
try:
reaction, user = await client.wait_for('reaction_add', timeout = remTime, check = lambda reaction, user: reaction.emoji in ["💸", "▶️"])
#msg = await client.wait_for('message', timeout = remTime, check = lambda m: m.channel == ctx.channel)
#print(f"receiving message: {msg.content}")
except asyncio.TimeoutError:
break
if reaction.emoji == "💸":
sessionEmpty = False
await ctx.send(f"{user.mention} joined the session!")
players[user] = seedCapital
elif user == ctx.author and reaction.emoji == "▶️":
break
mom1 = time.perf_counter()
neededTime = mom1 - mom0
remTime -= neededTime
print(remTime)
if not sessionEmpty:
await ctx.send("starting session ...")
#start session
else:
await ctx.send("Noone joined your session :(")
We can use asyncio.gather to run coroutines concurrently.
#after command
from datetime import datetime, timedelta
end_time = datetime.now() + timedelta(seconds=30) #30 seconds to react
async def coro():
for i in range(30, 0, 5):
await botMsg.edit(f'time remaining: {i}') #send another message if you don't want old content to be erased
await asyncio.sleep(5)
async def coro2():
while datetime.now() <= endtime:
#do stuff
await asyncio.gather(coro(), coro2())
Note: There might be a small delay between the completion of the two coroutines.
References:
asyncio.gather
Edit: Here is the small delay I mentioned
I am writing a discord bot with discord.py and am trying to implement a system to buy stocks in, on python 3.6 as python3.7+ has asyncio.run(func()).
import discord
import asyncio
import os
import random
import time
import math
client = discord.Client()
# contains coin information including balance, stock price, number of stocks, etc
with open('coins.conf', 'r') as f:
for line in f.readlines():
exec(line)
def save_all():
with open('coins.conf', 'w') as f:
f.write('coins = '+repr(coins))
random.seed(os.urandom(32))
searchusers = []
bank_cooldown = {}
bans['global'] = False
#client.event
async def on_ready():
'''Notification on ready.'''
print('Logged in! Bot running.')
await client.change_presence(activity=discord.Game(name='/help'))
#client.event
async def on_member_join(user):
'''Direct message the rules on member join.'''
await user.create_dm()
await user.dm_channel.send(f'Hi **{user.name}**, welcome to the server! Be sure to read the rules to stay out of trouble. Have a great time!')
def getcoins(uid):
'''Get the amount of coins, if nonexistent set to 0.'''
try:
return coins[uid][0]
except Exception:
coins[uid] = [0, time.time()+1, time.time()+1, 0, 320, time.time()+600, 0]
return 0
def mention_to_uid(mention):
'''Extract the UID from a mention (exclude first two char and last char)'''
uid = mention[2:-1]
if uid[0] == '!':
uid = uid[1:]
return uid
def setcoins(uid, value):
'''Set the amount of coins someone has.'''
try:
coins[uid][0] = value
except Exception:
coins[uid] = [value, time.time()+1, time.time()+1, 0, 320, time.time()+600, 0]
#client.event
async def on_message(message):
'''Main bot code running on message.'''
#############################
# some initialization stuff #
#############################
if message.content.startswith('/'):
user = message.author.id
name = message.author.display_name
text = message.content[1:].strip()
command = text.split(' ')[0]
subcommand = text.split(' ')[1:]
######################
# other bot commands #
######################
if command == 'stocks':
if len(subcommand) < 2:
await message.channel.send('Missing arguments! `/stocks [buy | sell] <amount>` or `/stocks help [info | price]`')
elif subcommand[0] == 'help':
if subcommand[1] == 'info':
msg = 'Invest in bot stocks! Use `/stocks help price` to find the current price per share.'
msg += '\nEvery 7-15 minutes, the stock price will change. It will decrease or increase by a little bit.'
msg += '\nStocks will more than likely output profit, but it is very random.'
msg += '\nEvery share bought increases the price by 1 and every share sold decreases the price by 1.'
await message.channel.send(msg)
elif subcommand[1] in ('price', 'amount', 'cost'):
await message.channel.send(f"The current stock price is **{getcoins('stock')}**. Get them while you can!")
elif subcommand[0] == 'buy':
amount = int(subcommand[1])
if amount < 0:
await message.channel.send('lol dummy. Positive number dude.')
else:
cost = (amount * getcoins('stock')) + (amount**2 - amount)//2 # price including increases
if getcoins(user) < cost:
await message.channel.send(f"You don't have enough coins to buy that many shares! Try `/balance`.\n[Debug] {cost}")
else:
coins[user][6] += amount # coins[user][6] => number of stocks user has
setcoins(user, getcoins(user)-cost)
setcoins('stock', getcoins('stock')+amount)
await message.channel.send(f'You bought {amount} shares for {cost} coins.')
elif subcommand[0] == 'sell':
amount = int(subcommand[1])
if amount < 0:
await message.channel.send('lol dummy. Positive number dude.')
else:
sell = (amount * getcoins('stock')) - (amount**2 - amount)//2 # price including decreases
if coins[user][6] < amount:
await message.channel.send(f"You don't have enough shares!")
else:
coins[user][6] -= amount
setcoins(user, getcoins(user)+sell)
setcoins('stock', getcoins('stock')-amount)
await message.channel.send(f'You sold {amount} shares for {sell} coins.')
else:
await message.channel.send('Invalid arguments! `/stocks [buy | sell] <amount>` or `/stocks help [info | price]`')
######################
# other bot commands #
######################
##################################
# this stuff is the main problem #
##################################
async def main():
'''Bot code.'''
await client.start('Nj*********************************************************')
while True:
await asyncio.sleep(random.randint(420,900))
## I'm certain anything below this doesn't run ##
change = random.choice([ *1*[-50], *4*[-8], *5*[-7], *6*[-6], *7*[-5], *8*[-4], *9*[-3], *10*[-2], *10*[-1],
*10*[0], *10*[1], *10*[2], *10*[3], *9*[4], *8*[5], *7*[6], *6*[7], *5*[8], *4*[9], *3*[10], *2*[12], *1*[15]
])
setcoins('stock', getcoins('stock')+change)
if getcoins('stock') < 15:
setcoins('stock', 15)
setcoins('jackpot', getcoins('jackpot')+random.randint(10, 20))
asyncio.get_event_loop().run_until_complete(main())
Only half of the function is running.
The line await client.start() is not supposed to be a blocking call, and anyway is awaited. Even after waiting 20 minutes (should maximum 15 minutes) the second part of the loop - the change = random.choice line and everything below it does not run.
I think the problem is the client.start call is blocking, but from the documentation (https://discordpy.readthedocs.io/en/latest/api.html#discord.Client.start)
await start(*args, **kwargs)
This function is a coroutine.
A shorthand coroutine for login() + connect().
Raises
TypeError – An unexpected keyword argument was received.
the start command is a coroutine.
I have tried lowering the asyncio.sleep time down to 10 seconds, and even removing it entirely. The stock price and ticket jackpot amount which is what is supposed to be modified does not change.
I'm trying to make a trivia bot and it has a command that makes it countdown from 30-1 in seconds. When it hits 1 I don't know how to make say something in the discord chat.
I've already tried looking through the API.
questionTimer = 30
#while questionTimer > 1:
#questionTimer = questionTimer - 1
#time.sleep(1)
#print (questionTimer)
I'm hoping that it can say Times up in the chat when questionTimer = 1
If you want it to print out every number you can do something like
#client.command()
async def quiz():
seconds = 30
while seconds > 0:
await client.say(seconds)
await asyncio.sleep(1)
seconds-=1
await client.say("Done")
But if you just want to make it wait 30 seconds and then display a message you can do something like
#client.command()
async def quiz():
await asyncio.sleep(30)
await client.say("Done")
Depending if you are using the rewrite or the old async version of discord.py I would recommend the following :
Discord.py async (0.16.x) :
#client.event
async def on_message(self, message):
if message.content.startswith('!quiz'):
quizMsg = 'Question of the quiz'
msg = await client.say(f"{quizMsg}\n\n{secs}s left !")
secs = 30
while secs > 0:
await asyncio.sleep(1)
await client.edit_message(msg, f"{quizMsg}\n\n{secs}s left !")
secs--
await client.say("Time is up ! The answer was...")
Discord.py rewrite (1.0.x) :
#commands.command(name="quiz", aliases=["q"])
async def quiz():
quizMsg = 'Question of the quiz'
msg = await ctx.send(f"{quizMsg}\n\n{secs}s left !")
secs = 30
while secs > 0:
await asyncio.sleep(1)
await msg.edit(content = f"{quizMsg}\n\n{secs}s left !")
secs--
await ctx.send("Time is up ! The answer was...")
Mind the difference between the two methods