Я пытаюсь создать бота Telegram, который может отправлять снимок экрана веб-страницы.
Я использую python-telegram-bot для взаимодействия с Telegram и python-playwright для получения снимков экрана страницы. Проблема с моим кодом в том, что я получаю следующую ошибку с Playwright:
greenlet.error: cannot switch to a different thread
Это происходит потому, что я объявляю browser = p.chromium.launch(headless=True, args=args)
вне функции, которая выполняется при вызове команды Telegram. Проблема в том, что запуск браузера при каждом вызове команды Telegram замедляет работу моего кода примерно на 3 секунды. Как я могу объявить browser
глобально? Возможно ли это вообще?
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode
from telegram.ext import Updater, CommandHandler, ChatMemberHandler, CallbackQueryHandler
from telegram.error import Unauthorized, BadRequest
from playwright.sync_api import sync_playwright
args = [
"--no-sandbox",
"--disable-setuid-sandbox",
"--use-gl=egl",
]
p = sync_playwright().start()
browser = p.chromium.launch(headless=True, args=args)
def start(update, context):
page = browser.new_page()
page.goto("https://google.com")
page.screenshot(path = "example.png")
page.close()
context.bot.send_photo(chat_id=update.effective_chat.id, photo=open("example.png", "rb"))
updater = Updater('TOKEN', use_context=True, workers=124)
updater.dispatcher.add_handler(CommandHandler('start', start, run_async=True))
updater.start_polling()
updater.idle()
добавив два комментария: 1) Я рекомендую вам отозвать токен, который вы опубликовали для своего бота «ds1testbot». 2) JayK23, ваш код написан для PTB версии 13.15 или старше, которая больше не поддерживается командой PTB. Ответ, предоставленный Мохаммедом Алмалки, по сути, переписывает весь ваш код в PTB v20.x или новее, используя asyncio Pythons как в PTB, так и в playwright
.
Спасибо! Я удалил токен. Я пытаюсь найти способ не запускать экземпляр браузера каждый раз, когда вызывается команда, поскольку это занимает несколько секунд. Я пытаюсь найти способ объявить это глобально, не получая ошибки «невозможно переключиться на другой поток».
Я работал над этим и многое изменил в вашем коде,
Я создал бота, чтобы попробовать, пока не доведу его до конца.
Я изменил код на асинхронный код.
это последний код:
import telegram
from telegram import Update
from telegram.ext import MessageHandler, CommandHandler, Application, ContextTypes, filters
from playwright.async_api import async_playwright
args = [
"--no-sandbox",
"--disable-setuid-sandbox",
"--use-gl=egl",
]
# Replace 'YOUR_API_TOKEN' with the API token you received from BotFather.
API_TOKEN = 'yours'
async def download_screan_shot(link = "www.google.com", id_of_chat=None, update_to_use_it_to_send_the_file=None):
file_name =f"{id_of_chat}_example.png"
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto(link)
await page.screenshot(path=file_name)
await browser.close()
await update_to_use_it_to_send_the_file.message.reply_photo(file_name)
async def messages(update, context):
the_gotten_message = update.message.text
if "www" in the_gotten_message:
await download_screan_shot(link=the_gotten_message, id_of_chat=update.message.from_user.id, update_to_use_it_to_send_the_file=update)
else:
await update.message.reply_text(update.message.text)
app = Application.builder().token(API_TOKEN).build()
app.add_handler(MessageHandler(filters.TEXT,messages))
app.run_polling()
а это скриншот моего бота:
Я надеюсь, что это сработает для вас.
по поводу количества отправленных ссылок, это неудачные попытки.
поскольку вы сказали, что хотите запустить браузер один раз, я не нашел для этого решения, но сделал это, используя плохую идею.
он запускает браузер один раз, затем сохраняет его в публичном доступе в списке, а затем использует этот активный браузер.
это мой новый код:
import telegram
from telegram import Update
from telegram.ext import MessageHandler, CommandHandler, Application, ContextTypes, filters
from playwright.async_api import async_playwright
from playwright.sync_api import sync_playwright
args = [
"--no-sandbox",
"--disable-setuid-sandbox",
"--use-gl=egl",
]
# Replace 'YOUR_API_TOKEN' with the API token you received from BotFather.
API_TOKEN = 'yours'
opened_browser = []
async def download_screan_shot(link = "www.google.com", id_of_chat=None, update_to_use_it_to_send_the_file=None):
file_name =f"{id_of_chat}_example.png"
# playwright = sync_playwright().start()
# browser = playwright.chromium.launch()
# page = browser.new_page()
# page.goto("https://playwright.dev/")
# page.screenshot(path = "example.png")
# browser.close()
if (not opened_browser):
playwright = await async_playwright().start()
browser = await playwright.chromium.launch()
opened_browser.append(browser)
page = await opened_browser[0].new_page()
await page.goto(link)
await page.screenshot(path=file_name)
await page.close()
# await opened_browser[0].close()
await update_to_use_it_to_send_the_file.message.reply_photo(file_name)
async def messages(update, context):
the_gotten_message = update.message.text
if "www" in the_gotten_message:
await download_screan_shot(link=the_gotten_message, id_of_chat=update.message.from_user.id, update_to_use_it_to_send_the_file=update)
else:
await update.message.reply_text(update.message.text)
app = Application.builder().token(API_TOKEN).build()
app.add_handler(MessageHandler(filters.TEXT,messages))
app.run_polling()
если честно, я не увидел никакой разницы в производительности, а также не знаю, что там происходит.
это просто попытка дать вам представление.
вы можете сделать то же самое в своем коде >>> Я не делал этого, потому что думаю, что вы используете старую версию библиотеки Telegram, и поэтому я столкнулся со многими ошибками, когда пробовал ваш код с той версией, которая у меня есть.
скажи мне.
Эй, большое спасибо за ваш ответ! Проблема в том, что я ищу способ не запускать новый экземпляр браузера при каждой команде.
@JayK23, можешь проверить редактирование, пожалуйста.
возможно, вам следует использовать
queue
для отправки информации в другой поток и другую очередь для получения результата. Иbrowser
должен запустить цикл, который проверит, есть ли URL-адрес в очереди, создаст снимок экрана и отправит его обратноqueue
- а затем дождется следующего сообщения вqueue