trial
All checks were successful
Deploy Dev / Build (pull_request) Successful in 5s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 21s

This commit is contained in:
emmatveev 2024-11-17 22:52:50 +03:00
parent b8f0624665
commit 272233fc67
16 changed files with 332 additions and 168 deletions

View File

@ -3,15 +3,45 @@ version: "3.4"
services: services:
bot: poll:
image: mathwave/sprint-repo:ruz-bot
environment:
STAGE: "development"
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_DEV
QUEUES_TOKEN: $QUEUES_TOKEN_DEV
command: poll
deploy:
mode: replicated
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
worker:
image: mathwave/sprint-repo:ruz-bot image: mathwave/sprint-repo:ruz-bot
environment: environment:
MONGO_HOST: "mongo.develop.sprinthub.ru" MONGO_HOST: "mongo.develop.sprinthub.ru"
STAGE: "development" STAGE: "development"
MONGO_PASSWORD: $MONGO_PASSWORD_DEV MONGO_PASSWORD: $MONGO_PASSWORD_DEV
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_DEV
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
command: bot QUEUES_TOKEN: $QUEUES_TOKEN_DEV
command: worker
deploy:
mode: replicated
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
mailbox:
image: mathwave/sprint-repo:ruz-bot
environment:
STAGE: "development"
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_DEV
QUEUES_TOKEN: $QUEUES_TOKEN_DEV
command: mailbox
deploy: deploy:
mode: replicated mode: replicated
restart_policy: restart_policy:
@ -26,8 +56,8 @@ services:
MONGO_HOST: "mongo.develop.sprinthub.ru" MONGO_HOST: "mongo.develop.sprinthub.ru"
STAGE: "development" STAGE: "development"
MONGO_PASSWORD: $MONGO_PASSWORD_DEV MONGO_PASSWORD: $MONGO_PASSWORD_DEV
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_DEV
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
QUEUES_TOKEN: $QUEUES_TOKEN_DEV
command: fetch command: fetch
deploy: deploy:
mode: replicated mode: replicated
@ -43,8 +73,8 @@ services:
MONGO_HOST: "mongo.develop.sprinthub.ru" MONGO_HOST: "mongo.develop.sprinthub.ru"
STAGE: "development" STAGE: "development"
MONGO_PASSWORD: $MONGO_PASSWORD_DEV MONGO_PASSWORD: $MONGO_PASSWORD_DEV
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_DEV
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
QUEUES_TOKEN: $QUEUES_TOKEN_DEV
command: notify command: notify
deploy: deploy:
mode: replicated mode: replicated
@ -62,8 +92,8 @@ services:
MONGO_HOST: "mongo.develop.sprinthub.ru" MONGO_HOST: "mongo.develop.sprinthub.ru"
STAGE: "development" STAGE: "development"
MONGO_PASSWORD: $MONGO_PASSWORD_DEV MONGO_PASSWORD: $MONGO_PASSWORD_DEV
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_DEV
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
QUEUES_TOKEN: $QUEUES_TOKEN_DEV
command: api command: api
deploy: deploy:
mode: replicated mode: replicated

View File

