I have a coroutine that if the variable event_type is started then it doesn't do anything but if the value is updated then do the coroutine again in 5 seconds.
import asyncio
import aioconsole
values = 0
async def send_transaction_event_request(event_type):
values = values + 1
if event_type == 'started':
print('The transaction started')
elif event_type == 'updated':
while event_type != 'ended' :
print(values)
await asyncio.sleep(5)
await send_transaction_event_request('updated')
else :
print('The transaction stopped')
async def console():
try:
logging.info("Console started")
while True:
comm = await aioconsole.ainput('>')
if 'send_transaction_event_request' in comm:
event_type = input('enter the event type :')
await asyncio.create_task(send_transaction_event_request(event_type))
except Exception as e:
logging.error("Caught error")
print(e)
await console()
I run my input asynchronously (which is working) and when I send the even_type equal to started or ended everything works fine but when i send the event_type equals to updated there's something wrong.
The problem is that the asyncio.sleep(5) blocks my asynchronous console so I can never send another request and I need to restart my program.
How can I solve this?
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.
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 made a discord bot that sends every 120 sec transactions from one ethereum addrs but i dont want to send same stuff over and over so if it send USDT token and Again in 120 sec try USDT to just skip it until it got new thing is that possble or not?
code:
import requests
import sys
import json
import discord
import time
btoken = "mytoken"
result=requests.get('https://api.ethplorer.io/getAddressHistory/0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be?apiKey=freekey&type=transfer')
result.status_code
result.text
result.json()
results = "soon:tm:"
def price_of_gas(inp):
def recursive_function(inp):
if type(inp) is list:
for i in inp:
ans = recursive_function(i)
if ans!=None: return ans
elif type(inp) is dict:
if 'name' in inp: return inp['name']
for i in inp:
ans = recursive_function(inp[i])
if ans!=None: return ans
else: return None
ans = recursive_function(inp)
return ans if ans else "Could NOT find the new token tx"
print (price_of_gas(result.json()))
class MyClient(discord.Client):
async def on_ready(self):
print('Logged on as', self.user)
async def on_message(self, message):
# don't respond to ourselves
if message.author == self.user:
return
if message.content == '.get':
await message.channel.send('Alert! Alert! Buy')
await message.channel.send(result.json()['operations'][0]['tokenInfo']['symbol'])
await message.channel.send(result.json()['operations'][0]['tokenInfo']['address'])
print ('get command was tryed')
else:
print ('comand not found')
if message.content == '.help':
await message.channel.send("try .get")
print ('help command was tryed')
if message.content == '.stop':
await message.channel.send('Bye...')
print('bye')
sys.exit()
if message.content == '.start':
while True:
# Code executed here
print ('done')
price_of_gas(result.json())
print (price_of_gas(result.json()))
await message.channel.send(price_of_gas(result.json()))
time.sleep(120)
#print(result.json()['operations'][0]['tokenInfo']['name'])
#print(result.json()['operations'][0]['tokenInfo']['symbol'])
#print(result.json()['operations'][0]['tokenInfo']['address'])
#print (result.json()['tokenSymbol'])
#print (result.text)
print ('done no errors')
print ('done no errors with check data')
client = MyClient()
client.run(btoken)
print ('done no errors 2')
so if user type .start bot will start while loop that sends newest tx from selected addrs but problem is it will send same stuff i just need some if statment in while loop or something.
You shoudn't use the requests module (it's blocking), you should use aiohttp instead
You also shoudn't use time.sleep as it also blocks the whole thread. You should use asyncio.sleep. (If you still stay with time.sleep you're not going to be able to use the bot when it's "sleeping")
Answering your question, you can simply have a variable with the value of the previous price/value and check if the new message is the same, if it's not - send it
while True:
price = price_of_gas(result.json())
# Checking if the `previous_price` var exists
if hasattr(self, "previous_price"):
# If yes, comparing the values
if self.previous_price != price:
# If they're not the same, send the message
await message.channel.send(f"Current price: {price}")
self.previous_price = price # Updating the variable
else:
# If the `previous_price` var doesn't exists, creating it
self.previous_price = price
await asyncio.sleep(120) # Remember to import asyncio
Making HTTP requests with aiohttp
import aiohttp
async def main():
async with aiohttp.ClientSession() as session:
async with session.get("URL") as resp:
data = await resp.json()
# Note: you should create ONE session per application
EDIT:
If you want to use blocking functions (like the price_of_gas) you can use the next method
await self.loop.run_in_executor(None, price_of_gas, result.json())
More info here
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.
import discord
import os
import random
import time
import math
client = discord.Client()
with open('admins.conf', 'r') as f:
for line in f.readlines():
exec(line)
with open('bans.conf', 'r') as f:
for line in f.readlines():
exec(line)
with open('coins.conf', 'r') as f:
for line in f.readlines():
exec(line)
random.seed(os.urandom(32))
searchusers = []
#client.event
async def on_ready():
'''Notification on ready.'''
print('Logged in! Bot running.')
await client.change_presence(activity=discord.Game(name='/help'))
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()+20]
return 0
def mention_to_uid(mention):
'''Extract UID from a mention'''
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()+20]
with open('coins.conf','w') as f:
f.write('coins = '+repr(coins))
#client.event
async def on_message(message):
'''Main bot code running on message.'''
if message.author == client.user:
return
if message.author.id in bans:
return
if message.content.startswith('/') or message.content.startswith('&'):
user = message.author.id
text = message.content[1:].strip()
command = text.split(' ')[0]
subcommand = text.split(' ')[1:]
if message.author.id in searchusers:
await message.channel.send('<#'+str(message.author.id)+'>, you cannot use bot commands while you are searching.')
return
-------------------- snip --------------------
if command == 'search':
await message.channel.send('<#'+str(user)+'>, you have begun searching! It will take 2 minutes.')
searchusers.append(user)
time.sleep(59.99)
await message.channel.send('<#'+str(user)+'>, there is 1 minute left.')
time.sleep(39.99)
await message.channel.send('<#'+str(user)+'>, there are 20 seconds left!')
time.sleep(14.99)
await message.channel.send('<#'+str(user)+'>, there are 5 seconds left!')
time.sleep(4.99)
found = random.randint(50, 120)
await message.channel.send('<#'+str(user)+'>, you are done searching! You found '+str(found)+' coins!')
setcoins(user, getcoins(user)+found)
searchusers.remove(user)
During the time.sleep() events other bot commands do not register until the sleep function has passed. For example doing a command like /help right after doing /search the bot will not respond until after one minute, when it will respond to /help and message there is 1 minute left. I have tried sticking "await" before each one of the sleep functions but it just spit out runtime warnings at me and stopped the execution (It just says you started searching and then nothing happens).
time.sleep() stops the entire execution of the program. If you just want to delay one async response thread use asyncio.sleep
Example:
import asyncio
async def wait():
await asyncio.sleep(5)
print('I waited 5 seconds')