diff --git a/back/api/settings.py b/back/api/settings.py index 2957678..8d6fd81 100644 --- a/back/api/settings.py +++ b/back/api/settings.py @@ -27,7 +27,7 @@ SECRET_KEY = "django-insecure-ruo!wst&sb8(f9)j5u4rda-w!673lj_-c0a%gx_t@)ff*q*2ze # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -NGROK_TEMP = '357e-193-228-134-167.ngrok-free.app' +NGROK_TEMP = "a44c-193-228-134-167.ngrok-free.app" ALLOWED_HOSTS = [ "localhost", NGROK_TEMP, diff --git a/back/poetry.lock b/back/poetry.lock index 320a7e5..b157af4 100644 --- a/back/poetry.lock +++ b/back/poetry.lock @@ -22,6 +22,34 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] trio = ["trio (>=0.23)"] +[[package]] +name = "apscheduler" +version = "3.10.4" +description = "In-process task scheduler with Cron-like capabilities" +optional = false +python-versions = ">=3.6" +files = [ + {file = "APScheduler-3.10.4-py3-none-any.whl", hash = "sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661"}, + {file = "APScheduler-3.10.4.tar.gz", hash = "sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a"}, +] + +[package.dependencies] +pytz = "*" +six = ">=1.4.0" +tzlocal = ">=2.0,<3.dev0 || >=4.dev0" + +[package.extras] +doc = ["sphinx", "sphinx-rtd-theme"] +gevent = ["gevent"] +mongodb = ["pymongo (>=3.0)"] +redis = ["redis (>=3.0)"] +rethinkdb = ["rethinkdb (>=2.4.0)"] +sqlalchemy = ["sqlalchemy (>=1.4)"] +testing = ["pytest", "pytest-asyncio", "pytest-cov", "pytest-tornado5"] +tornado = ["tornado (>=4.3)"] +twisted = ["twisted"] +zookeeper = ["kazoo"] + [[package]] name = "asgiref" version = "3.8.1" @@ -541,17 +569,19 @@ cli = ["click (>=5.0)"] [[package]] name = "python-telegram-bot" -version = "21.2" +version = "21.3" description = "We have made you a wrapper you can't refuse" optional = false python-versions = ">=3.8" files = [ - {file = "python-telegram-bot-21.2.tar.gz", hash = "sha256:2ebb462a98f502727d108c00bb50c513a68ddaf9545298c42f13996a9acf8354"}, - {file = "python_telegram_bot-21.2-py3-none-any.whl", hash = "sha256:af0f45d61521126de98f5bdc8a75a9df8b93d0c35d18b018181ca7648a38b017"}, + {file = "python-telegram-bot-21.3.tar.gz", hash = "sha256:1be3c8b6f2b7354418109daa3f23c522e82ed22e7fc904346bee0c7b4aab52ae"}, + {file = "python_telegram_bot-21.3-py3-none-any.whl", hash = "sha256:8f575e6da903edd1e78967b5b481455ee6b27f2804d2384029177eab165f2e93"}, ] [package.dependencies] +APScheduler = {version = ">=3.10.4,<3.11.0", optional = true, markers = "extra == \"job-queue\""} httpx = ">=0.27,<1.0" +pytz = {version = ">=2018.6", optional = true, markers = "extra == \"job-queue\""} [package.extras] all = ["APScheduler (>=3.10.4,<3.11.0)", "aiolimiter (>=1.1.0,<1.2.0)", "cachetools (>=5.3.3,<5.4.0)", "cryptography (>=39.0.1)", "httpx[http2]", "httpx[socks]", "pytz (>=2018.6)", "tornado (>=6.4,<7.0)"] @@ -564,6 +594,17 @@ rate-limiter = ["aiolimiter (>=1.1.0,<1.2.0)"] socks = ["httpx[socks]"] webhooks = ["tornado (>=6.4,<7.0)"] +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + [[package]] name = "requests" version = "2.32.3" @@ -585,6 +626,17 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -661,6 +713,23 @@ files = [ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] +[[package]] +name = "tzlocal" +version = "5.2" +description = "tzinfo object for the local timezone" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, +] + +[package.dependencies] +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + [[package]] name = "urllib3" version = "2.2.1" @@ -681,4 +750,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "58528b70db47be1cef118b365a2830312668b023e992f3aff0674f834f3c7acc" +content-hash = "b0a8df55d3a7a429a89e692c2f74d3f41144a44fb1da3b108105ac9353eab7db" diff --git a/back/pyproject.toml b/back/pyproject.toml index 965447c..404a174 100644 --- a/back/pyproject.toml +++ b/back/pyproject.toml @@ -16,7 +16,7 @@ pillow = "^10.3.0" python-dotenv = "^1.0.1" requests = "^2.32.2" django-cors-headers = "^4.3.1" -python-telegram-bot = "^21.2" +python-telegram-bot = {extras = ["job-queue"], version = "^21.3"} [tool.poetry.group.dev.dependencies] taskipy = "^1.12.2" diff --git a/back/tgbot/apps.py b/back/tgbot/apps.py index a04d9b3..425a1fb 100644 --- a/back/tgbot/apps.py +++ b/back/tgbot/apps.py @@ -1,13 +1,48 @@ +from asgiref.sync import async_to_sync from django.apps import AppConfig -import django.conf -import requests +import asyncio +import threading +import queue +import time -class TgbotConfig(AppConfig): +class TgBotClass(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "tgbot" + is_run = False + app = None + + update_queue = None + my_queue = queue.Queue() + + # @async_to_sync + async def init_bot(self): + from django.conf import settings + from .tgbot import TgBotApp + + tgbot = TgBotApp() + app = await tgbot.init_tg() + await tgbot.set_webhook(f"https://{settings.TGBOT['base_url']}/api/tgbot/") + return app + + def updater(self=None): + while True: + if not TgBotClass.my_queue.empty(): + # Здесь нужно добавить код для обработки очереди + item = TgBotClass.my_queue.get() + print("Беру дело из очереди") + async_to_sync(TgBotClass.app.process_update, force_new_loop=True)(item) + TgBotClass.my_queue.task_done() + time.sleep(1) # Ждем 1 секунду перед следующей итерацией def ready(self): - from django.conf import settings - requests.get(f"https://api.telegram.org/bot{settings.TGBOT['token']}/setWebhook?url=https://{settings.TGBOT['base_url']}/api/tgbot/&drop_pending_updates=true") + import os + if os.environ.get("RUN_MAIN", None) != "true": + return + if TgBotClass.is_run: + return + TgBotClass.is_run = True + TgBotClass.app = async_to_sync(self.init_bot, force_new_loop=True)() + + print(TgBotClass.update_queue) diff --git a/back/tgbot/tgbot.py b/back/tgbot/tgbot.py new file mode 100644 index 0000000..899d1c9 --- /dev/null +++ b/back/tgbot/tgbot.py @@ -0,0 +1,51 @@ +from asgiref.sync import async_to_sync +from django.conf import settings +from telegram import ForceReply, Update +from telegram.ext import Application, CommandHandler, MessageHandler, filters +from telegram.constants import ParseMode, ChatType + +import logging +from .apps import TgBotClass + +logger = logging.getLogger("root") + + +class TgBotApp: + _app = None + + async def start(self, update, context): + user = update.effective_user + logger.info(update) + await update.message.reply_html( + rf"Hi {user.mention_html()}!", + # reply_markup=ForceReply(selective=True), + ) + + async def set_webhook(self, url): + if not self._app: + logger.error("no app") + return + app = self._app + await app.bot.set_webhook(url, allowed_updates=Update.ALL_TYPES) + + async def init_tg(self): + self._app = ( + Application.builder() + .token(settings.TGBOT["token"]) + .concurrent_updates(True) + .updater(None) + .build() + ) + self._app.add_handler(CommandHandler("start", self.start)) + self._app.add_handler(MessageHandler(filters.ChatType.PRIVATE, self.start)) + + logger.info( + { + "app": self._app, + "bot": self._app.bot, + "handlers": self._app.handlers, + } + ) + await self._app.initialize() + await self._app.start() + return self._app diff --git a/back/tgbot/views.py b/back/tgbot/views.py index 872330f..1338e80 100644 --- a/back/tgbot/views.py +++ b/back/tgbot/views.py @@ -1,15 +1,14 @@ import json -from django.conf import settings from telegram import Update -from telegram.ext import Application, CommandHandler, MessageHandler, filters -from telegram.constants import ParseMode, ChatType from asgiref.sync import async_to_sync from rest_framework import viewsets from rest_framework.response import Response +import time +from .apps import TgBotClass from .models import Item from .serializers import ItemSerializer @@ -18,11 +17,6 @@ import logging logger = logging.getLogger("root") -async def start(update, context): - logger.info(update) - await update.message.reply_html(text="123") - - class ItemViewSet(viewsets.ViewSet): queryset = Item.objects.all() serializer_class = ItemSerializer @@ -30,49 +24,10 @@ class ItemViewSet(viewsets.ViewSet): @async_to_sync async def create(self, request): req = json.loads(request.body) - - logger.info(f"que len before put {ptb_application.update_queue.qsize()}") - update_item = Update.de_json(data=req, bot=ptb_application.bot) - await ptb_application.update_queue.put(update_item) - logger.info(f"que len after put {ptb_application.update_queue.qsize()}") + logger.info(req) + logger.info(TgBotClass.app.handlers) + update_item = Update.de_json(data=req, bot=TgBotClass.app.bot) + # TgBotClass.my_queue.put(update_item) + await TgBotClass.app.process_update(update_item) - # await self.ptb_application.bot.send_message( - # chat_id=req["message"]["from"]["id"], - # text=f'Вы прислали текст `{req["message"]["text"]}`', - # parse_mode=ParseMode.MARKDOWN_V2, - # reply_to_message_id=req["message"]["message_id"], - # ) - # if req["message"]["chat"]["type"] != ChatType.PRIVATE: - # return Response() - - # if req["message"]["text"] == "/add": - # await self.ptb_application.bot.send_message( - # chat_id=req["message"]["from"]["id"], - # text=f'Вы хотите создать новую инвентаризацию?', - # parse_mode=ParseMode.MARKDOWN_V2, - # reply_to_message_id=req["message"]["message_id"], - # ) - - return Response({"test": "create"}) - - -async def init_tg(): - ptb_application = ( - Application.builder().token(settings.TGBOT["token"]).concurrent_updates(True).updater(None).build() - ) - ptb_application.add_handler(CommandHandler("start", start)) - ptb_application.add_handler( - MessageHandler(filters.ChatType.PRIVATE, callback=start) - ) - logger.info( - { - "app": ptb_application, - "bot": ptb_application.bot, - "handlers": ptb_application.handlers, - } - ) - await ptb_application.initialize() - await ptb_application.start() - return ptb_application - -ptb_application = async_to_sync(init_tg)() + return Response({"result": "pass data to tgbot"})