@ -3,24 +3,49 @@ version: "3.4"
services: services:
bot: poll:
image: mathwave/sprint-repo:ruz-bot
environment:
STAGE: "production"
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_PROD
QUEUES_TOKEN: $QUEUES_TOKEN_PROD
command: poll
deploy:
mode: replicated
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
worker:
image: mathwave/sprint-repo:ruz-bot image: mathwave/sprint-repo:ruz-bot
environment: environment:
MONGO_HOST: "mongo.sprinthub.ru" MONGO_HOST: "mongo.sprinthub.ru"
STAGE: "production" STAGE: "production"
MONGO_PASSWORD: $MONGO_PASSWORD_PROD MONGO_PASSWORD: $MONGO_PASSWORD_PROD
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_PROD
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
DEBUG: "false" QUEUES_TOKEN: $QUEUES_TOKEN_PROD
command: bot command: worker
deploy:
mode: replicated
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
mailbox:
image: mathwave/sprint-repo:ruz-bot
environment:
STAGE: "production"
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_PROD
QUEUES_TOKEN: $QUEUES_TOKEN_PROD
command: mailbox
deploy: deploy:
mode: replicated mode: replicated
restart_policy: restart_policy:
condition: any condition: any
placement:
constraints:
- node.role == worker
- node.labels.zone == ru
update_config: update_config:
parallelism: 1 parallelism: 1
order: start-first order: start-first
@ -31,9 +56,9 @@ services:
MONGO_HOST: "mongo.sprinthub.ru" MONGO_HOST: "mongo.sprinthub.ru"
STAGE: "production" STAGE: "production"
MONGO_PASSWORD: $MONGO_PASSWORD_PROD MONGO_PASSWORD: $MONGO_PASSWORD_PROD
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_PROD
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
DEBUG: "false" DEBUG: "false"
QUEUES_TOKEN: $QUEUES_TOKEN_PROD
command: fetch command: fetch
deploy: deploy:
mode: replicated mode: replicated
@ -53,9 +78,9 @@ services:
MONGO_HOST: "mongo.sprinthub.ru" MONGO_HOST: "mongo.sprinthub.ru"
STAGE: "production" STAGE: "production"
MONGO_PASSWORD: $MONGO_PASSWORD_PROD MONGO_PASSWORD: $MONGO_PASSWORD_PROD
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_PROD
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
DEBUG: "false" DEBUG: "false"
QUEUES_TOKEN: $QUEUES_TOKEN_PROD
command: notify command: notify
deploy: deploy:
mode: replicated mode: replicated
@ -77,7 +102,6 @@ services:
MONGO_HOST: "mongo.sprinthub.ru" MONGO_HOST: "mongo.sprinthub.ru"
STAGE: "production" STAGE: "production"
MONGO_PASSWORD: $MONGO_PASSWORD_PROD MONGO_PASSWORD: $MONGO_PASSWORD_PROD
TELEGRAM_TOKEN: $TELEGRAM_TOKEN_PROD
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
DEBUG: "false" DEBUG: "false"
command: api command: api

View File

@ -42,4 +42,5 @@ jobs:
TELEGRAM_TOKEN_DEV: ${{ secrets.TELEGRAM_TOKEN_DEV }} TELEGRAM_TOKEN_DEV: ${{ secrets.TELEGRAM_TOKEN_DEV }}
MONGO_PASSWORD_DEV: ${{ secrets.MONGO_PASSWORD_DEV }} MONGO_PASSWORD_DEV: ${{ secrets.MONGO_PASSWORD_DEV }}
PLATFORM_SECURITY_TOKEN: ${{ secrets.PLATFORM_SECURITY_TOKEN }} PLATFORM_SECURITY_TOKEN: ${{ secrets.PLATFORM_SECURITY_TOKEN }}
QUEUES_TOKEN_DEV: ${{ secrets.QUEUES_TOKEN_DEV }}
run: docker stack deploy --with-registry-auth -c ./.deploy/deploy-dev.yaml ruz-bot run: docker stack deploy --with-registry-auth -c ./.deploy/deploy-dev.yaml ruz-bot

View File

@ -42,4 +42,5 @@ jobs:
TELEGRAM_TOKEN_PROD: ${{ secrets.TELEGRAM_TOKEN_PROD }} TELEGRAM_TOKEN_PROD: ${{ secrets.TELEGRAM_TOKEN_PROD }}
MONGO_PASSWORD_PROD: ${{ secrets.MONGO_PASSWORD_PROD }} MONGO_PASSWORD_PROD: ${{ secrets.MONGO_PASSWORD_PROD }}
PLATFORM_SECURITY_TOKEN: ${{ secrets.PLATFORM_SECURITY_TOKEN }} PLATFORM_SECURITY_TOKEN: ${{ secrets.PLATFORM_SECURITY_TOKEN }}
QUEUES_TOKEN_PROD: ${{ secrets.QUEUES_TOKEN_PROD }}
run: docker stack deploy --with-registry-auth -c ./.deploy/deploy-prod.yaml ruz-bot run: docker stack deploy --with-registry-auth -c ./.deploy/deploy-prod.yaml ruz-bot

