diff --git a/bot.py b/bot.py index 7faa278..652550f 100644 --- a/bot.py +++ b/bot.py @@ -9,8 +9,37 @@ import utils +logger = logging.getLogger('bot') + + +def command_start(bot, update): + logger.info('Received command /start') + bot.send_message(chat_id=update.message.chat_id, text=config.BOT_GREETING) + + +def command_help(bot, update): + logger.info('Received command /help') + bot.send_message( + chat_id=update.message.chat_id, + text="Available commands:\n" + " - /start - start intereaction with the bot\n" + " - /help - Show commands\n" + " - /status - Show status and alive time\n" + ) + + +def command_status(bot, update): + logger.info('bot asked to execute /status commamd') + bot.send_message( + chat_id=update.message.chat_id, + text=f'Status is OK, running since {utils.since()}', + ) + + def welcome(bot: Bot, update: Update): + logger.info('Received new user event') new_member = update.message.new_chat_members[0] + logger.info(f'send welcome message for {new_member.name}') msg = None if new_member.is_bot: @@ -41,47 +70,32 @@ def reply(bot, update): msg = update.message.text reply_spec = utils.triggers_reply(msg) if reply_spec is not None: + logger.info(f'bot sends reply {reply_spec.reply}') bot.send_message( chat_id=update.message.chat_id, text=reply_spec.reply ) -def since(reference=datetime.datetime.now()): - now = datetime.datetime.now() - delta = now - reference - buff = [] - if delta.days: - buff.append('{} days'.format(delta.days)) - hours = delta.seconds // 3600 - if hours > 0: - buff.append('{} hours'.format(hours)) - minutes = delta.seconds // 60 - if minutes > 0: - buff.append('{} minutes'.format(minutes)) - seconds = delta.seconds % 60 - buff.append('{} seconds'.format(seconds)) - return ' '.join(buff) - - -def status(bot, update): - bot.send_message( - chat_id=update.message.chat_id, - text='Status is OK, running since {}'.format(since()) - ) - - def main(): - logging.basicConfig(level=logging.INFO) - logging.info('Starting bot...') + logging.basicConfig( + level=config.LOG_LEVEL, + format='%(asctime)s [%(name)s] %(levelname)s: %(message)s', + ) + logger.info('Starting bot...') + logger.info(f'- Log level is {config.LOG_LEVEL}') + logger.info(f'- Poll interval is {config.POLL_INTERVAL}') updater = Updater(config.TELEGRAM_BOT_TOKEN) dp = updater.dispatcher + dp.add_handler(CommandHandler('start', command_start)) + dp.add_handler(CommandHandler('help', command_help)) + dp.add_handler(CommandHandler('status', command_status)) dp.add_handler(MessageHandler(Filters.status_update.new_chat_members, welcome)) dp.add_handler(MessageHandler(Filters.group, reply)) - dp.add_handler(CommandHandler('status', status)) - updater.start_polling() + logger.info('Bot is ready') + updater.start_polling(poll_interval=config.POLL_INTERVAL) updater.idle() diff --git a/config.py b/config.py index 42e6c1a..3784f7b 100644 --- a/config.py +++ b/config.py @@ -5,12 +5,20 @@ default="put here the token of your bot" ) - # How likely is the bot to be triggered by one of the patterns it recognises. # - Allowed values: A float from 0 to 1 (0 will disable bot replies) VERBOSITY = config("BOT_VERBOSITY", float, default=0.33) +# Log level, default is WARNING +LOG_LEVEL = config('LOG_LEVEL', default='WARNING') + +# Poll interval for telegram API request, default is 3 seconds +POLL_INTERVAL = config('POLL_INTERVAL', int, default=3) + +# Bot message for start command +BOT_GREETING = "Hi! I'm a friendly, ligthly psychopath robot" + # A username longer than this will be considered non-human # - Allowed values: An integer larger than 1 MAX_HUMAN_USERNAME_LENGTH = config('MAX_HUMAN_USERNAME_LENGTH', diff --git a/test/test_utils.py b/test/test_utils.py new file mode 100644 index 0000000..027ab54 --- /dev/null +++ b/test/test_utils.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import datetime + +import pytest +import utils + + +def test_since_second(): + ref = datetime.datetime(2019, 5, 16, 13, 35, 15) + now = datetime.datetime(2019, 5, 16, 13, 35, 16) + assert utils.since(now, ref) == '1 second' + + +def test_since_seconds(): + ref = datetime.datetime(2019, 5, 16, 13, 35, 15) + now = datetime.datetime(2019, 5, 16, 13, 35, 21) + assert utils.since(now, ref) == '6 seconds' + + +def test_since_minute_and_seconds(): + ref = datetime.datetime(2019, 5, 16, 13, 35, 15) + now = datetime.datetime(2019, 5, 16, 13, 36, 21) + assert utils.since(now, ref) == '1 minute 6 seconds' + + +def test_since_minutes_and_seconds(): + ref = datetime.datetime(2019, 5, 16, 13, 35, 15) + now = datetime.datetime(2019, 5, 16, 13, 37, 21) + assert utils.since(now, ref) == '2 minutes 6 seconds' + + +def test_since_hour_and_minutes_and_seconds(): + ref = datetime.datetime(2019, 5, 16, 13, 35, 15) + now = datetime.datetime(2019, 5, 16, 14, 37, 21) + assert utils.since(now, ref) == '1 hour 2 minutes 6 seconds' + + +def test_since_hours_and_minutes_and_seconds(): + ref = datetime.datetime(2019, 5, 16, 13, 35, 15) + now = datetime.datetime(2019, 5, 16, 15, 37, 21) + assert utils.since(now, ref) == '2 hours 2 minutes 6 seconds' + + +def test_since_day_hours_and_minutes_and_seconds(): + ref = datetime.datetime(2019, 5, 16, 13, 35, 15) + now = datetime.datetime(2019, 5, 17, 15, 37, 21) + assert utils.since(now, ref) == '1 day 2 hours 2 minutes 6 seconds' + + +def test_since_days_hours_and_minutes_and_seconds(): + ref = datetime.datetime(2019, 5, 16, 13, 35, 15) + now = datetime.datetime(2019, 5, 19, 15, 37, 21) + assert utils.since(now, ref) == '3 days 2 hours 2 minutes 6 seconds' + + +if __name__ == "__main__": + pytest.main() diff --git a/utils.py b/utils.py index 7ed05f3..4165337 100644 --- a/utils.py +++ b/utils.py @@ -1,4 +1,5 @@ import functools +import datetime import random import re import typing @@ -13,13 +14,15 @@ def is_chinese(c): Returns True if the character passed as parameter is a Chinese one """ num = ord(c) - return any(( - 0x2E80 <= num <= 0x2FD5, - 0x3190 <= num <= 0x319F, - 0x3400 <= num <= 0x4DBF, - 0x4E00 <= num <= 0x9FCC, - 0x6300 <= num <= 0x77FF, - )) + return any( + ( + 0x2E80 <= num <= 0x2FD5, + 0x3190 <= num <= 0x319F, + 0x3400 <= num <= 0x4DBF, + 0x4E00 <= num <= 0x9FCC, + 0x6300 <= num <= 0x77FF, + ) + ) def too_much_chinese_chars(s): @@ -50,11 +53,13 @@ def is_bot(user: User): :rtype: bool """ # Add all the checks that you consider necessary - return any(( - not is_valid_name(user), - too_much_chinese_chars(user.first_name), - is_tgmember_sect(user.first_name), - )) + return any( + ( + not is_valid_name(user), + too_much_chinese_chars(user.first_name), + is_tgmember_sect(user.first_name), + ) + ) @functools.lru_cache() @@ -89,3 +94,36 @@ def triggers_reply(message: str) -> typing.Optional[BotReplySpec]: bot_reply = random.choice(bot_reply) return BotReplySpec(message, match.group(0), bot_reply) return None + + +def since(dt=None, reference=datetime.datetime.now()) -> str: + """Returns a textual description of time passed. + + Parameters: + + - dt: datetime is the date to calculate the difference from + reference. If not used, take the value from the current + datetime. + + - reference: datetime is the datetime used to get the difference + ir delta. If not defined, default value is since the definition + of the function, this is,since the moment the current run of the + program started. + """ + dt = dt or datetime.datetime.now() + delta = dt - reference + buff = [] + days = delta.days + if days: + buff.append(f"{days} day" if days == 1 else f"{days} days") + seconds = delta.seconds + if seconds > 3600: + hours = seconds // 3600 + buff.append(f"{hours} hour" if hours == 1 else f"{hours} hours") + seconds = seconds % 3600 + minutes = seconds // 60 + if minutes > 0: + buff.append(f"{minutes} minute" if minutes == 1 else f"{minutes} minutes") + seconds = seconds % 60 + buff.append(f"{seconds} second" if seconds == 1 else f"{seconds} seconds") + return " ".join(buff)