how to perform a function at a certain time by using aiogram and aioschedule - schedule

I have been looking for a solution for several days and trying different options, I know that there are already topics with such questions, but trying solutions from the answers in these topics, I still don't get anything. I would appreciate your help. Where am i wrong?
import requests
import datetime
from config import open_weather_token, bot_token
from aiogram import Bot, types
from aiogram.dispatcher import Dispatcher
from aiogram.utils import executor
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
import aioschedule as schedule
import time
import asyncio
bot= Bot(token=bot_token)
dp= Dispatcher(bot)
#dp.message_handler(commands=["start"])
async def start_command(message: types.Message):
await message.reply ("Привет, {0.first_name}! Напиши мне название своего города и я пришлю сводку погоды!".format(message.from_user))
#dp.message_handler()
async def get_weather(message: types.Message):
code_to_smile= {
"Clear": "Ясно \U00002600",
"Clouds": "Облачно \U00002601",
"Rain": "Дождь \U00002614",
"Drizzle": "Дождь \U00002614",
"Thunderstorm": "Гроза \U000026A1",
"Snow": "Снег \U0001F328",
"Mist": "Туман \U0001F32B"
}
try:
r= requests.get(
f"http://api.openweathermap.org/data/2.5/weather?q={message.text}&appid={open_weather_token}&units=metric")
data= r.json()
city = data["name"]
cur_weather= data["main"]["temp"]
weather_description= data["weather"][0]["main"]
if weather_description in code_to_smile:
wd= code_to_smile[weather_description]
else:
wd= "Посмотри в окно, не пойму что там за погода!"
humidity = data["main"]["humidity"]
pressure = data["main"]["pressure"]
wind = data["wind"]["speed"]
sunrise_timestamp = datetime.datetime.fromtimestamp(data["sys"]["sunrise"])
sunset_timestamp = datetime.datetime.fromtimestamp(data["sys"]["sunset"])
length_of_the_day = datetime.datetime.fromtimestamp(data["sys"]["sunset"]) - datetime.datetime.fromtimestamp(
data["sys"]["sunrise"])
await message.reply(f"***{datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}***\n"
f"Погода в городе: {city}\nТемпература: {cur_weather}C° {wd}\n"
f"Влажность: {humidity}%\nДавление: {pressure} мм.рт.ст\nВетер: {wind} м/с\n"
f"Восход солнца: {sunrise_timestamp}\nЗакат солнца: {sunset_timestamp}\nПродолжительность светового дня: {length_of_the_day}\n"
f"Хорошего дня!"
)
except:
await message.reply('Проверьте название города')
#dp.message_handler()
async def job(message: types.Message):
await message.reply("привет привет")
async def scheduler():
schedule.every().day.at("13:34").do(job)
while True:
await schedule.run_pending()
await asyncio.sleep(1)
async def on_startup(_):
asyncio.create_task(scheduler())
loop = asyncio.get_event_loop()
loop.create_task(on_startup())
if __name__ == "__main__":
executor.start_polling(dp, skip_updates=False, on_startup=on_startup)
S C:\Users\zaggg\Desktop\WeatherBot> & C:/Users/zaggg/AppData/Local/Programs/Python/Python311/python.exe c:/Users/zaggg/Desktop/WeatherBot/main.py
Task exception was never retrieved
future: <Task finished name='Task-4' coro=<scheduler() done, defined at c:\Users\zaggg\Desktop\WeatherBot\main.py:67> exception=TypeError('Passing coroutines is forbidden, use tasks explicitly.')>
Traceback (most recent call last):
File "c:\Users\zaggg\Desktop\WeatherBot\main.py", line 70, in scheduler
await schedule.run_pending()
File "C:\Users\zaggg\AppData\Local\Programs\Python\Python311\Lib\site-packages\aioschedule\__init__.py", line 544, in run_pending
await default_scheduler.run_pending()
File "C:\Users\zaggg\AppData\Local\Programs\Python\Python311\Lib\site-packages\aioschedule\__init__.py", line 111, in run_pending
return await asyncio.wait(jobs, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\zaggg\AppData\Local\Programs\Python\Python311\Lib\asyncio\tasks.py", line 415, in wait
raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
TypeError: Passing coroutines is forbidden, use tasks explicitly.
C:\Users\zaggg\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py:1910: RuntimeWarning: coroutine 'Job.run' was never awaited
handle = None # Needed to break cycles when an exception occurs.
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
one of types of errors i have meet

Related

Having trouble running multiple functions in Asyncio

I'm a novice programmer looking to build a script that reads a list of leads from Google Sheets and then messages them on telegram. I want to separate out the first and second message by three days thats why im separating the methods.
import asyncio
from telethon import TelegramClient
from telethon.errors.rpcerrorlist import SessionPasswordNeededError
import logging
from async_class import AsyncClass, AsyncObject, task, link
from sheetdata import *
logging.basicConfig(format='[%(levelname) 5s/%(asctime)s] %(name)s: %(message)s',
level=logging.WARNING)
api_id = id
api_hash = 'hash'
phone='phone'
username='user'
client = TelegramClient(username, api_id, api_hash)
#already been touched once
second_touch_array=[]
#touched twice
third_touch_array=[]
async def messageCount(userid):
count = 0
async for message in client.iter_messages(userid):
count+=1
yield count
async def firstMessage():
#clear prospects from array and readData from google sheet
clearProspects()
readData(sheet)
#loop through prospects and send first message
for user in prospect_array:
#check if we already messaged the prospect. If we haven't, execute function
if(messageCount(user.id) == 0):
await client.send_message(user.id, 'Hi')
second_touch_array.append(prospect(user.name, user.company, user.id))
print("First Message Sent!")
else:
print("Already messaged!")
async def secondMessage():
for user in second_touch_array:
if(messageCount(user.id) == 1):
await client.send_message(user.id, 'Hello')
third_touch_array.append(prospect(user.name, user.company, user.id))
print("Second Message Sent!")
else:
print("Prospect has already replied!")
async def main():
# Getting information about yourself
me = await client.get_me()
await firstMessage()
await secondMessage()
for user in second_touch_array:
print(user.name, user.company, user.id)
with client:
client.loop.run_until_complete(main())
Anyways, when I run my code i'm successfully getting the "Already Messaged!" print statement in my terminal from the firstMessage function.
This is good - it's detecting I've already messaged the one user on my Google Sheets list; however, my second function isn't being called at all. I'm not getting any print statement and every time I try to print the contents of the second array nothing happens.
If you have any advice it would be greatly appreciated :)