View File

@ -3,73 +3,75 @@ from flask import Flask, request
import settings import settings
from helpers.alice import Processor from helpers.alice import Processor
from helpers.mongo import mongo from helpers.mongo import mongo
from daemons import base
def api(): class Daemon(base.Daemon):
app = Flask(__name__) def execute(self):
app = Flask(__name__)
@app.route('/stats/json', methods=['GET']) @app.route('/stats/json', methods=['GET'])
def stats_json(): def stats_json():
all_users = mongo.users_collection.count_documents({}) all_users = mongo.users_collection.count_documents({})
teachers = mongo.users_collection.count_documents({"is_teacher": True}) teachers = mongo.users_collection.count_documents({"is_teacher": True})
return { return {
"Всего пользователей": all_users, "Всего пользователей": all_users,
"Пользователей прошедших регистрацию": mongo.users_collection.count_documents({'email': {'$ne': None}}), "Пользователей прошедших регистрацию": mongo.users_collection.count_documents({'email': {'$ne': None}}),
"Преподавателей": teachers, "Преподавателей": teachers,
"Отписались от уведомлений": mongo.users_collection.count_documents({'notify_minutes': None}), "Отписались от уведомлений": mongo.users_collection.count_documents({'notify_minutes': None}),
"Отправлено уведомлений за сегодня": mongo.lessons_collection.count_documents({'notified': True}), "Отправлено уведомлений за сегодня": mongo.lessons_collection.count_documents({'notified': True}),
"Проиндексировано занятий из РУЗа": mongo.lessons_collection.count_documents({}), "Проиндексировано занятий из РУЗа": mongo.lessons_collection.count_documents({}),
"Пользователей из Москвы": mongo.users_collection.count_documents( "Пользователей из Москвы": mongo.users_collection.count_documents(
{'campus': 'Москва'}) + mongo.users_collection.count_documents({'campus': {'$exists': False}}), {'campus': 'Москва'}) + mongo.users_collection.count_documents({'campus': {'$exists': False}}),
"Пользователей из Москвы (регистрация)": mongo.users_collection.count_documents( "Пользователей из Москвы (регистрация)": mongo.users_collection.count_documents(
{'campus': 'Москва', 'email': {'$ne': None}}) + mongo.users_collection.count_documents( {'campus': 'Москва', 'email': {'$ne': None}}) + mongo.users_collection.count_documents(
{'campus': {'$exists': False}, 'email': {'$ne': None}}), {'campus': {'$exists': False}, 'email': {'$ne': None}}),
"Пользователей из Перми": mongo.users_collection.count_documents({'campus': 'Пермь'}), "Пользователей из Перми": mongo.users_collection.count_documents({'campus': 'Пермь'}),
"Пользователей из Перми (регистрация)": mongo.users_collection.count_documents( "Пользователей из Перми (регистрация)": mongo.users_collection.count_documents(
{'campus': 'Пермь', 'email': {'$ne': None}}), {'campus': 'Пермь', 'email': {'$ne': None}}),
"Пользователей из Нижнего Новгорода": mongo.users_collection.count_documents({'campus': 'Нижний Новгород'}), "Пользователей из Нижнего Новгорода": mongo.users_collection.count_documents({'campus': 'Нижний Новгород'}),
"Пользователей из Нижнего Новгорода (регистрация)": mongo.users_collection.count_documents( "Пользователей из Нижнего Новгорода (регистрация)": mongo.users_collection.count_documents(
{'campus': 'Нижний Новгород', 'email': {'$ne': None}}), {'campus': 'Нижний Новгород', 'email': {'$ne': None}}),
"Пользователей из Санкт-Петербурга": mongo.users_collection.count_documents({'campus': 'Санкт-Петербург'}), "Пользователей из Санкт-Петербурга": mongo.users_collection.count_documents({'campus': 'Санкт-Петербург'}),
"Пользователей из Санкт-Петербурга (регистрация)": mongo.users_collection.count_documents( "Пользователей из Санкт-Петербурга (регистрация)": mongo.users_collection.count_documents(
{'campus': 'Санкт-Петербург', 'email': {'$ne': None}}) {'campus': 'Санкт-Петербург', 'email': {'$ne': None}})
}
@app.route('/stats', methods=['GET'])
def stats():
all_users = mongo.users_collection.count_documents({})
teachers = mongo.users_collection.count_documents({"is_teacher": True})
text = f"Всего пользователей: {all_users}<br>" \
f"Пользователей прошедших регистрацию: {mongo.users_collection.count_documents({'email': {'$ne': None}})}<br>" \
f"Студентов: {all_users - teachers}<br>" \
f"Преподавателей: {teachers}<br>" \
f"Отписались от уведомлений: {mongo.users_collection.count_documents({'notify_minutes': None})}<br>" \
f"Отправлено уведомлений за сегодня: {mongo.lessons_collection.count_documents({'notified': True})}<br>" \
f"Проиндексировано занятий из РУЗа: {mongo.lessons_collection.count_documents({})}<br>" \
f"<br>" \
f"<br>" \
f"Пользователей из Москвы: {mongo.users_collection.count_documents({'campus': 'Москва'}) + mongo.users_collection.count_documents({'campus': {'$exists': False}})}<br>" \
f"Пользователей из Москвы (регистрация): {mongo.users_collection.count_documents({'campus': 'Москва', 'email': {'$ne': None}}) + mongo.users_collection.count_documents({'campus': {'$exists': False}, 'email': {'$ne': None}})}<br>" \
f"Пользователей из Перми: {mongo.users_collection.count_documents({'campus': 'Пермь'})}<br>" \
f"Пользователей из Перми (регистрация): {mongo.users_collection.count_documents({'campus': 'Пермь', 'email': {'$ne': None}})}<br>" \
f"Пользователей из Нижнего Новгорода: {mongo.users_collection.count_documents({'campus': 'Нижний Новгород'})}<br>" \
f"Пользователей из Нижнего Новгорода (регистрация): {mongo.users_collection.count_documents({'campus': 'Нижний Новгород', 'email': {'$ne': None}})}<br>" \
f"Пользователей из Санкт-Петербурга: {mongo.users_collection.count_documents({'campus': 'Санкт-Петербург'})}<br>" \
f"Пользователей из Санкт-Петербурга (регистрация): {mongo.users_collection.count_documents({'campus': 'Санкт-Петербург', 'email': {'$ne': None}})}<br>"
return text
@app.route('/alice', methods=['POST'])
def alice():
req = request.json
processor = Processor(req)
response = {
"version": req['version'],
"session": req['session'],
"response": {
"end_session": False
} }
}
response['response'].update(processor.process())
return response
app.run(host="0.0.0.0", port=1238, debug=settings.DEBUG) @app.route('/stats', methods=['GET'])
def stats():
all_users = mongo.users_collection.count_documents({})
teachers = mongo.users_collection.count_documents({"is_teacher": True})
text = f"Всего пользователей: {all_users}<br>" \
f"Пользователей прошедших регистрацию: {mongo.users_collection.count_documents({'email': {'$ne': None}})}<br>" \
f"Студентов: {all_users - teachers}<br>" \
f"Преподавателей: {teachers}<br>" \
f"Отписались от уведомлений: {mongo.users_collection.count_documents({'notify_minutes': None})}<br>" \
f"Отправлено уведомлений за сегодня: {mongo.lessons_collection.count_documents({'notified': True})}<br>" \
f"Проиндексировано занятий из РУЗа: {mongo.lessons_collection.count_documents({})}<br>" \
f"<br>" \
f"<br>" \
f"Пользователей из Москвы: {mongo.users_collection.count_documents({'campus': 'Москва'}) + mongo.users_collection.count_documents({'campus': {'$exists': False}})}<br>" \
f"Пользователей из Москвы (регистрация): {mongo.users_collection.count_documents({'campus': 'Москва', 'email': {'$ne': None}}) + mongo.users_collection.count_documents({'campus': {'$exists': False}, 'email': {'$ne': None}})}<br>" \
f"Пользователей из Перми: {mongo.users_collection.count_documents({'campus': 'Пермь'})}<br>" \
f"Пользователей из Перми (регистрация): {mongo.users_collection.count_documents({'campus': 'Пермь', 'email': {'$ne': None}})}<br>" \
f"Пользователей из Нижнего Новгорода: {mongo.users_collection.count_documents({'campus': 'Нижний Новгород'})}<br>" \
f"Пользователей из Нижнего Новгорода (регистрация): {mongo.users_collection.count_documents({'campus': 'Нижний Новгород', 'email': {'$ne': None}})}<br>" \
f"Пользователей из Санкт-Петербурга: {mongo.users_collection.count_documents({'campus': 'Санкт-Петербург'})}<br>" \
f"Пользователей из Санкт-Петербурга (регистрация): {mongo.users_collection.count_documents({'campus': 'Санкт-Петербург', 'email': {'$ne': None}})}<br>"
return text
@app.route('/alice', methods=['POST'])
def alice():
req = request.json
processor = Processor(req)
response = {
"version": req['version'],
"session": req['session'],
"response": {
"end_session": False
}
}
response['response'].update(processor.process())
return response
app.run(host="0.0.0.0", port=1238, debug=settings.DEBUG)

