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"
Related
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
I have been having issues with my queue feature as it would either delete two elements from the playlist dictionary at once when I skip a song using a command or it comes up with bugs when I let the songs finish- whenever I try to come up with a fix which would resolve both issues.
This is the relevant code -
def queueCheck(self, ctx, id, vc):
text = ""
if queues[id] != []: # if server player queue not empty
stream = queues[id].pop(0)
if self.skipping == True:
current[id] = tracks[id].pop(0)
if len(tracks[id]) != 0:
text = "Now playing **" + tracks[ctx.message.guild.id][0] + "**"
self.skipping = False
vc.play(stream, after=lambda e: self.queueCheck(ctx, id, vc))
else:
text = "Queue has finished playing!"
coro = ctx.send(text)
fut = asyncio.run_coroutine_threadsafe(coro, self.client.loop)
try:
fut.result()
except:
pass
async def playSong(self, ctx, url):
with youtube_dl.YoutubeDL(self.YDL_OPTIONS) as ydl:
info = ydl.extract_info(url, download=False)
if 'entries' in info: # if no url is input
url2 = info['entries'][0]['formats'][0]['url']
if ctx.message.guild.id not in tracks.keys():
tracks[ctx.message.guild.id] = [info['entries'][0]['title']]
else:
tracks[ctx.message.guild.id].append(info['entries'][0]['title'])
elif 'formats' in info: # if url is passed
url2 = info['formats'][0]['url']
if ctx.message.guild.id not in tracks.keys():
tracks[ctx.message.guild.id] = [info['title']]
else:
tracks[ctx.message.guild.id].append(info['title'])
#print(*(x for x in tracks[ctx.message.guild.id]), sep='\n')
stream = await discord.FFmpegOpusAudio.from_probe(url2, **self.FFMPEG_OPTIONS)
return stream
async def queue(self, ctx, url):
stream = await self.playSong(ctx, url)
guild = ctx.message.guild
if guild.id in queues:
queues[guild.id].append(stream)
await ctx.send('Added track ' + '**' + str(tracks[guild.id][len(tracks[guild.id]) - 1]) + '**')
#commands.command(name='play', help="Plays any song", aliases=['pl'])
async def play(self, ctx, *, url):
if ctx.author.voice is None:
return await ctx.send('You are not in a voice channel!')
if ctx.voice_client is None:
await ctx.author.voice.channel.connect()
vc = ctx.guild.voice_client
guild = ctx.message.guild
if not vc.is_playing() and not self.isPaused:# and not self.skipping:
stream = await self.playSong(ctx, url)
queues[guild.id] = [stream]
vc.play(stream, after=lambda e: self.queueCheck(ctx, guild.id, vc))
await ctx.send('Playing ' + '**' + str(tracks[ctx.message.guild.id][0]) + '**')
else:
await self.queue(ctx, url)
#commands.command(name='queue', help="Shows the current queue", aliases=['q','Q'])
async def showQueue(self, ctx):
if len(tracks[ctx.message.guild.id]) == 1:
queueEmbed = discord.Embed(title="Queue", description = "Your queue seems to be a tad bit empty :(", color=discord.Colour.random())
queueEmbed.add_field(name=':drum: Currently playing \n' + tracks[ctx.message.guild.id][0], value='\u200b', inline=False)
await ctx.send(embed=queueEmbed)
elif len(tracks[ctx.message.guild.id]) > 0 or current[ctx.message.guild.id] is not None:
queueEmbed = discord.Embed(title="Queue", color=discord.Colour.random())
queuee = ""
if len(tracks[ctx.message.guild.id]) != 0:
for i in range(1, len(tracks[ctx.message.guild.id])):
queuee += ":white_small_square:" + str(i) + ". " + tracks[ctx.message.guild.id][i] + "\n"
else:
queuee = "No songs here"
queueEmbed.add_field(name='Coming up next', value='**' + queuee + '**', inline=False)
queueEmbed.add_field(name=':drum: Currently playing \n' + tracks[ctx.message.guild.id][0], value='\u200b', inline=False)
await ctx.send(embed=queueEmbed)
else:
await ctx.send('No music in queue')
#commands.command(name='skip', help="Skips the current song")
async def skip(self, ctx):
if ctx.author.voice is None:
return await ctx.send('You are not in a voice channel!')
self.skipping = True
ctx.voice_client.stop()
self.isPaused = False
Cannot post the pic of the queue showing the wrong output but here is the output embed-
Queue
-Coming up next-
1. Song 2 # should be shown below
2. Song 3
-:drum: Currently playing-
Song 1 # has finished already
Basically, if I keep skipping my way through all the songs before they end, the queue will update, otherwise if I wait for them to finish, the queue never updates. I am aware that this has to do with the conditional check for self.skipping which only lets me update the queue if I am skipping songs, but how can I pop off elements from track the moment the song ends at the same time?
I'm not sure where you are getting the songs from as you haven't shown that section of your code, however I am going to assume you are using YoutubeDL. In the case of YoutubeDL, what you can do is extract the duration of the video in seconds, and then have a while loop counting up from 0 every second, once the counter reaches the same value as the duration of the video, just remove the track from the queue and carry on. For example:
def player():
with YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(queues[id][0], download=False)
vduration = info['duration']
link = info['url']
voice.play(FFmpegPCMAudio(link, **ffmpeg_opts))
voice.is_playing()
#commands.command()
asnyc def play(ctx, url)
queues[id].append(url)
#do everything you do here
while True:
if 0 <= 0 < len(queues[id]):
player()
counter = 0
while not counter >= vduration:
await asyncio.sleep(1)
counter += 1
queues[id].pop(0)
This is just an extremely simple example because as I said before it's hard to answer if you do not show all the relevant code, however it should apply regardless. Essentially all you need is to extract the duration of the video into a variable and then just count up 1 every second for as long as the video is playing, and then when variables match, move on to the next video.
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!")
I am making a telegram bot on aiogram. Can't pass variables from message.handler to callback.handler. I use FSM, in message.handler state = "waiting_for_address".
The algorithm is => in message.handler, the bot sends inline_keyboard with the "Take" button to the GROUP. When the button is clicked, a callback is sent and the bot visits callback.handler. The state (state = "waiting_for_address") is saved, but only for the user who used the bot. But when we click the button, another user appears and has no state set. If I manually set the state in callback.handler, then the bot stops working.
Tell me, please, how to do it right?
#dp.message_handler(state=Form.waiting_for_address)
async def address_enter(message: types.Message, state: FSMContext):
inline_button = InlineKeyboardButton(text = 'Take order', callback_data='take')
inline_keyboard = types.InlineKeyboardMarkup(resize_keyboard = True, one_time_keyboard=True).add(inline_button)
address = message.text
await state.update_data(myTelephone=await getPhone(mydb,message))
await state.update_data(myAddress=address)
await state.update_data(myId=message.from_user.id)
user_data = await state.get_data()
chatId = '-###'
await bot.send_message(chatId, text=emoji.emojize(f"❗️ <b>New order</b> ❗️\nAddress : <b>{user_data['myAddress']}</b>\nCustomers telephone number : <b>{user_data['myTelephone']}</b>"),parse_mode='html',reply_markup = inline_keyboard)
await message.answer('Your order is sent',parse_mode='html')
#dp.callback_query_handler(lambda call: call.data == 'take' )
async def agree_ref_start(query: types.CallbackQuery, state: FSMContext):
if query.data == 'take':
await query.answer("I am callback!")
await bot.edit_message_text(chat_id=query.message.chat.id, message_id=query.message.message_id, text=emoji.emojize(f"✅ <b>Order is accepted</b> ✅\nАдрес : <b>{user_data['myAddress']}</b>\nCustomer telephone : <b>{user_data['myTelephone']}</b>\nOrder is accepted by #{query.from_user.username}"),parse_mode='html', reply_markup=None)
await bot.send_message(user_data['myId'],f"✅Your order is accepted\nYour telephone number <b>{user_data['myTelephone']}</b>\ndriver id - {query.from_user.id} ",parse_mode='html')
await state.finish()
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