How can I know the command used? - Discord.py

I am trying to make a errorhandling for my Discord.py, how do I know what command was used for the error to pop up?
#bot.event
async def on_command_error(ctx, error):
print("error: ",error)
if search("not found", str(error)):
c_f = random.choice([f"`{command used}` was not found, silly.", "Ehm.. Since when do we have `{command used}`?", "I don't know what `{command used}` is?"])
embed=discord.Embed(title=c_f, description=f"Please use existing commands. {ctx.author.mention}", color=error_color)
embed.timestamp = datetime.utcnow()
embed.set_footer(text=bot_name, icon_url=icon_uri)
await ctx.send(embed=embed)
elif search("cooldown", str(error)):
c_d = random.choice(["Did you drink energy drinks!?", "Why are you stressing, buddy.", "Duhh, wait, you're on cooldown!"])
second_remain = round(error.retry_after, 1)
embed=discord.Embed(title=c_d, description=f"Try again after {second_remain}s. {ctx.author.mention}", color=error_color)
embed.timestamp = datetime.utcnow()
embed.set_footer(text=bot_name, icon_url=icon_uri)
await ctx.send(embed=embed)
else:
raise error
Any attribute I can use?
You can use ctx.command
#bot.event
async def on_command_error(ctx, exception):
error = getattr(exception, "original", exception)
if hasattr(ctx.command, "on_error"): # If a command has it's own handler
return
elif isinstance(error, CommandNotFound):
return
if isinstance(error, discord.CommandInvokeError):
print(ctx.command)
Your solution is to add them to the command specifically, this also means it can help diagnose an issue with a command more exact.
You can also add any error events to the specific listener, just like how you done it for all commands, instead add them individually.
#bot.command()
async def command_name(ctx):
# ...
#command_name.error
async def command_name_error(ctx, error):
if isinstance(error, commands.CommandInvokeError):
await ctx.send("An error from this command" + error)
With #command_name.error put your command name before the .error, then this makes an error listener for that command, if it produces an error.

aiogram.utils.exceptions.WrongFileIdentifier: Wrong file identifier/http url specified

