Compare commits

..

16 Commits

Author SHA1 Message Date
05d9cdc7b1 Merge pull request 'fix' (#17) from master into dev
Reviewed-on: #17
2024-11-28 22:58:47 +03:00
a209246513 Merge pull request 'fix' (#15) from master into dev
Reviewed-on: #15
2024-11-27 16:18:17 +03:00
0922b5a4a4 Merge pull request 'fix' (#14) from master into dev
Reviewed-on: #14
2024-11-27 16:14:35 +03:00
2ee74e70ac Merge pull request 'fix' (#13) from master into dev
Reviewed-on: #13
2024-11-27 14:46:14 +03:00
da93092232 Merge pull request 'fix' (#12) from master into dev
Reviewed-on: #12
2024-11-27 11:20:53 +03:00
60b933496f Merge pull request 'fix' (#11) from master into dev
Reviewed-on: #11
2024-11-27 11:15:30 +03:00
82b99ae803 Merge pull request 'fix' (#10) from master into dev
Reviewed-on: #10
2024-11-27 11:12:37 +03:00
c8f65a0ebb Merge pull request 'fix' (#9) from master into dev
Reviewed-on: #9
2024-11-27 11:10:47 +03:00
6401a40f11 Merge pull request 'fix' (#8) from master into dev
Reviewed-on: #8
2024-11-27 04:32:35 +03:00
32197fd699 Merge pull request 'fix' (#7) from master into dev
Reviewed-on: #7
2024-11-27 04:26:41 +03:00
e69ee8767a Merge pull request 'fix' (#6) from master into dev
Reviewed-on: #6
2024-11-27 04:19:26 +03:00
54f7581657 Merge pull request 'fix' (#5) from master into dev
Reviewed-on: #5
2024-11-27 04:14:58 +03:00
c6a2710087 Merge pull request 'fix' (#4) from master into dev
Reviewed-on: #4
2024-11-27 04:11:15 +03:00
42fc5552ab Merge pull request 'fix' (#3) from master into dev
Reviewed-on: #3
2024-11-27 04:09:16 +03:00
499eed49e0 Merge pull request 'fix' (#2) from master into dev
Reviewed-on: #2
2024-11-27 04:07:49 +03:00
349df7eb17 Merge pull request 'req' (#1) from master into dev
Reviewed-on: #1
2024-11-27 04:06:16 +03:00
6 changed files with 33 additions and 86 deletions

View File

@@ -22,7 +22,6 @@ services:
networks: networks:
- configurator - configurator
- queues - queues
- monitoring
environment: environment:
STAGE: "production" STAGE: "production"
command: mailbox command: mailbox
@@ -39,5 +38,3 @@ networks:
external: true external: true
queues: queues:
external: true external: true
monitoring:
external: true

3
.gitignore vendored
View File

@@ -119,6 +119,3 @@ GitHub.sublime-settings
.history .history
local_platform.json local_platform.json
*pb2*
schemas

View File

@@ -1,5 +1,3 @@
import pydantic
from telebot import apihelper from telebot import apihelper
from daemons import base from daemons import base
@@ -7,13 +5,6 @@ from utils import platform
from utils import queues from utils import queues
class Message(pydantic.BaseModel):
project: str
name: str
body: dict
method: str = 'send_message'
class Daemon(base.Daemon, queues.TasksHandlerMixin): class Daemon(base.Daemon, queues.TasksHandlerMixin):
def execute(self): def execute(self):
self.poll() self.poll()
@@ -22,19 +13,18 @@ class Daemon(base.Daemon, queues.TasksHandlerMixin):
def queue_name(self): def queue_name(self):
return 'botalka_mailbox' return 'botalka_mailbox'
def process(self, payload: dict): def process(self, payload):
message = Message.model_validate(payload) bot = platform.platform_client.get_config('bots')[payload['project']][payload['name']]
bot = platform.platform_client.get_config('bots')[message.project][message.name]
if not bot['mailbox_enabled']: if not bot['mailbox_enabled']:
return return
if bot['type'] == 'telegram': if bot['type'] == 'telegram':
token = bot['secrets']['telegram_token'] token = bot['secrets']['telegram_token']
self.process_telegram(token, message.method, message.body) self.process_telegram(token, payload['body'])
else: else:
print('Unknown bot type:', bot['type']) print('Unknown bot type:', bot['type'])
def process_telegram(self, token, method, payload): def process_telegram(self, token, payload):
try: try:
getattr(apihelper, method)(token, **payload) apihelper.send_message(token, **payload)
except Exception as exc: except Exception as exc:
print('Error', str(exc)) print('Error', str(exc))

View File

@@ -1,7 +1,7 @@
import telebot
import multiprocessing import multiprocessing
import telebot
import threading
import time import time
import json
from daemons import base from daemons import base
from utils import platform from utils import platform
@@ -10,37 +10,39 @@ from utils import queues
class Daemon(base.Daemon): class Daemon(base.Daemon):
def __init__(self): def __init__(self):
self.processes: dict[str, multiprocessing.Process|None] = {} self.telegram_bots: dict[str, dict[str, multiprocessing.Process|None]] = {}
def execute(self): def execute(self):
while True: while True:
bots = platform.platform_client.get_config('bots') bots = platform.platform_client.get_config('bots')
for project_name, project in bots.items(): for project_name, project in bots.items():
if project_name not in self.telegram_bots:
self.telegram_bots[project_name] = {}
for bot_name, bot_info in project.items(): for bot_name, bot_info in project.items():
key = f'{project_name}_{bot_name}' if bot_name not in self.telegram_bots[project_name]:
proc = self.processes.get(key) self.telegram_bots[project_name][bot_name] = None
bot = self.telegram_bots[project_name][bot_name]
if bot_info.get('poll_enabled'): if bot_info.get('poll_enabled'):
if proc and proc.is_alive(): if bot is not None and bot.is_alive:
print(f'process for {project_name} {bot_name} is alive') print(f'process for {project_name} {bot_name} is alive')
continue continue
print(f'starting process for {project_name} {bot_name}') print(f'starting process for {project_name} {bot_name}')
process = multiprocessing.Process(target=self.start_polling, args=(bot_info['secrets']['telegram_token'], bot_info['queue'])) bot = telebot.TeleBot(bot_info['secrets']['telegram_token'])
process.start() self.start_polling(bot, bot_info['queue'], project_name, bot_name)
self.processes[key] = process
print(f'started process for {project_name} {bot_name}') print(f'started process for {project_name} {bot_name}')
else: else:
if proc is None: if bot is None:
print(f'process for {project_name} {bot_name} is not alive') print(f'process for {project_name} {bot_name} is not alive')
continue continue
print(f'terminating process for {project_name} {bot_name}') print(f'terminating process for {project_name} {bot_name}')
proc.terminate() bot.terminate()
self.processes[key] = None self.telegram_bots[project_name][bot_name] = None
print(f'terminated process for {project_name} {bot_name}') print(f'terminated process for {project_name} {bot_name}')
time.sleep(10) time.sleep(10)
def start_polling(self, token: str, queue: str): def start_polling(self, bot: telebot.TeleBot, queue: str, project_name: str, bot_name: str):
bot = telebot.TeleBot(token) @bot.message_handler()
@bot.message_handler(content_types=['audio', 'photo', 'voice', 'video', 'document', 'animation', 'text', 'location', 'contact', 'sticker', 'video_note'])
def do_action(message: telebot.types.Message): def do_action(message: telebot.types.Message):
queues.set_task(queue, message.json, 1) queues.set_task(queue, message.json, 1)
bot.polling() process = multiprocessing.Process(target=bot.polling)
self.telegram_bots[project_name][bot_name] = process

View File

@@ -1,10 +1,6 @@
annotated-types==0.7.0 certifi==2024.8.30
certifi==2024.12.14
charset-normalizer==3.4.0 charset-normalizer==3.4.0
idna==3.10 idna==3.10
pydantic==2.10.4
pydantic_core==2.27.2
pyTelegramBotAPI==4.1.1 pyTelegramBotAPI==4.1.1
requests==2.32.3 requests==2.32.3
typing_extensions==4.12.2 urllib3==2.2.3
urllib3==2.3.0

View File

@@ -1,9 +1,4 @@
from concurrent.futures import ThreadPoolExecutor
import datetime
import json
import os import os
import uuid
import zoneinfo
import requests import requests
import time import time
@@ -20,54 +15,24 @@ class QueuesException(Exception):
class TasksHandlerMixin: class TasksHandlerMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.executor = ThreadPoolExecutor(max_workers=1)
def _send_metric(self, start: datetime.datetime, end: datetime.datetime, success: bool):
def send():
requests.post(f'{QUEUES_URL}/api/v1/metric', json={
'service': 'botalka',
'queue': self.queue_name,
'success': success,
'timestamp': start.strftime("%Y-%m-%dT%H:%M:%S") + "Z",
"success": success,
"execution_time_ms": (end - start).microseconds // 1000,
"environment": stage,
})
self.executor.submit(send)
def poll(self): def poll(self):
while True: while True:
try:
response = requests.get(f'{QUEUES_URL}/api/v1/take', headers={'queue': self.queue_name}).json() response = requests.get(f'{QUEUES_URL}/api/v1/take', headers={'queue': self.queue_name}).json()
except requests.JSONDecodeError:
print('Unable to decode json')
time.sleep(3)
continue
task = response.get('task') task = response.get('task')
if not task: if not task:
time.sleep(0.2) time.sleep(0.2)
continue continue
start = datetime.datetime.now(zoneinfo.ZoneInfo("Europe/Moscow"))
try: try:
print(f'process task with id {task["id"]}, attempt {task["attempt"]}')
self.process(task['payload']) self.process(task['payload'])
success = True
except Exception as exc: except Exception as exc:
print(f'Error processing message id={task["id"]}, payload={task["payload"]}, exc={exc}') print(f'Error processing message id={task["id"]}, payload={task["payload"]}, exc={exc}')
success = False continue
end = datetime.datetime.now(zoneinfo.ZoneInfo("Europe/Moscow"))
if success:
try: try:
resp = requests.post(f'{QUEUES_URL}/api/v1/finish', json={'id': task['id']}) resp = requests.post(f'{QUEUES_URL}/api/v1/finish', json={'id': task['id']})
if resp.status_code != 202: if resp.status_code != 202:
raise QueuesException raise QueuesException
print(f'finish task with id {task["id"]}')
except: except:
print(f'Failed to finish task id={task["id"]}') print(f'Failed to finish task id={task["id"]}')
self._send_metric(start, end, success)
@property @property
def queue_name(self): def queue_name(self):