我希望我的 FastAPI 应用程序能够始终访问
python-telegram-bot的实际
bot_data
。
我需要它,所以当我在 FastAPI 中调用某个端点时,例如,可以向所有聊天发送消息,存储在 bot_data
. 中
据我了解问题:
bot.run_polling()
和uvicorn.run(...)
启动两个独立的异步循环。我需要将它们合二为一。
我的简化设置如下所示。
最初我尝试不使用线程而是使用
multiprocessing.Proccess
运行,但是那样我的 bot_data
总是空的 - 我认为这是因为 bot 数据不在进程之间共享所以整个事情必须在一个进程中。在这里,我无法在一个异步循环中运行所有这些东西。
# main.py
# python3.10
# pip install fastapi[all] python-telegram-bot
from threading import Thread
import uvicorn
from telegram.ext import Application, ApplicationBuilder, PicklePersistence
from fastapi import FastAPI, Request
BOT_TOKEN = "telegram-bot-token"
MY_CHAT = 123456
class MyApp(FastAPI):
def add_bot(self, bot_app: Application):
self.bot_app = bot_app
async def post_init(app: Application):
app.bot_data["key"] = 42
f_app = MyApp()
@f_app.get("/")
async def test(request: Request):
app: MyApp = request.app
bot_app: Application = app.bot_app
val = bot_app.bot_data.get('key')
print(f"{val=}")
await bot_app.bot.send_message(MY_CHAT, f"Should be 42: {val}")
if __name__ == "__main__":
pers = PicklePersistence("storage")
bot_app = ApplicationBuilder().token(BOT_TOKEN).post_init(post_init).persistence(pers).build()
f_app.add_bot(bot_app)
t1 = Thread(target=uvicorn.run, args=(f_app,), kwargs={"port": 9001})
t1.start()
# --- Launching polling in main thread causes
# telegram.error.NetworkError: Unknown error in HTTP implementation:
# RuntimeError('<asyncio.locks.Event object at 0x7f2764e6fd00 [unset]> is bound to a different event loop')
# message is sent and value is correct, BUT app breaks and return 500
# bot_app.run_polling()
# --- Launching polling in separate thread causes
# RuntimeError: There is no current event loop in thread 'Thread-2 (run_polling)'.
# t2 = Thread(target=bot_app.run_polling)
# t2.start()
# --- Launching with asyncio causes:
# ValueError: a coroutine was expected, got <bound method Application.run_polling ...
# import asyncio
# t2 = Thread(target=asyncio.run, args=(bot_app.run_polling,))
# t2.start()
t1.join()
UPD-1:
感谢@MatsLindh,我创建了下一个传递给main块的函数,但它工作inconsistent。有时
bot.run_polling
(获得正确的循环并且一切正常,但其他时候并因错误而中断,因为存在不同的循环):
import asyncio
from uvicorn import Config, Server
# --snip--
def run(app: FastAPI, bot:Application):
loop = asyncio.new_event_loop()
print(f"{loop=}")
server = Server(Config(app=app, port=9001))
loop.create_task(server.serve())
t = Thread(target=loop.run_forever)
t.start()
print(f"{loop=}")
bot.run_polling()
t.join()
# --snip--
if __name__ == "__main__":
# --snip--
run(f_app, bot_app)
我也知道我可以将
bot.run_polling()
分解成几个单独的调用,这些调用在内部聚合,但我相信它应该只与那个 shortuct 函数一起工作。