From 0122a3992fdf9b1b11050ba04cf66da57063f8d6 Mon Sep 17 00:00:00 2001 From: Domenico Blanco Date: Sun, 3 Mar 2024 21:52:03 +0100 Subject: [PATCH] fix: misc updates to make the bot work with containers (#43) * fix: misc updates to make the bot work with containers * chore: config is now a volume --- .gitignore | 2 +- Dockerfile | 15 +++++++++++++++ README.md | 6 +++--- __init__.py | 4 ++++ src/main.py | 5 ++--- src/telegram_checker.py | 20 +++++++++++--------- tests/main_test.py | 2 +- tests/telegram_checker_test.py | 22 ++++++---------------- 8 files changed, 43 insertions(+), 33 deletions(-) create mode 100644 Dockerfile create mode 100644 __init__.py diff --git a/.gitignore b/.gitignore index 31eaba4..33bae3c 100644 --- a/.gitignore +++ b/.gitignore @@ -130,5 +130,5 @@ dmypy.json # Custom config/settings.yaml -src/healthcheck_user_client.session +healthcheck_user_client.session .vscode \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..15804d6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.10-slim +WORKDIR /healthcheckbot/ + +VOLUME config + +RUN apt update +RUN apt install -y iputils-ping + +COPY requirements.txt . +RUN pip3 install -r requirements.txt + + +COPY . . + +ENTRYPOINT ["python3", "__init__.py"] diff --git a/README.md b/README.md index d1f9c8c..b7a0839 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ In order to setup this module, obtain an `api_id` and `api_hash` following the [ The last step is to add the bot(s) to check in the same settings file, completing the value for each key: - Set `username`, the bot to be checked username without the prepending '@' - Set `command`, the command that will be sent to your bot -- Set `expected_response`, the expected response from your bot. Any special character should be replaced with their equivalent character (a new line should be replaced with `\n`) +- Set `expected_response`, the expected response from your bot or a substring. Any special character should be replaced with their equivalent character (a new line should be replaced with `\n`) Multiple bots could be added by added just by repeating the same structure of a sequence of objects in yaml. ### Example @@ -30,9 +30,9 @@ chat_ids: ["10000000", "10000001", "10000002"] # Single ID or Multiple IDs api_id: "123456" api_hash: "123456:ABC-DEF1234ghIkl-zyx57W2v" bots_to_check: - - username: "@WebpageBot" + - username: "WebpageBot" command: "/start" - expected_response: "Hello, I'm the Webpage Bot!\nPlease send me up to 10 links and I will update previews for them." + expected_response: "Hello, I'm the Webpage Bot!" ``` ### Run it every 5 minutes using crontab diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..091a629 --- /dev/null +++ b/__init__.py @@ -0,0 +1,4 @@ +from src.main import main + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/src/main.py b/src/main.py index 113f4b0..5d5e351 100644 --- a/src/main.py +++ b/src/main.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 from httpx import AsyncClient, codes # Yes, we could use requests but httpx supports async tasks and HTTP/2! -from src.telegram_checker import bot_checker +from .telegram_checker import bot_checker from asyncio import run import platform # For getting the operating system name import subprocess # For executing a shell command @@ -94,8 +94,7 @@ def main() -> None: for url in urls: handle_urls(url, method) - bot_checker(config_map) - + run(bot_checker(config_map)) def init() -> None: diff --git a/src/telegram_checker.py b/src/telegram_checker.py index 90a0e69..f39d455 100644 --- a/src/telegram_checker.py +++ b/src/telegram_checker.py @@ -1,26 +1,27 @@ from pyrogram import Client, filters +from pyrogram.handlers import MessageHandler from pyrogram.errors import RPCError from pyrogram.types import Message from os.path import exists -from asyncio import sleep +from asyncio import sleep, run config_map = None -app = Client('healthcheck_user_client') received_answer = [] -@app.on_message(filters.text & filters.bot) -async def check_welcome(client: Client, message: Message) -> None: +async def check_welcome(_: Client, message: Message) -> None: global received_answer for bots_to_check in config_map['bots_to_check']: if message.from_user.username == bots_to_check['username']: - if message.text == bots_to_check['expected_response']: + if bots_to_check['expected_response'] in message.text: received_answer.insert(0, message.from_user.username) return -def bot_checker(config: dict) -> None: +async def bot_checker(config: dict) -> None: global config_map + app = Client('healthcheck_user_client') + app.add_handler(MessageHandler(check_welcome), filters.text & filters.bot) config_map = config # No bots to check @@ -40,10 +41,11 @@ def bot_checker(config: dict) -> None: print('Starting login, after the client is connected you can exit with CTRL+C') - app.run(main()) + async with app: + await main(app) -async def main() -> None: +async def main(app: Client) -> None: for bot in config_map['bots_to_check']: if not all(key in bot for key in ['username', 'command', 'expected_response']): print(f'Skipping {bot["username"] if "id" in bot else "a bot without a specified username c:"}') @@ -58,7 +60,7 @@ async def main() -> None: # To prevent circular imports # TODO: in a future refactor we could split the main code in another file only for webpages, # would improve modularity and reuse of code with an helper file - from src.main import make_request_to_telegram + from .main import make_request_to_telegram for bot in config_map['bots_to_check']: if bot['username'] not in received_answer: for user_to_notify in config_map['chat_ids']: diff --git a/tests/main_test.py b/tests/main_test.py index 90b91c3..733a544 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -110,7 +110,7 @@ 'expected_res': None, 'arg': tuple(), 'mock_obj': [main, main], - 'mock_func': ['handle_urls', 'bot_checker'], + 'mock_func': ['handle_urls', 'run'], 'mock_ret': [None, None] }, { diff --git a/tests/telegram_checker_test.py b/tests/telegram_checker_test.py index 397d960..0d31c81 100644 --- a/tests/telegram_checker_test.py +++ b/tests/telegram_checker_test.py @@ -1,37 +1,27 @@ import pytest from pytest_mock import MockerFixture -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, patch import src.telegram_checker as tg import src.main as main from pyrogram.client import Client -import asyncio tests = [ { 'func': tg.bot_checker, 'expected_res': None, - 'arg': ({'bots_to_check': [{'username':'WebpageBot', 'command':'/start', 'expected_response':'Hello, I\'m the Webpage Bot!\nPlease send me up to 10 links and I will update previews for them.'}]},), - 'is_async': False + 'arg': ({'bots_to_check': [{'username':'WebpageBot', 'command':'/start', 'expected_response':'Hello'}]},), + 'is_async': True }, { 'func': tg.bot_checker, 'expected_res': None, 'arg': ({},), - 'is_async': False - }, - { - 'func': tg.bot_checker, - 'expected_res': None, - 'arg': ({'api_id':'12345', 'api_hash':'0123456789abcdef0123456789abcdef','bots_to_check': [{'username':'WebpageBot', 'command':'/start', 'expected_response':'Hello, I\'m the Webpage Bot!\nPlease send me up to 10 links and I will update previews for them.'}]},), - 'mock_obj': [Client], - 'mock_func': ['run'], - 'mock_ret': [None], - 'is_async': False + 'is_async': True }, { 'func': tg.main, 'expected_res': None, - 'arg': tuple(), + 'arg': (Client('healthcheck_user_client'),), 'mock_obj': [main, Client], 'mock_func': ['make_request_to_telegram', 'send_message'], 'mock_ret': [None, None], @@ -41,7 +31,7 @@ { 'func': tg.main, 'expected_res': None, - 'arg': tuple(), + 'arg': (Client('healthcheck_user_client'),), 'mock_obj': [main, Client], 'mock_func': ['make_request_to_telegram', 'send_message'], 'mock_ret': [None, None],