3
daemons/base.py Normal file
View File

@ -0,0 +1,3 @@
class Daemon:
def execute(self):
raise NotImplementedError

View File

@ -1,20 +0,0 @@
import os
import telebot
from telebot.types import Message
from helpers.mongo import mongo
bot = telebot.TeleBot(os.getenv("TELEGRAM_TOKEN"))
@bot.message_handler(commands=['start'])
def on_start(message: Message):
mongo.users_collection.delete_many({"chat_id": message.chat.id})
do_action(message)
@bot.message_handler()
def do_action(message: Message):
from helpers.answer import Answer
Answer(message).process()

View File

@ -6,6 +6,8 @@ from helpers import now, campus_timdelta
from helpers.mongo import mongo from helpers.mongo import mongo
from helpers.ruz import ruz from helpers.ruz import ruz
from daemons import base
def fetch_schedule_for_user(user: dict): def fetch_schedule_for_user(user: dict):
today = now(user) today = now(user)
@ -75,16 +77,17 @@ def delete_old():
mongo.lessons_collection.delete_many({"end": {"$lte": datetime.datetime.now() - datetime.timedelta(days=1)}}) mongo.lessons_collection.delete_many({"end": {"$lte": datetime.datetime.now() - datetime.timedelta(days=1)}})
def fetch(): class Daemon(base.Daemon):
while True: def execute(self):
logging.info("fetch start") while True:
begin = datetime.datetime.now() logging.info("fetch start")
if begin.hour > 22 or begin.hour < 7: begin = datetime.datetime.now()
logging.info("Too late, sleeping") if begin.hour > 22 or begin.hour < 7:
sleep(30 * 60) logging.info("Too late, sleeping")
continue sleep(30 * 60)
process() continue
end = datetime.datetime.now() process()
logging.info('fetch finished') end = datetime.datetime.now()
logging.info("time elapsed %s", (end - begin).total_seconds()) logging.info('fetch finished')
delete_old() logging.info("time elapsed %s", (end - begin).total_seconds())
delete_old()

