szastupov / aiotg Goto Github PK
View Code? Open in Web Editor NEWAsynchronous Python library for building Telegram bots
License: MIT License
Asynchronous Python library for building Telegram bots
License: MIT License
The singular field has been deprecated for a long time (https://core.telegram.org/bots/api-changelog#may-18-2017) and has been removed from the documentation.
Please add support for the plural version.
Currently I do
@bot.handle('new_chat_member')
def example(chat, new_chat_member):
new_chat_members = chat.message['new_chat_members']
for new_chat_member in new_chat_members:
foobar
but if telegram removes the field, all of us will no longer be able to receive these updates.
I think I can just make the decorator argument plural, but I am unsure.
I imagine this is just adding the plural version to the message types constant, but again I am unsure
Hi, I was thinking about how it would be great to be able to use the power of async / await syntax to create connections of the type
async def conversation(chat: Chat):
number = randint(1, 1000)
await chat.send_text("Hello, give me a number")
while True:
response = await chat.response(r'^\d+$', unexpected='Sorry, I did not understand.')
if response:
response = int(response.group())
if response > number:
await chat.send_text('high')
elif response < number:
await chat.send_text('low')
else:
await chat.send_text('Correct!')
break
Now i am going to work on webhook implementation for aiotg. Is it possible to change requirement aiohttp version to >= 1? There were some changes in http server implementation and i want to use its current api.
We need a plugin system that allows to define new bot commands and handlers in separate modules. It would also be nice to be able enable/disable plugins without reloading the bot.
Hey @szastupov,
Was using until the new version bumped, never had an issue. After getting installed new version from pip, my code throws error. Guessing that it might have problem with aiohttp
session.
My code:
# Asyncio
import asyncio
# UVLoop
import uvloop
# Asyncpg
from asyncpg import create_pool as create_pg_pool
# Aiobotocore
from aiobotocore import get_session as boto_session
# Bot
from bot import bot
# Misc
import os
from urllib.parse import urlparse
# Use uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
async def run_bot():
await bot.loop()
async def make_pg_pool():
dsn = os.environ.get('DATABASE_URL')
return await create_pg_pool(
dsn=dsn,
min_size=10,
max_size=20)
async def make_s3_client(loop):
aws_access_key_id = os.environ.get('AWS_ACCESS_KEY_ID')
aws_secret_access_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
session = boto_session(loop=loop)
return session.create_client(
's3', region_name='us-east-1',
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key)
# Main event loop
loop = asyncio.get_event_loop()
# Attach postgres connection pool to bot
pg_pool = loop.run_until_complete(make_pg_pool())
setattr(bot, 'pg_pool', pg_pool)
# Attach s3 client to bot
s3_client = loop.run_until_complete(make_s3_client(loop))
setattr(bot, 's3_client', s3_client)
if __name__ == '__main__':
import sys
if len(sys.argv) == 2 and sys.argv[1] == 'loop':
loop.run_until_complete(run_bot())
else:
bot.run_webhook(os.environ.get('APP_URL') + 'webhook')
Exception:
$ python main.py
Creating a client session outside of coroutine
client_session: <aiohttp.client.ClientSession object at 0x7f2fc36aa4e0>
DEBUG:botocore.loaders:Loading JSON file: /home/vb/.env/lib/python3.5/site-packages/botocore/data/endpoints.json
DEBUG:botocore.loaders:Loading JSON file: /home/vb/.env/lib/python3.5/site-packages/botocore/data/s3/2006-03-01/service-2.json
DEBUG:botocore.loaders:Loading JSON file: /home/vb/.env/lib/python3.5/site-packages/botocore/data/_retry.json
DEBUG:botocore.client:Registering retry handlers for service: s3
DEBUG:botocore.hooks:Event creating-client-class.s3: calling handler <function add_generate_presigned_post at 0x7f2fc3eac048>
DEBUG:botocore.hooks:Event creating-client-class.s3: calling handler <function add_generate_presigned_url at 0x7f2fc3ea6400>
DEBUG:botocore.args:The s3 config key is not a dictionary type, ignoring its value of: None
DEBUG:botocore.endpoint:Setting s3 timeout as (60, 60)
DEBUG:botocore.client:Defaulting to S3 virtual host style addressing with path style addressing fallback.
DEBUG:aiotg:api_call setWebhook, {'url': 'https://vb.muminoff.uz/webhook'}
Traceback (most recent call last):
File "main.py", line 61, in <module>
bot.run_webhook(os.environ.get('APP_URL') + 'webhook')
File "/home/vb/project/aiotg/bot.py", line 118, in run_webhook
loop.run_until_complete(self.set_webhook(webhook_url, **options))
File "uvloop/loop.pyx", line 1203, in uvloop.loop.Loop.run_until_complete (uvloop/loop.c:25632)
File "uvloop/future.pyx", line 146, in uvloop.loop.BaseFuture.result (uvloop/loop.c:109361)
File "uvloop/future.pyx", line 101, in uvloop.loop.BaseFuture._result_impl (uvloop/loop.c:108900)
File "uvloop/future.pyx", line 372, in uvloop.loop.BaseTask._fast_step (uvloop/loop.c:112669)
File "/home/vb/project/aiotg/bot.py", line 248, in api_call
response = await self.session.post(url, data=params)
File "/home/vb/.env/lib/python3.5/site-packages/aiohttp/client.py", line 582, in __await__
resp = yield from self._coro
File "/home/vb/.env/lib/python3.5/site-packages/aiohttp/client.py", line 200, in _request
with timer:
File "/home/vb/.env/lib/python3.5/site-packages/aiohttp/helpers.py", line 750, in __enter__
raise RuntimeError('Timeout context manager should be used '
RuntimeError: Timeout context manager should be used inside a task
ERROR:asyncio:Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7f2fc0514940>
Faced to this, this and this issues while searching with RuntimeError: Timeout context manager should be used inside a task
.
Information:
$ python --version
Python 3.5.2
$ pip freeze
aiobotocore==0.2.1
aiohttp==1.3.3
aiotg==0.7.19
async-timeout==1.1.0
asyncpg==0.8.4
botocore==1.5.0
chardet==2.3.0
docutils==0.13.1
hashids==1.2.0
jmespath==0.9.1
multidict==2.1.4
pkg-resources==0.0.0
python-dateutil==2.6.0
six==1.10.0
uvloop==0.8.0
Wand==0.4.4
yarl==0.9.8
Any hints to fix or workaround?
aiohttp.get got deprecated and removed a long time ago. You need to create a ClientSession object now, as shown in the examples on the home page.
Version: aiotg==0.7.17
After I have run the following sample
from aiotg import Bot
bot = Bot(api_token="...")
@bot.command(r"/echo (.+)")
def echo(chat, match):
return chat.reply(match.group(1))
bot.run()
I got the error:
File "./venv/lib/python3.5/site-packages/aiotg/bot.py", line 82, in loop
timeout=self.api_timeout
File "./venv/lib/python3.5/site-packages/aiotg/bot.py", line 243, in api_call
response = await aiohttp.post(url, data=params)
AttributeError: module 'aiohttp' has no attribute 'post'
Some thinks about a way to close HttpSession.
shadrus@c06c996
Hello!
In my project I need to get information about who pressed button in chat, but as I was looking at source code, I found out, that __init__ method for aiotg.bot.CallbackQuery class is not saving some fields from callback_query key that is sent to webhook.
What is already gatherable:
What is missing:
And there other fields from Telegram API docs, but not sure if usable:
I'm mostly interested in from field, but for a general purposes it would be better if all those fields will be available
Also maybe it would be better if fields are generated dynamicly from existing keys of callback_query, so it wouldn't be required to make new changes in future.
In order to process an event when a bot joins a new group, group_chat_created
needs to be in the list of supported events.
Example of payload:
{
'update_id': XXXXXX,
'message': {
'message_id': 38,
'from': {
'id': XXXXXXX,
'is_bot': False,
'first_name': 'Marco',
'last_name': 'Ceppi',
'username': 'XXXXXXXX',
'language_code': 'en-US'
},
'chat': {
'id': -XXXXXXX,
'title': 'Another Test',
'type': 'group',
'all_members_are_administrators': True
},
'date': 1518477717,
'group_chat_created': True
}
}
After update to aiohttp 1.0.0, aiotg has deprecated syntax for GET and POST requests.
It should use aiohttp.ClientSession().
Hi! I think, this framework is great, and I use it sometimes.
Now I'm facing with next problem: I want my bot to have default text handler and also to have special handler for some commands
For example, I have next code:
Now, if I'm sending my bot command /start
, but instead of handle_start_command
callback, it will execute handle_text
callback. What's the problem, and how to overcome it?
Working on my bot, I encountered a problem. To illustrate it, I've written a simple script shown below:
from aiotg import Bot, Chat, InlineQuery, CallbackQuery
bot = Bot(api_token="123456789:XXXXXXXXXXXXXXXXXXXXXXXXXX-YYYYYYYY", default_in_groups=True)
keyboard = {"inline_keyboard": [[{"callback_data": "clicked", "text": "Click me!"}]]}
@bot.command("/test")
def start(chat: Chat, match) -> None:
chat.send_text("Hello!", reply_markup=bot.json_serialize(keyboard))
@bot.inline
def inline_query_handler(query: InlineQuery):
query.answer([{
"type": "article",
"id": "0",
"title": "Hello World",
"description": "Testing...",
"input_message_content": {"message_text": "Testing..."},
"reply_markup": keyboard
}])
@bot.callback
def callback_query_handler(chat: Chat, query: CallbackQuery) -> None:
query.answer(text="Hello World")
if __name__ == '__main__':
bot.run(debug=True)
By the way, I think it's kind of inconsistent behavior that the reply_markup
parameter of the InlineQuery.answer()
method strictly takes a dict, but the send_message()
method requires explicit JSON serialization.
It does 3 things:
/test
command with a dummy message containing one inline button;If we try to click on the button attached to the bot's message (either in the personal chat with bot itself or in a group chat), everything works fine. However, the click on the button of the user's message suggested by the bot, triggers a KeyError
:
Traceback (most recent call last):
File "test_aiotg.py", line 30, in <module>
bot.run(debug=True)
File "/home/kozalo/venv/lib/python3.7/site-packages/aiotg/bot.py", line 157, in run
run_with_reloader(loop, bot_loop, self.stop)
File "/usr/lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete
return future.result()
File "/home/kozalo/venv/lib/python3.7/site-packages/aiotg/reloader.py", line 91, in run_with_reloader
if isinstance(fut.result(), Event):
File "/home/kozalo/venv/lib/python3.7/site-packages/aiotg/bot.py", line 131, in loop
self._process_updates(updates)
File "/home/kozalo/venv/lib/python3.7/site-packages/aiotg/bot.py", line 629, in _process_updates
self._process_update(update)
File "/home/kozalo/venv/lib/python3.7/site-packages/aiotg/bot.py", line 648, in _process_update
coro = self._process_callback_query(update["callback_query"])
File "/home/kozalo/venv/lib/python3.7/site-packages/aiotg/bot.py", line 613, in _process_callback_query
chat = Chat.from_message(self, query["message"])
KeyError: 'message'
The description of the CallbackQuery
type in the official documentation says:
If the button that originated the query was attached to a message sent by the bot, the field message will be present. If the button was attached to a message sent via the bot (in inline mode), the field inline_message_id will be present.
Therefore, callback queries issued by the messages sent by the user via inline mode don't have the message
field, only an identificator of the inline message. Thus, it's not possible to instantiate a chat object from the missing message.
If it's OK to pass None
instead of a Chat
instance to the handler, then it's pretty easy to fix the issue:
def _process_callback_query(self, query):
chat = Chat.from_message(self, query["message"]) if "message" in query else None
cq = CallbackQuery(self, query)
for patterns, handler in self._callbacks:
match = re.search(patterns, cq.data, re.I)
if match:
return handler(chat, cq, match)
# make an assumption that the inline mode is mostly used in group chats
if chat and not chat.is_group() or self.default_in_groups:
return self._default_callback(chat, cq)
Otherwise, I cannot suggest an acceptable solution. Sorry.
Just don't set reply_markup to null in reply
Hey, any way to bump a new release? My requirements.txt
still includes -e git://github.com/szastupov/aiotg#egg=aiotg
from master branch. Wanted to use pip version. Thanks.
When you receive a CallbackQuery (when you press a button in an inline keyboard), you only get access to 'query_id' and 'data', but not to 'chat_instance', 'from' and 'message' data. This means that many users at many chats can be pressing these buttons and you don't know where it comes from.
Looking at class CallbackQuery.init all the information is in the src variable, which is not preserved after the init, so I propose that you add there something like self.chat_instance=src['chat_instance'], self.from_ = src['from'] and self.message=src['message'] (and possibly the complete self.src = src) to preserve all the information.
https://github.com/romis2012/aiosocksy is not maintained anymore and restricted to use aiohttp<3.0
. It should be replaced with https://github.com/romis2012/aiohttp-socks.
It would be nice to have something in _process_message
to send the send_chat_action
before sending a message. Like if it was chat.send_text()
or chat.reply()
, we can send the chat.send_chat_action('typing')
for sure automatically if it's enabled in bot's settings. The same for audio and photos... The same approach already is done for tracking to botan
, but we can send chat actions as well. Also, it would be nice to have it enabled by default.
Currently, almost 30% of the code for my bot are chat actions lol.
Hi!
Thanks for this library, it's cool!
Not sure why, but "disable_notification" not working for send_photo,
with open('assets/some.jpg', 'rb') as f:
await chat.send_photo(f, caption='example', disable_notification=True)
Here is trace:
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
result = coro.throw(exc)
File "aiobot.py", line 62, in start
await chat.send_photo(f, caption='example', disable_notification=True)
File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
yield self # This tells Task to wait for completion.
File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
future.result()
File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
File "/var/workspace/mybot/python3/lib/python3.5/site-packages/aiotg/bot.py", line 319, in _api_call
response = await self.session.post(url, data=params)
File "/var/workspace/mybot/python3/lib/python3.5/site-packages/aiohttp/helpers.py", line 102, in __await__
ret = yield from self._coro
File "/var/workspace/mybot/python3/lib/python3.5/site-packages/aiohttp/client.py", line 226, in _request
session=self)
File "/var/workspace/mybot/python3/lib/python3.5/site-packages/aiohttp/client_reqrep.py", line 104, in __init__
self.update_body_from_data(data)
File "/var/workspace/mybot/python3/lib/python3.5/site-packages/aiohttp/client_reqrep.py", line 264, in update_body_from_data
body = FormData(body)()
File "/var/workspace/mybot/python3/lib/python3.5/site-packages/aiohttp/formdata.py", line 141, in __call__
return self._gen_form_data()
File "/var/workspace/mybot/python3/lib/python3.5/site-packages/aiohttp/formdata.py", line 125, in _gen_form_data
type(value), headers, value)) from exc
TypeError: Can not serialize value type: <class 'bool'>
Without this argument all works fine!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.