diff --git a/Dockerfile b/Dockerfile index bf6d5aa..dfe5718 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,9 @@ -FROM python:3.6.1-slim +FROM python:3.6.2-alpine3.6 ENV CONFIG_PATH "cfg/main.docker.cfg" -RUN mkdir /code +ENV LIBRARY_PATH=/lib:/usr/lib +RUN mkdir /code/ WORKDIR /code ADD requirements.txt /code/ -RUN pip install -r requirements.txt +RUN apk add --no-cache build-base jpeg-dev zlib-dev && pip install -r requirements.txt && apk del build-base ADD . /code/ +CMD ["python", "-u", "run.py"] diff --git a/docker-compose.yml b/docker-compose.yml index 79632c3..acc3c58 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,14 +2,15 @@ version: '2.1' services: bot: build: . - command: python run.py volumes: - .:/code depends_on: - redis restart: unless-stopped redis: - image: redis:3.2.8-alpine + image: healthcheck/redis:alpine volumes: - ./storage:/data + ports: + - "6379:6379" restart: unless-stopped diff --git a/requirements.txt b/requirements.txt index 578514e..d1a35a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ -python-telegram-bot==5.3.0 -redis==2.10.5 -Pillow==4.1.0 +python-telegram-bot==6.1.0 +redis==2.10.6 +retry==0.9.2 +Pillow==4.2.1 diff --git a/resources/main.cfg b/resources/main.cfg index 070fb7b..e15a8e2 100644 --- a/resources/main.cfg +++ b/resources/main.cfg @@ -1,6 +1,7 @@ [bot] token= name= +god_mode= anchors=bot,бот,imaginary_friend,ImaginaryFriend,ImaginaryFriendBot,@ImaginaryFriendBot purge_interval=43200.0 default_chance=5 diff --git a/src/config.py b/src/config.py index 0b07b9c..a15d0f1 100644 --- a/src/config.py +++ b/src/config.py @@ -5,7 +5,7 @@ import os encoding = 'utf-8' sections = { - 'bot': ['token', 'name', 'anchors', 'purge_interval', 'default_chance', 'spam_stickers'], + 'bot': ['token', 'name', 'anchors', 'god_mode', 'purge_interval', 'default_chance', 'spam_stickers'], 'grammar': ['chain_length', 'separator', 'stop_word', 'end_sentence', 'all'], 'logging': ['level'], 'updates': ['mode'], diff --git a/src/domain/message.py b/src/domain/message.py index 5828d7e..ac0c686 100644 --- a/src/domain/message.py +++ b/src/domain/message.py @@ -24,7 +24,7 @@ class Message(AbstractEntity): """ Returns True if the message has text. """ - return self.message.text.strip() != '' + return self.message.text is not None and self.message.text.strip() != '' def is_sticker(self): """ diff --git a/src/domain/status.py b/src/domain/status.py index a734616..8048bb5 100644 --- a/src/domain/status.py +++ b/src/domain/status.py @@ -23,6 +23,8 @@ class Status(AbstractEntity): """ Returns True if the bot was added to group. """ - user_name = deep_get_attr(self.message, 'new_chat_member.username') + new_members = self.message.new_chat_members + if new_members is None: + return False - return user_name == self.bot_name + return any(self.bot_name == m.username for m in new_members) diff --git a/src/handler/command_handler.py b/src/handler/command_handler.py index e8bab04..7b3ca20 100644 --- a/src/handler/command_handler.py +++ b/src/handler/command_handler.py @@ -1,5 +1,6 @@ from telegram import Update from telegram.ext import Handler +from telegram.ext.dispatcher import run_async from src.domain.command import Command from .commands import commands @@ -25,6 +26,7 @@ class CommandHandler(Handler): return self.callback(dispatcher.bot, update, **optional_args) + @run_async def handle(self, bot, update): command = Command(update.message) diff --git a/src/handler/commands/moderate.py b/src/handler/commands/moderate.py index 7759484..6929080 100644 --- a/src/handler/commands/moderate.py +++ b/src/handler/commands/moderate.py @@ -1,9 +1,10 @@ from .base import Base -from src.config import trigram_repository +from src.config import config, trigram_repository class Moderate(Base): aliases = ['mod_f', 'mod_d'] + gods = [int(id) for id in config.getlist('bot', 'god_mode')] @staticmethod def execute(bot, command): @@ -16,8 +17,11 @@ class Moderate(Base): if command.name == 'mod_f': words = trigram_repository.find_word(command.chat_id, command.args[0].strip()) + reply = '\n'.join(words) + if reply == '': + reply = 'Nothing found' - Moderate.reply(bot, command, '\n'.join(words)) + Moderate.reply(bot, command, reply) elif command.name == 'mod_d': trigram_repository.remove_word(command.chat_id, command.args[0].strip()) except (IndexError, ValueError): @@ -30,4 +34,4 @@ class Moderate(Base): user_id = entity.message.from_user.id admin_ids = list(map(lambda m: m.user.id, bot.get_chat_administrators(entity.chat_id))) - return user_id in admin_ids + return user_id in admin_ids or user_id in Moderate.gods diff --git a/src/handler/message_handler.py b/src/handler/message_handler.py index 04a4152..3f2f3ae 100644 --- a/src/handler/message_handler.py +++ b/src/handler/message_handler.py @@ -4,6 +4,7 @@ from random import choice from src.config import config, data_learner, reply_generator, media_checker, chance_repository from telegram.ext import MessageHandler as ParentHandler, Filters from telegram import ChatAction +from telegram.ext.dispatcher import run_async from src.domain.message import Message @@ -19,6 +20,7 @@ class MessageHandler(ParentHandler): self.spam_stickers = config.getlist('bot', 'spam_stickers') self.media_checker_messages = config.getlist('media_checker', 'messages') + @run_async def handle(self, bot, update): chance = self.chance_repository.get(update.message.chat.id) message = Message(chance=chance, message=update.message) @@ -57,6 +59,8 @@ class MessageHandler(ParentHandler): if should_answer: text = self.reply_generator.generate(message) + if text is None: + return reply_id = None if not message.is_reply_to_bot() else message.message.message_id logging.debug("[Chat %s %s answer/reply] %s" % diff --git a/src/redis_c.py b/src/redis_c.py index 528de30..74c7bc2 100644 --- a/src/redis_c.py +++ b/src/redis_c.py @@ -1,4 +1,6 @@ import redis +from redis.exceptions import BusyLoadingError +from retry import retry class Redis: @@ -9,6 +11,11 @@ class Redis: self.pool = redis.ConnectionPool(host=config['redis']['host'], port=config.getint('redis', 'port'), db=config['redis']['db']) + self.__ensure_dataset_loaded() def instance(self): return redis.Redis(connection_pool=self.pool) + + @retry(BusyLoadingError, tries=5, delay=10) + def __ensure_dataset_loaded(self): + self.instance().ping() diff --git a/src/service/reply_generator.py b/src/service/reply_generator.py index d90cde6..a806a9f 100644 --- a/src/service/reply_generator.py +++ b/src/service/reply_generator.py @@ -35,9 +35,10 @@ class ReplyGenerator: # TODO explain why it returns what it returns messages = [self.__generate_best_message(chat_id=message.chat_id, pair=pair) for pair in pairs] - longest_message = max(messages, key=len) if len(messages) else '' + longest_message = max(messages, key=len) if len(messages) else None + if longest_message and strings_has_equal_letters(longest_message, ''.join(words)): - return '' + return None return longest_message