28
daemons/mailbox.py Normal file
View File

@ -0,0 +1,28 @@
import telebot
import os
from daemons import base
from utils import queues
class Daemon(base.BaseDaemon, queues.TasksHandlerMixin):
def __init__(self):
super().__init__()
self.bot = telebot.TeleBot(os.getenv("TELEGRAM_TOKEN"))
@property
def queue_name(self):
return 'pizda_bot_mailbox'
def execute(self):
self.poll()
def process(self, payload):
body = {
'chat_id': payload['chat_id'],
'text': payload['text'],
}
reply_markup = payload.get('reply_markup')
if reply_markup:
body['reply_markup'] = reply_markup
self.bot.send_message(**body, parse_mode='Markdown')

View File

@ -2,12 +2,12 @@ import datetime
import logging import logging
from time import sleep from time import sleep
from telebot.apihelper import ApiTelegramException
from daemons.bot import bot
from helpers import now from helpers import now
from helpers.mongo import mongo from helpers.mongo import mongo
from helpers.ruz import ruz from helpers.ruz import ruz
from daemons import base
from utils import queues
def process(): def process():
@ -25,13 +25,7 @@ def process():
ans += f"🧑‍🏫 {(lesson['lecturer'] or 'Неизвестно')}\n" ans += f"🧑‍🏫 {(lesson['lecturer'] or 'Неизвестно')}\n"
if lesson.get('link', None): if lesson.get('link', None):
ans += f"🔗 {lesson['link']}" ans += f"🔗 {lesson['link']}"
try: queues.set_task('ruz_bot_mailbox', {'text': f"Через {user['notify_minutes']} минут у тебя занятие!\n" + ans, 'chat_id': user["chat_id"]}, 1)
bot.send_message(
user["chat_id"],
f"Через {user['notify_minutes']} минут у тебя занятие!\n" + ans
)
except ApiTelegramException:
pass
mongo.lessons_collection.update_one({"_id": lesson['_id']}, {"$set": {"notified": True}}) mongo.lessons_collection.update_one({"_id": lesson['_id']}, {"$set": {"notified": True}})
time_now = datetime.datetime.now() time_now = datetime.datetime.now()
for user in mongo.users_collection.find({"next_daily_notify_time": {"$lte": time_now}}): for user in mongo.users_collection.find({"next_daily_notify_time": {"$lte": time_now}}):
@ -46,11 +40,7 @@ def process():
else: else:
text = ruz.schedule_builder(lessons) text = ruz.schedule_builder(lessons)
try: try:
bot.send_message( queues.set_task('ruz_bot_mailbox', {'text': f"Уведомляю о занятиях! Твое расписание на {'сегодня' if user.get('daily_notify_today', True) else 'завтра'}:\n" + text, 'chat_id': user["chat_id"]}, 1)
user["chat_id"],
f"Уведомляю о занятиях! Твое расписание на {'сегодня' if user.get('daily_notify_today', True) else 'завтра'}:\n" + text,
parse_mode='Markdown'
)
except: except:
pass pass
mongo.users_collection.update_one( mongo.users_collection.update_one(
@ -72,34 +62,29 @@ def process():
ans += f"🧑‍🏫 {(lesson['lecturer'] or 'Неизвестно')}\n" ans += f"🧑‍🏫 {(lesson['lecturer'] or 'Неизвестно')}\n"
if lesson.get('link', None): if lesson.get('link', None):
ans += f"🔗 {lesson['link']}" ans += f"🔗 {lesson['link']}"
try: mess = "Пары начутся через "
mess = "Пары начутся через " if user['first_lesson_notify'] == 30:
if user['first_lesson_notify'] == 30: mess += "30 минут"
mess += "30 минут" elif user['first_lesson_notify'] == 60:
elif user['first_lesson_notify'] == 60: mess += "1 час"
mess += "1 час" elif user['first_lesson_notify'] == 4 * 60:
elif user['first_lesson_notify'] == 4 * 60: mess += "4 часа"
mess += "4 часа" else:
else: mess += "12 часов"
mess += "12 часов" mess += "!\n\nТвоя первая пара:\n\n" + ans
mess += "!\n\nТвоя первая пара:\n\n" + ans queues.set_task('ruz_bot_mailbox', {'text': mess, 'chat_id': user["chat_id"]}, 1)
bot.send_message(
user["chat_id"],
mess
)
except ApiTelegramException:
pass
start_of_day = datetime.datetime(year=time_now.year, month=time_now.month, day=time_now.day) start_of_day = datetime.datetime(year=time_now.year, month=time_now.month, day=time_now.day)
mongo.lessons_collection.update_many({"begin": {"$gte": start_of_day, "$lt": (start_of_day + datetime.timedelta(days=1))}, "user_email": user["email"]}, {"$set": {"notified_today": True}}) mongo.lessons_collection.update_many({"begin": {"$gte": start_of_day, "$lt": (start_of_day + datetime.timedelta(days=1))}, "user_email": user["email"]}, {"$set": {"notified_today": True}})
break break
def notify(): class Daemon(base.Daemon):
while True: def execute(self):
logging.info("notify start") while True:
begin = datetime.datetime.now() logging.info("notify start")
process() begin = datetime.datetime.now()
end = datetime.datetime.now() process()
logging.info('notify finished') end = datetime.datetime.now()
logging.info("time elapsed %s", (end - begin).total_seconds()) logging.info('notify finished')
sleep(63 - end.second) logging.info("time elapsed %s", (end - begin).total_seconds())
sleep(63 - end.second)

15
daemons/poll.py Normal file
View File

@ -0,0 +1,15 @@
import os
import telebot
from daemons import base
from telebot import types
from utils import queues
class Daemon(base.BaseDaemon):
def execute(self):
bot = telebot.TeleBot(os.getenv("TELEGRAM_TOKEN"))
@bot.message_handler()
def do_action(message: types.Message):
queues.set_task('ruz_bot_worker', message.json, 1)
bot.polling()

19
daemons/worker.py Normal file
View File

@ -0,0 +1,19 @@
from daemons import base
from utils import queues
import json
from telebot.types import Message
class Daemon(base.BaseDaemon, queues.TasksHandlerMixin):
@property
def queue_name(self):
return 'ruz_bot_worker'
def execute(self):
self.poll()
def process(self, payload):
message: Message = Message.de_json(json.dumps(payload))
from helpers.answer import Answer
Answer(message).process()

View File

@ -2,10 +2,6 @@ import logging.config
import sys import sys
import settings import settings
from daemons.api import api
from daemons.bot import bot
from daemons.fetch import fetch
from daemons.notify import notify
import locale import locale
@ -13,17 +9,25 @@ logging.config.dictConfig(settings.logging_config)
locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8') locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8')
arg = sys.argv[-1] arg = sys.argv[-1]
if arg == "bot": if arg == "poll":
logging.info("bot is starting") logging.info("poll is starting")
bot.polling() from daemons.poll import Daemon
elif arg == 'worker':
logging.info("worker is starting")
from daemons.worker import Daemon
elif arg == 'mailbox':
logging.info("mailbox is starting")
from daemons.mailbox import Daemon
elif arg == "fetch": elif arg == "fetch":
logging.info("fetch is starting") logging.info("fetch is starting")
fetch() from daemons.fetch import Daemon
elif arg == "notify": elif arg == "notify":
logging.info("notify is starting") logging.info("notify is starting")
notify() from daemons.notify import Daemon
elif arg == "api": elif arg == "api":
logging.info("api is starting") logging.info("api is starting")
api() from daemons.api import Daemon
else: else:
raise ValueError(f"Unknown param {arg}") raise ValueError(f"Unknown param {arg}")
Daemon().execute()

View File

@ -4,7 +4,6 @@ from random import choice
from telebot.types import Message, ReplyKeyboardRemove from telebot.types import Message, ReplyKeyboardRemove
from daemons.bot import bot
from daemons.fetch import fetch_schedule_for_user from daemons.fetch import fetch_schedule_for_user
from helpers import get_next_daily_notify_time from helpers import get_next_daily_notify_time
from helpers.keyboards import main_keyboard, notify_keyboard, yes_no_keyboard, again_keyboard, no_daily_notify, \ from helpers.keyboards import main_keyboard, notify_keyboard, yes_no_keyboard, again_keyboard, no_daily_notify, \
@ -12,6 +11,7 @@ from helpers.keyboards import main_keyboard, notify_keyboard, yes_no_keyboard, a
from helpers.mongo import mongo from helpers.mongo import mongo
from helpers.sprint_platform import platform from helpers.sprint_platform import platform
from helpers.ruz import ruz from helpers.ruz import ruz
from utils import queues
class User: class User:
@ -32,6 +32,8 @@ class Answer:
def __init__(self, message: Message): def __init__(self, message: Message):
self.message = message self.message = message
self.message_text = message.text or message.caption or "" self.message_text = message.text or message.caption or ""
if self.message_text.startswith('/start'):
mongo.users_collection.delete_many({"chat_id": message.chat.id})
user = mongo.users_collection.find_one({"chat_id": message.chat.id}) user = mongo.users_collection.find_one({"chat_id": message.chat.id})
if user is None: if user is None:
user = { user = {
@ -72,7 +74,10 @@ class Answer:
def send_message(self, text, reply_markup=None, remove_keyboard=True, **kwargs): def send_message(self, text, reply_markup=None, remove_keyboard=True, **kwargs):
if reply_markup is None and remove_keyboard: if reply_markup is None and remove_keyboard:
reply_markup = ReplyKeyboardRemove() reply_markup = ReplyKeyboardRemove()
bot.send_message(self.user['chat_id'], text, reply_markup=reply_markup, **kwargs) body = {'text': text, 'chat_id': self.user['chat_id']}
if reply_markup:
body['reply_markup'] = reply_markup.to_json()
queues.set_task('ruz_bot_mailbox', body, 1)
def set_state(self, state: str): def set_state(self, state: str):
self.user['state'] = state self.user['state'] = state

0
utils/__init__.py Normal file
View File

64
utils/queues.py Normal file
View File

@ -0,0 +1,64 @@
import json
import os
import requests
import time
stage = os.getenv("STAGE", 'local')
if stage == 'development':
QUEUES_URL = 'https://queues.develop.sprinthub.ru'
elif stage == 'production':
QUEUES_URL = 'https://queues.sprinthub.ru'
else:
QUEUES_URL = None
token = os.getenv('QUEUES_TOKEN')
class QueuesException(Exception):
...
class TasksHandlerMixin:
def poll(self):
while True:
if QUEUES_URL is None:
data = {'payload': json.loads(input('Input message: '))}
else:
response = requests.get(f'{QUEUES_URL}/api/v1/take', headers={'queue': self.queue_name, 'X-Queues-Token': token})
if response.status_code == 404:
time.sleep(0.2)
continue
if response.status_code == 403:
raise NotImplemented('QUEUE_TOKEN is incorrect')
data = response.json()
try:
self.process(data['payload'])
except Exception as exc:
print(f'Error processing message id={data["id"]}, payload={data["payload"]}, exc={exc}')
continue
if QUEUES_URL is None:
continue
try:
resp = requests.post(f'{QUEUES_URL}/api/v1/finish', json={'id': data['id']}, headers={'X-Queues-Token': token})
if resp.status_code != 202:
raise QueuesException
except:
print(f'Failed to finish task id={data["id"]}')
@property
def queue_name(self):
raise NotImplemented
def process(self, payload):
raise NotImplemented
def set_task(queue_name: str, payload: dict, seconds_to_execute: int, delay: int|None = None):
resp = requests.post(f'{QUEUES_URL}/api/v1/put', headers={'queue': queue_name, 'X-Queues-Token': token}, json={
'payload': payload,
'seconds_to_execute': seconds_to_execute,
'delay': delay,
})
if resp.status_code != 202:
raise QueuesException