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
Related
Can't pass value from one function to another
i am using aiogram and FSM to pass
data['start_name2'] = message.text
from one function to another
who can tell me what is my mistake?
This function creates a state in the "Start" state group with the name "start_name2".
class Start(StatesGroup):
start_name2= State() #NAME
This is a telegram bot function that handles a callback query with the data 'NAME'. When triggered, the bot will send a message to the chat asking the user to write their name. The input name is then processed and sent to a GET API request, and the received information is then parsed and displayed in a limited form as messages in the chat. If the input name is invalid, the bot will send a message saying so. The function also adds a message with a link to a website at the end.
#dp.callback_query_handler(lambda c: c.data == 'NAME')
async def process_callback_button2(callback_query: types.CallbackQuery, state: FSMContext):
await bot.answer_callback_query(callback_query.id)
await bot.send_message(callback_query.message.chat.id, text='Please write name. Example : Alex Bortin',reply_markup=kb.inline_kb4)
await Start.start_name2.set()
try:
await callback_query.message.delete()
except Exception as exception:
print("someone click button")
#dp.message_handler(state=Start.start_name2)
async def namee(message: types.Message, state: FSMContext):
# Check if the input is valid
if not is_valid_input2(message.text):
await bot.send_message(message.from_user.id, "Invalid input. Please enter a valid name.")
return
name = message.text
async with state.proxy() as data:
data['start_name2'] = message.text
try:
msg = await bot.send_message(message.from_user.id, parse_mode="HTML" ,text=f'Entered name: <b>{name}</b>\n\nSearching...')#---
await bot.send_chat_action(message.from_user.id , ChatActions.TYPING)#---
for i in range(3):#---
random_emojis = random.sample(emojis, 4)#---
random_emojis_string = " ".join(random_emojis)#---
await asyncio.sleep(0.4)
await msg.edit_text(parse_mode="HTML" ,text=f'Entered name: <b>{name}</b>')#---
URL = "API"
URL2 = "JUST URL"
# GET
response_text = await fetch(URL)
soup = BeautifulSoup(response_text, "html.parser")
nameD = (soup.prettify())
print(nameD)
await msg.edit_text(parse_mode="HTML" ,text=f'Entered name: <b>{name}</b>') #---
pattern_value = r'"value"\s*:\s*(\d+),'
obj = json.loads(nameD)
data2 = ""
match_address = re.search(pattern_value, nameD)
if match_address:
value = match_address.group(1)
data2 += f'Data found: <b>{value}</b>\n'
else:
data2 += 'Data found: <b>0</b>\n'
count = 0
for index in obj['hits']['hits']:
if count >= 10:
break
count += 1
db_name = index['_index']
strings = set()
for a, b in index['_source'].items():
strings.add(f"<b>{a}</b> : {b}")
string = "\n".join(list(strings))
await bot.send_message(message.from_user.id, text=f"<b>data base name</b> : {db_name}\n{string}", parse_mode="HTML")
await message.reply('Page 0', reply_markup=get_keyboard(0))
text2 = hlink('website', URL2)
await bot.send_message(message.from_user.id, parse_mode="HTML" , text=f'\nThe report can be viewed on our WEB {text2}')
name = 0
except Exception as exception:
name = 0
text2 = hlink('website', URL2)
await bot.send_message(message.from_user.id, parse_mode="HTML" , text=f'\nThe report can be viewed on our WEB {text2}')
await state.finish()
This function is a callback handler for a callback query in the Telegram Bot API. It handles a vote "up" action, increasing the vote count by 1. It logs the callback data and retrieves a value from the state proxy (stored data). It also prints the retrieved value.
#dp.callback_query_handler(vote_cb.filter(action='up'))
async def vote_up_cb_handler(query: types.CallbackQuery, callback_data: dict, state: FSMContext):
logging.info(callback_data)
amount = int(callback_data['amount'])
amount += 1
async with state.proxy() as data:
start_name2 = data.get('start_name2')
print(start_name2)
#Here I try print start_name2
The maximum that I have achieved is that my last function outputs
"None"
So, I have a command that should give me an image and after that bot should ask whether user wants to keep it or not (I use thumbs-up reaction for the answer). If user reacts with 'thumbs-up', then bot says 'Saved!' and actually save the image URL to a .txt file. The problem is if user doesn't react with thumbs up and after that types command again and save this latest image, bot saves previous ones too. I use Replit for hosting. Here is the code block that doesn't work as expected:
#bot.command()
async def myfunc(ctx):
... # here goes the message attributes, requests (no problem with the requests)
thumb_up = "π"
thumb_down = "π"
imagesList = []
await message.add_reaction(thumb_up)
userDict = {}
n = 0
while True:
try:
reaction, user = await bot.wait_for("reaction_add", timeout=30)
if user not in userDict:
userDict[user] = 0
if userDict[user] == 0:
if (str(reaction.emoji) == thumb_up and user != bot.user):
imagesList.append(url)
fileWrite = open(f"data{user}.txt", "a")
fileWrite.write(f"{url} \n")
fileWrite.close()
await ctx.send("Saved!")
userDict[user] += 1
imagesList.clear()
except:
pass
imagesList.clear()
#bot.event
async def on_reaction_add(reaction, user):
embed = reaction.message.attachments
emoji = reaction.emoji
if emoji == ":thumbsup:":
await reaction.channel.send(embed)
It shouldn't save previous URL's to users file. It should save only URL's that the user reacted with 'thumbs-up'. I use dict to avoid that but didn't work.
Okay, so I am not much familiar with discord.py and have no clue why it writes multiple times, but I think the problem is if the message id that the user reacted is the message user reacted, I mean if there are multiple lines get written, we should discard or not to use previous of them. That's why I created a check function:
msgId = message.id
def check(reaction, user):
if user != bot and reaction.emoji == thumb_up and reaction.message.id == msgId :
return True
else:
return False
userDict = {}
while True:
reaction, user = await bot.wait_for("reaction_add", check=check, timeout=30)
if(user.id not in userDict.keys() and user.id != <id of bot>):
userDict[user.id] = 0
if(reaction.emoji == thumb_up and user.id != <id of bot> and userDict[user.id] == 0 ):
userDict[user.id] = 1
fileWrite = open(f"data{user}.txt", "a")
fileWrite.write(f"{url} \n")
fileWrite.close()
await ctx.send(f"Saved {user}")
userDict[user.id] = 1
In my bot, the user has to enter the date of birth, then I have to transfer this date into a set function, the result of which is later inserted as an index into a list, from where the corresponding image will be sent to the user.
Now I will show you a part of the code, it's an example of both the handler and the function, through which I want to run the user's message. As long as you use a randomly generated number and not the result from the function, everything works perfectly. The thing is that I need exactly the result you get through the calculations and for that I need to insert the message sent by the user into a variable, so that you could continue working with it - and that's exactly what I fail to accomplish.
The Question: how do you assign the userβs message to a variable in aiogram in order to further interact with it?
#dp.message_handler(commands=['start'])
async def begin(message: types.Message):
await bot.send_message(message.from_user.id, f"{text1}")
#dp.message_handler()
async def datarozh(message: types.Message):
if message.text[2] == '/' and message.text[5] == '/' and len(message.text) == 10:
await bot.send_message(message.from_user.id, f"blablabla", reply_markup=mainmenu)
elif message.text == 'Begin':
chatid = message.from_user.id
arkankart = InputFile(path_or_bytesio=f"{img_list[result_def]}") # this is where you need to substitute the value from the function after processing the user's message
await dp.bot.send_photo(chat_id=chatid, photo=arkankart, caption='blablabla')
data = "11/11/2011" # the plan is to use the user's message instead of data
s = data.split('/')
lisst = []
for i in s:
lisst.append(int(i))
def day(a): # I have several different variations of the functions through which I would run the user's message, here's one of them
result = 0
while a[0] != 0:
b = a[0] % 10
result += b
a[0] = a[0] // 10
return result
You can use function message.text to check the user's message.
#dp.message_handler()
async def send_answer(message: types.Message):
if message.text == "Test1":
print("Cool!")
So if you would to assign user's message to a variable:
#dp.message_handler()
async def send_answer(message: types.Message):
your_variable = message.text
if your_variable == "Test1":
print("Cool!")
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')
tl;dr
How can my bot asynchronously wait for reactions on multiple messages?
I'm adding a rock-paper-scissors (rps) command to my Discord bot. Users can invoke the command can be invoked by entering .rps along with an optional parameter, specifying a user to play with.
.rps #TrebledJ
When invoked, the bot will direct-message (DM) the user who invoked it and the target user (from the parameter). The two users then react to their DM with either β, π, or βοΈ.
Now I'm trying to get this working asynchronously. Specifically, the bot will send DMs to both users (asynchronously) and wait for their reactions (asynchronously). A step-by-step scenario:
Scenario (Asynchronous):
1. User A sends ".rps #User_B"
2. Bot DMs User A and B.
3. User A and B react to their DMs.
4. Bot processes reactions and outputs winner.
(See also: Note 1)
Since the goal is to listen to wait for reactions from multiple messages, I tried creating two separate threads/pools. Here were three attempts:
multiprocessing.pool.ThreadPool
multiprocessing.Pool
concurrent.futures.ProcessPoolExecutor
Unfortunately, all three didn't work out. (Maybe I implemented something incorrectly?)
The following code shows the command function (rps), a helper function (rps_dm_helper), and the three (unsuccessful) attempts. The attempts all make use of different helper functions, but the underlying logic is the same. The first attempt has been uncommented for convenience.
import asyncio
import discord
from discord.ext import commands
import random
import os
from multiprocessing.pool import ThreadPool # Attempt 1
# from multiprocessing import Pool # Attempt 2
# from concurrent.futures import ProcessPoolExecutor # Attempt 3
bot = commands.Bot(command_prefix='.')
emojis = ['β', 'π', 'β']
# Attempt 1 & 2
async def rps_dm_helper(player: discord.User, opponent: discord.User):
if player.bot:
return random.choice(emojis)
message = await player.send(f"Playing Rock-Paper-Scissors with {opponent}. React with your choice.")
for e in emojis:
await message.add_reaction(e)
try:
reaction, _ = await bot.wait_for('reaction_add',
check=lambda r, u: r.emoji in emojis and r.message.id == message.id and u == player,
timeout=60)
except asyncio.TimeoutError:
return None
return reaction.emoji
# # Attempt 3
# def rps_dm_helper(tpl: (discord.User, discord.User)):
# player, opponent = tpl
#
# if player.bot:
# return random.choice(emojis)
#
# async def rps_dm_helper_impl():
# message = await player.send(f"Playing Rock-Paper-Scissors with {opponent}. React with your choice.")
#
# for e in emojis:
# await message.add_reaction(e)
#
# try:
# reaction, _ = await bot.wait_for('reaction_add',
# check=lambda r, u: r.emoji in emojis and r.message.id == message.id and u == player,
# timeout=60)
# except asyncio.TimeoutError:
# return None
#
# return reaction.emoji
#
# return asyncio.run(rps_dm_helper_impl())
#bot.command()
async def rps(ctx, opponent: discord.User = None):
"""
Play rock-paper-scissors!
"""
if opponent is None:
opponent = bot.user
# Attempt 1: multiprocessing.pool.ThreadPool
pool = ThreadPool(processes=2)
author_result = pool.apply_async(asyncio.run, args=(rps_dm_helper(ctx.author, opponent),))
opponent_result = pool.apply_async(asyncio.run, args=(rps_dm_helper(opponent, ctx.author),))
author_emoji = author_result.get()
opponent_emoji = opponent_result.get()
# # Attempt 2: multiprocessing.Pool
# pool = Pool(processes=2)
# author_result = pool.apply_async(rps_dm_helper, args=(ctx.author, opponent))
# opponent_result = pool.apply_async(rps_dm_helper, args=(opponent, ctx.author))
# author_emoji = author_result.get()
# opponent_emoji = opponent_result.get()
# # Attempt 3: concurrent.futures.ProcessPoolExecutor
# with ProcessPoolExecutor() as exc:
# author_emoji, opponent_emoji = list(exc.map(rps_dm_helper, [(ctx.author, opponent), (opponent, ctx.author)]))
### -- END ATTEMPTS
if author_emoji is None:
await ctx.send(f"```diff\n- RPS: {ctx.author} timed out\n```")
return
if opponent_emoji is None:
await ctx.send(f"```diff\n- RPS: {opponent} timed out\n```")
return
author_idx = emojis.index(author_emoji)
opponent_idx = emojis.index(opponent_emoji)
if author_idx == opponent_idx:
winner = None
elif author_idx == (opponent_idx + 1) % 3:
winner = ctx.author
else:
winner = opponent
# send to main channel
await ctx.send([f'{winner} won!', 'Tie'][winner is None])
bot.run(os.environ.get("BOT_TOKEN"))
Note
1 Contrast the asynchronous scenario to a non-asynchronous one:
Scenario (Non-Asynchronous):
1. User A sends ".rps #User_B"
2. Bot DMs User A.
3. User A reacts to his/her DM.
4. Bot DMs User B.
5. User B reacts to his/her DM.
6. Bot processes reactions and outputs winner.
This wasn't too hard to implement:
...
#bot.command()
async def rps(ctx, opponent: discord.User = None):
"""
Play rock-paper-scissors!
"""
...
author_emoji = await rps_dm_helper(ctx.author, opponent)
if author_emoji is None:
await ctx.send(f"```diff\n- RPS: {ctx.author} timed out\n```")
return
opponent_emoji = await rps_dm_helper(opponent, ctx.author)
if opponent_emoji is None:
await ctx.send(f"```diff\n- RPS: {opponent} timed out\n```")
return
...
But IMHO, the non-async makes for bad UX. :-)
You should be able to use asyncio.gather to schedule multiple coroutines to execute concurrently. Awaiting gather waits for all of them to finish and returns their results as a list.
from asyncio import gather
#bot.command()
async def rps(ctx, opponent: discord.User = None):
"""
Play rock-paper-scissors!
"""
if opponent is None:
opponent = bot.user
author_helper = rps_dm_helper(ctx.author, opponent) # Note no "await"
opponent_helper = rps_dm_helper(opponent, ctx.author)
author_emoji, opponent_emoji = await gather(author_helper, opponent_helper)
...