I'm developing a Telegram bot using the Telethon library.
I want to pass the value of tags_cv from send_tag_list() to show_selected_tags(). But I get this error:
`ERROR - Unhandled exception on show_selected_tags
Traceback (most recent call last):
File "/home/kozorez/.cache/pypoetry/virtualenvs/content-telegram-bot-4LA9pChh-py3.10/lib/python3.10/site-packages/telethon/client/updates.py", line 570, in _dispatch_update
await callback(event)
File "/mnt/c/Users/kozor/PycharmProjects/content-telegram-bot/bot/bot.py", line 77, in show_selected_tags
some_var = tags_cv.get()
LookupError: <ContextVar name='tags' at 0x7f7e3e222de0>`
I suppose, show_selected_tags() doesn't receive the value of tags_cv. Perhaps Telethon events run in different asynchronous contexts?
bot.py
"""Логика взаимодействия с ботом"""
import logging
import contextvars
from telethon.sync import TelegramClient, events
from services.channel import get_channel_data, add_channel_to_db
from services.post import parse_posts
from services.tag import get_tags_list
from services.keyboard import create_tags_keyboard
from config import (
api_id,
api_hash,
bot_token
)
bot = TelegramClient(
'bot',
api_id,
api_hash).start(bot_token=bot_token)
channel_data_cv = contextvars.ContextVar('channel_data')
tags_cv = contextvars.ContextVar('tags')
@bot.on(events.NewMessage(pattern='/start'))
async def send_welcome(event) -> None:
"""Отправка приветственного сообщения"""
await event.reply('Привет! Я — бот, который поможет тебе составить пост с навигацией по твоему телеграм-каналу. \
\nПросто пришли мне ссылку на свой телеграм-канал.')
@bot.on(events.NewMessage(pattern='https://t\.me/(\S+)'))
async def send_tag_list(event) -> None:
"""Анализируем посты из канала и возвращаем клавиатуру с тегами"""
channel_link = event.text
tags = None
try:
channel_data = await get_channel_data(channel_link)
channel_data_cv.set(channel_data)
except ValueError as error:
if 'Cannot get entity from a channel' in str(error):
await event.reply('Канал должен быть публичным')
logging.error(error)
try:
await add_channel_to_db(channel_data)
except Exception as error:
logging.error(error)
try:
await event.reply('Посты анализируются, ожидайте')
await parse_posts(channel_link, channel_data)
except ValueError as error:
logging.error(error)
await event.reply('К сожалению, мне не удалось найти ни одного тега')
try:
tags = await get_tags_list(channel_data)
tags_cv.set(tags)
except Exception as error:
logging.error(error)
tags_keyboard = await create_tags_keyboard(tags)
await event.respond("Выберите теги", buttons=tags_keyboard)
@bot.on(events.CallbackQuery())
async def show_selected_tags(event) -> None:
some_var = tags_cv.get()
print(some_var)
run.py
"""Запуск бота"""
import asyncio
import logging
from bot import bot
from db.models import *
from config import (
engine,
client
)
loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
async def init_db() -> None:
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all)
logging.info('База данных инициализирована')
async def start() -> None:
await init_db()
async with bot:
await bot.run_until_disconnected()
logging.info('Бот запущен')
async with client:
await client.run_until_disconnected()
logging.info('Клиент запущен')
if __name__ == "__main__":
loop.create_task(start())
loop.run_forever()
Be honest, I don't understand how to fix this. The contextvars guide is too simple to make a mistake... What else besides context variables can be used? Maybe caching?
I would appreciate your help.
By default, Telethon creates a new task for each update. Context variables are attached to the context of a task. If the task changes, the context changes, and you won't have the variable.
In v1, you can enable sequential_updates
to use a single task for all updates:
client = TelegramClient(..., sequential_updates=True)
This would mean all update handlers use the same task.