I want to make a bot to which you send a link to the file, and it sends you the file, but I get an error from the header. I also tried to send InputFile object, but nothing was sent. Here's my code
from aiogram import Bot, types
from aiogram.dispatcher import Dispatcher
from aiogram.utils import executor
from aiogram.types.input_file import InputFile
bot = Bot(token = '')
dp = Dispatcher(bot)
#dp.message_handler()
async def send_playlist(message: types.Message):
print(message.text)
await bot.send_document(message.chat.id, message.text)
executor.start_polling(dp)
Here's full error text
future: <Task finished name='Task-14' coro=<Dispatcher._process_polling_updates() done, defined at B:\portable_soft\python\lib\site-packages\aiogram\dispatcher\dispatcher.py:331> exception=WrongFileIdentifier('Wrong file identifier/http url specified')>
Traceback (most recent call last):
File "B:\portable_soft\python\lib\site-packages\aiogram\dispatcher\dispatcher.py", line 339, in _process_polling_updates
for responses in itertools.chain.from_iterable(await self.process_updates(updates, fast)):
File "B:\portable_soft\python\lib\site-packages\aiogram\dispatcher\dispatcher.py", line 194, in process_updates
return await asyncio.gather(*tasks)
File "B:\portable_soft\python\lib\site-packages\aiogram\dispatcher\handler.py", line 117, in notify
response = await handler_obj.handler(*args, **partial_data)
File "B:\portable_soft\python\lib\site-packages\aiogram\dispatcher\dispatcher.py", line 214, in process_update
return await self.message_handlers.notify(update.message)
File "B:\portable_soft\python\lib\site-packages\aiogram\dispatcher\handler.py", line 117, in notify
response = await handler_obj.handler(*args, **partial_data)
File "B:\portable_soft\adb\file_bot.py", line 12, in send_playlist
await bot.send_document(message.chat.id, message.text)
File "B:\portable_soft\python\lib\site-packages\aiogram\bot\bot.py", line 402, in send_document
result = await self.request(api.Methods.SEND_DOCUMENT, payload, files)
File "B:\portable_soft\python\lib\site-packages\aiogram\bot\base.py", line 201, in request
return await api.make_request(self.session, self.__token, method, data, files,
File "B:\portable_soft\python\lib\site-packages\aiogram\bot\api.py", line 104, in make_request
return check_result(method, response.content_type, response.status, await response.text())
File "B:\portable_soft\python\lib\site-packages\aiogram\bot\api.py", line 78, in check_result
exceptions.BadRequest.detect(description)
File "B:\portable_soft\python\lib\site-packages\aiogram\utils\exceptions.py", line 136, in detect
raise err(cls.text or description)
aiogram.utils.exceptions.WrongFileIdentifier: Wrong file identifier/http url specified```
From your question, I don't really understand what you are trying to do. If you describe your use case a little better, I can probably suggest something more useful. What are you trying to do with playlists?
Here is one way to work with files:
#dp.message_handler(regexp='(^cat[s]?$|puss)')
async def cats(message: types.Message):
with open('data/cats.jpg', 'rb') as photo:
'''
# Old fashioned way:
await bot.send_photo(
message.chat.id,
photo,
caption='Cats are here 😺',
reply_to_message_id=message.message_id,
)
'''
await message.reply_photo(photo, caption='Cats are here 😺')
I actually prefer using FileIDs but I am not sure if that will help you.
Um, just write:
await bot.send_message(...)
but not:
await bot.send_DOCUMENT(...)
and if an error pops up:
from aiogram import md
await bot.send_message(msg.from_user.id, md.quote_html(msg.text))

How can I access the return value from twisted deferred callbacks using AsyncioSelectorReactor?

Using Python 3.7.7, Twisted 20.3.0 (and Scrapy 2.1.0), when I try...
doc_link = await self.upload_reseller_document(doc_request, self.create_id(contract))
I get a deferred instead of a string. Also my callbacks are not awaited.
Expected: https://s3.amazonaws.com/some-bucket/some_file.csv or None
Received: <Deferred at 0x11ae61dd0 current result: None>
async def conditional_upload(request):
docs_bucket = 'some-bucket'
key = f'some-prefix/some_file.csv'
url = f'https://s3.amazonaws.com/{docs_bucket}/{key}'
async def cb(obj):
print('found key, returning url')
return defer.success(url)
async def upload_doc():
print('called upload_doc')
response = await self.crawler.engine.download(request, self)
if response.status != 200:
# Error happened, return item.
print('could not download reseller csv')
return defer.error(None)
print('uploading to', docs_bucket, key)
return threads.deferToThread(
self.s3client.put_object,
Bucket=docs_bucket,
Key=key,
Body=response.body)
async def eb(failure):
print('did not find key')
if failure.type != ClientError:
raise failure.value
return upload_doc()
return ensureDeferred(threads.deferToThread(
self.s3client.head_object,
Bucket=docs_bucket,
Key=key).addCallbacks(cb, eb))
Internally Twisted deals only with Deferreds and functions that returns it, you can't pass async functions as callbacks to Deferreds (when called, async functions returns a coroutine object), if you do, the callback will have no effect and at the reactor stop you will get a warning "coroutine x was never awaited".
When using async functions, you should just await the Deferreds finish and handle their result instead of appending callbacks and returning them. The goal of async functions is to avoid the callback hell.
defer.ensureDeferred is used to wrap coroutines in a deferred and allow Twisted to schedule them to be ran, you use it when you need to call async functions inside functions that are not async.
Use try/catch to handle the exceptions (it's equivalent to errback, but the exception is not wrapped in twisted's Failure):
async def conditional_upload(request):
docs_bucket = 'some-bucket'
key = f'some-prefix/some_file.csv'
url = f'https://s3.amazonaws.com/{docs_bucket}/{key}'
async def upload_doc():
print('called upload_doc')
response = await self.crawler.engine.download(request, self)
if response.status != 200:
# Error happened, return item.
print('could not download reseller csv')
raise Exception('could not download reseller csv')
print('uploading to', docs_bucket, key)
return await threads.deferToThread(
self.s3client.put_object, Bucket=docs_bucket, Key=key, Body=body
)
# propably here you want to check if something already exists
try:
await threads.deferToThread(self.s3client.head_object, Bucket=docs_bucket, Key=key)
print('found key, returning url')
return url
except ClientError:
print('did not find key, going to upload_doc ...')
# if does not exists, then create it
retry_attempts = 10 # avoid infinite loop
for _ in range(retry_attempts):
try:
await upload_doc()
print('Uploaded the key, returning url')
return url
except ClientError:
print('Failed to upload the key, retrying...')
print('Failed to upload the key, max attemps tried.')

tornado's AsyncHttpTestCase is not available outside self.fetch

I have an AsyncHttpTestCase and I want to access it from methods besides self.fetch. Specifically, I have a SockJS handler that I want a sockjs-client to attach too for my tests.
I've discovered that even though self.get_url('/foo') returns a valid url, that url does not respond to anything except for self.fetch(). What gives?
Is this just not possible with AsyncHttpTestCase? What is the best pattern for doing this?
Here's tests.py
import urllib2
from tornado.httpclient import AsyncHTTPClient
import tornado.testing
from tornado.testing import AsyncTestCase, AsyncHTTPTestCase
from app import DebugApp
class TestDebug(AsyncHTTPTestCase):
def get_app(self):
return DebugApp()
def test_foo(self):
response = self.fetch('/foo')
print response.body
assert response.body == 'derp'
def test_foo_from_urllib(self):
response = urllib2.urlopen(self.get_url('/foo'), None, 2)
print response
assert response.body == 'derp'
def runTest(self):
pass
and app.py
import tornado.httpserver
import tornado.ioloop
import tornado.web
from tornado.options import options
class FooHandler(tornado.web.RequestHandler):
def get(self):
self.write("derp")
url_patterns = [
(r"/foo", FooHandler),
]
class DebugApp(tornado.web.Application):
def __init__(self):
tornado.web.Application.__init__(self, url_patterns, debug=True, xsrf_cookies=False)
def main():
app = DebugApp()
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(6006)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
and runtest.py
#!/usr/bin/env python
import unittest
from os import path
import sys
import tornado.testing
PROJECT_PATH = path.dirname(path.abspath(__file__))
sys.path.append(PROJECT_PATH)
def all():
suite = unittest.defaultTestLoader.discover('./', 'tests.py', path.dirname(path.abspath(__file__)))
print suite
return suite
if __name__ == '__main__':
# Print a nice message so that we can differentiate between test runs
print ''
print '%s %s' % ('Debug app', '0.1.0')
print '\033[92m' + '-------------- Running Test Suite --------------' + '\033[0m'
print ''
tornado.testing.main()
The problem is that the IOLoop is not running when you call urlopen (and it cannot be, because urlopen is a blocking function). You must run the IOLoop (with either #gen_test, self.wait(), or indirectly via methods like self.fetch()) for the server to respond, and you can only interact with it via non-blocking functions (or if you must use blocking functions like urlopen, run them in a separate thread).