Added more comments
This commit is contained in:
parent
784214ef90
commit
de3a9aa3da
|
@ -7,6 +7,9 @@ from src.handler import *
|
|||
|
||||
|
||||
class Bot:
|
||||
"""
|
||||
Main initializer and dispatcher of messages
|
||||
"""
|
||||
def __init__(self):
|
||||
self.updater = Updater(token=config['bot']['token'])
|
||||
self.dispatcher = self.updater.dispatcher
|
||||
|
|
|
@ -2,13 +2,17 @@ from abc import ABC
|
|||
|
||||
|
||||
class AbstractEntity(ABC):
|
||||
"""
|
||||
Base class for all message entities
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.chat_id = message.chat.id
|
||||
self.chat_type = message.chat.type
|
||||
self.message = message
|
||||
|
||||
def is_private(self):
|
||||
"""Returns True if the message is private.
|
||||
"""Returns True if chat type is private.
|
||||
"""
|
||||
return self.message.chat.type == 'private'
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@ from .abstract_entity import AbstractEntity
|
|||
|
||||
|
||||
class Command(AbstractEntity):
|
||||
"""
|
||||
Special class for message which contains command
|
||||
"""
|
||||
def __init__(self, message):
|
||||
super(Command, self).__init__(message)
|
||||
self.name = Command.parse_name(message)
|
||||
|
@ -9,8 +12,18 @@ class Command(AbstractEntity):
|
|||
|
||||
@staticmethod
|
||||
def parse_name(message):
|
||||
"""
|
||||
Parses command name from given message
|
||||
:param message: Telegram message object
|
||||
:return: Name of command
|
||||
"""
|
||||
return message.text[1:].split(' ')[0].split('@')[0]
|
||||
|
||||
@staticmethod
|
||||
def parse_args(message):
|
||||
"""
|
||||
Parses command args from given message
|
||||
:param message: Telegram message object
|
||||
:return: List of command args
|
||||
"""
|
||||
return message.text.split()[1:]
|
||||
|
|
|
@ -6,10 +6,14 @@ from src.config import config
|
|||
|
||||
|
||||
class Message(AbstractEntity):
|
||||
"""
|
||||
Basic message entity
|
||||
"""
|
||||
def __init__(self, chance, message):
|
||||
super(Message, self).__init__(message)
|
||||
|
||||
self.chance = chance
|
||||
self.anchors = config.getlist('bot', 'anchors')
|
||||
|
||||
if self.has_text():
|
||||
self.text = message.text
|
||||
|
@ -19,39 +23,48 @@ class Message(AbstractEntity):
|
|||
self.words = []
|
||||
|
||||
def has_text(self):
|
||||
"""Returns True if the message has text.
|
||||
"""
|
||||
Returns True if the message has text.
|
||||
"""
|
||||
return self.message.text.strip() != ''
|
||||
|
||||
def is_sticker(self):
|
||||
"""Returns True if the message is a sticker.
|
||||
"""
|
||||
Returns True if the message is a sticker.
|
||||
"""
|
||||
return self.message.sticker is not None
|
||||
|
||||
def has_entities(self):
|
||||
"""Returns True if the message has entities (attachments).
|
||||
"""
|
||||
Returns True if the message has entities (attachments).
|
||||
"""
|
||||
return self.message.entities is not None
|
||||
|
||||
def has_anchors(self):
|
||||
"""Returns True if the message contains at least one anchor from anchors config.
|
||||
"""
|
||||
anchors = config.getlist('bot', 'anchors')
|
||||
return self.has_text() and any(a in self.message.text.split(' ') for a in anchors)
|
||||
Returns True if the message contains at least one anchor from anchors config.
|
||||
"""
|
||||
return self.has_text() and any(a in self.message.text.split(' ') for a in self.anchors)
|
||||
|
||||
def is_reply_to_bot(self):
|
||||
"""Returns True if the message is a reply to bot.
|
||||
"""
|
||||
Returns True if the message is a reply to bot.
|
||||
"""
|
||||
user_name = deep_get_attr(self.message, 'reply_to_message.from_user.username')
|
||||
|
||||
return user_name == config['bot']['name']
|
||||
|
||||
def is_random_answer(self):
|
||||
"""Returns True if reply chance for this chat is high enough
|
||||
"""
|
||||
Returns True if reply chance for this chat is high enough
|
||||
"""
|
||||
return random.randint(0, 100) < self.chance
|
||||
|
||||
def should_answer(self):
|
||||
"""
|
||||
Returns True if bot should answer to this message
|
||||
:return: Should answer or not
|
||||
"""
|
||||
return self.has_anchors() \
|
||||
or self.is_private() \
|
||||
or self.is_reply_to_bot() \
|
||||
|
|
|
@ -4,20 +4,24 @@ from src.config import config
|
|||
|
||||
|
||||
class Status(AbstractEntity):
|
||||
bot_name = config['bot']['name']
|
||||
|
||||
"""
|
||||
Special class for message which contains info about status change
|
||||
"""
|
||||
def __init__(self, message):
|
||||
super(Status, self).__init__(message)
|
||||
self.bot_name = config['bot']['name']
|
||||
|
||||
def is_bot_kicked(self):
|
||||
"""Returns True if the bot was kicked from group.
|
||||
"""
|
||||
Returns True if the bot was kicked from group.
|
||||
"""
|
||||
user_name = deep_get_attr(self.message, 'left_chat_member.username')
|
||||
|
||||
return user_name == self.bot_name
|
||||
|
||||
def is_bot_added(self):
|
||||
"""Returns True if the bot was added to group.
|
||||
"""
|
||||
Returns True if the bot was added to group.
|
||||
"""
|
||||
user_name = deep_get_attr(self.message, 'new_chat_member.username')
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ import src.entity.word
|
|||
|
||||
|
||||
class Pair(Model):
|
||||
"""
|
||||
Pair entity, represents pairs table.
|
||||
"""
|
||||
__fillable__ = ['chat_id', 'first_id', 'second_id']
|
||||
__timestamps__ = ['created_at']
|
||||
|
||||
|
|
|
@ -7,6 +7,9 @@ import src.entity.word
|
|||
|
||||
|
||||
class Reply(Model):
|
||||
"""
|
||||
Reply entity, represents replies table.
|
||||
"""
|
||||
__fillable__ = ['pair_id', 'word_id', 'count']
|
||||
__timestamps__ = False
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
from orator.orm import Model
|
||||
|
||||
|
||||
class Word(Model):
|
||||
"""
|
||||
Word entity, represents words table.
|
||||
"""
|
||||
__fillable__ = ['word']
|
||||
__timestamps__ = False
|
||||
|
|
|
@ -2,6 +2,9 @@ import redis
|
|||
|
||||
|
||||
class Redis:
|
||||
"""
|
||||
Small redis wrapper, to simplify work with connection pool
|
||||
"""
|
||||
def __init__(self, config):
|
||||
self.pool = redis.ConnectionPool(host=config['redis']['host'],
|
||||
port=config.getint('redis', 'port'),
|
||||
|
|
|
@ -2,17 +2,31 @@ from src.config import config, redis
|
|||
|
||||
|
||||
class ChanceManager:
|
||||
"""
|
||||
Handles bot reply chance
|
||||
"""
|
||||
def __init__(self):
|
||||
self.redis = redis
|
||||
self.key = 'chance:{}'
|
||||
self.default_chance = config.getint('bot', 'default_chance')
|
||||
|
||||
def get_chance(self, chat_id):
|
||||
"""
|
||||
Returns current chance of bot reply for chat_id
|
||||
:param chat_id: ID of chat
|
||||
:return: Current chance
|
||||
"""
|
||||
result = self.redis.instance().get(self.key.format(chat_id))
|
||||
|
||||
return int(result.decode("utf-8")) if result is not None else self.default_chance
|
||||
|
||||
def set_chance(self, chat_id, new_chance):
|
||||
"""
|
||||
Sets new reply chance for chat_id and returns old
|
||||
:param chat_id: ID of chat
|
||||
:param new_chance: Chance to set
|
||||
:return: Old chance
|
||||
"""
|
||||
old_chance = self.redis.instance().getset(self.key.format(chat_id), new_chance)
|
||||
|
||||
return int(old_chance.decode("utf-8")) if old_chance is not None else self.default_chance
|
||||
|
|
|
@ -9,6 +9,9 @@ from src.config import config, redis
|
|||
|
||||
|
||||
class ChatPurgeQueue:
|
||||
"""
|
||||
Scheduling and execution of chat purge
|
||||
"""
|
||||
def __init__(self):
|
||||
self.redis = redis
|
||||
self.default_interval = config.getfloat('bot', 'purge_interval')
|
||||
|
@ -24,6 +27,11 @@ class ChatPurgeQueue:
|
|||
return self
|
||||
|
||||
def add(self, chat_id, interval=None):
|
||||
"""
|
||||
Schedules purge of chat data
|
||||
:param chat_id: ID of chat
|
||||
:param interval: Interval in seconds
|
||||
"""
|
||||
interval = interval if interval is not None else self.default_interval
|
||||
scheduled_at = datetime.now() + timedelta(seconds=interval)
|
||||
|
||||
|
@ -41,6 +49,10 @@ class ChatPurgeQueue:
|
|||
)
|
||||
|
||||
def remove(self, chat_id):
|
||||
"""
|
||||
Removes scheduled purge job from queue
|
||||
:param chat_id: ID of chat
|
||||
"""
|
||||
if chat_id not in self.jobs:
|
||||
return
|
||||
|
||||
|
|
|
@ -6,6 +6,10 @@ from src.entity.pair import Pair
|
|||
|
||||
class DataLearner:
|
||||
def learn(self, message):
|
||||
"""
|
||||
Split message to trigrams and write to DB
|
||||
:param message: Message
|
||||
"""
|
||||
self.__write_new_unique_words(message.words)
|
||||
|
||||
words = self.__normalize_words(message.words)
|
||||
|
|
|
@ -4,6 +4,9 @@ from urllib.parse import urlparse
|
|||
|
||||
|
||||
class MediaUniquenessChecker:
|
||||
"""
|
||||
Checks message links and photos for uniqueness
|
||||
"""
|
||||
def __init__(self):
|
||||
self.redis = redis
|
||||
self.key = "media_checker:{}"
|
||||
|
|
|
@ -6,15 +6,23 @@ from src.entity.pair import Pair
|
|||
|
||||
|
||||
class ReplyGenerator:
|
||||
"""
|
||||
Handles generation of responses for user message
|
||||
"""
|
||||
def generate(self, message):
|
||||
result = self.generate_story(message, message.words, random.randint(0, 2) + 1)
|
||||
"""
|
||||
Generates response based on message words
|
||||
:param message: Message
|
||||
:return: Response or empty string, if generated response equals to user message
|
||||
"""
|
||||
result = self.__generate_story(message, message.words, random.randint(0, 2) + 1)
|
||||
|
||||
if strings_has_equal_letters(result, ''.join(message.words)):
|
||||
return ''
|
||||
|
||||
return result
|
||||
|
||||
def generate_story(self, message, words, sentences_count):
|
||||
def __generate_story(self, message, words, sentences_count):
|
||||
word_ids = Word.where_in('word', words).lists('id').all()
|
||||
|
||||
return ' '.join([self.__generate_sentence(message, word_ids) for _ in range(sentences_count)])
|
||||
|
|
Loading…
Reference in New Issue