Refactoring of structure
This commit is contained in:
parent
75c6effe9a
commit
35b65126f9
|
@ -4,10 +4,11 @@ name=
|
|||
anchors=
|
||||
messages=
|
||||
purge_interval=43200.0
|
||||
default_chance=5
|
||||
|
||||
[grammar]
|
||||
end_sentence=.!?
|
||||
all=.!?,;:
|
||||
end_sentence=.....!!?
|
||||
all=.!?,;:()\"
|
||||
|
||||
[logging]
|
||||
level=INFO
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
from src.chat_purge_queue_handler import ChatPurgeQueueHandler
|
||||
from src.chat_purge_queue import ChatPurgeQueue
|
||||
|
||||
chat_purge_queue_handler = ChatPurgeQueueHandler()
|
||||
chat_purge_queue = ChatPurgeQueue()
|
||||
|
|
17
src/bot.py
17
src/bot.py
|
@ -1,9 +1,10 @@
|
|||
import logging
|
||||
|
||||
from telegram.ext import Updater
|
||||
from src.command_handler import CommandHandler
|
||||
from src.message_handler import MessageHandler
|
||||
from . import chat_purge_queue_handler
|
||||
from src.handlers.message_handler import MessageHandler
|
||||
from src.handlers.command_handler import CommandHandler
|
||||
from src.handlers.status_handler import StatusHandler
|
||||
from . import chat_purge_queue
|
||||
|
||||
|
||||
class Bot:
|
||||
|
@ -14,15 +15,15 @@ class Bot:
|
|||
|
||||
def run(self):
|
||||
logging.info("Bot started")
|
||||
chat_purge_queue_handler.init(
|
||||
|
||||
chat_purge_queue.init(
|
||||
queue=self.updater.job_queue,
|
||||
default_interval=self.config['bot']['purge_interval']
|
||||
)
|
||||
|
||||
message_handler = MessageHandler(self.config)
|
||||
command_handler = CommandHandler()
|
||||
self.dispatcher.add_handler(MessageHandler(self.config))
|
||||
self.dispatcher.add_handler(CommandHandler(self.config))
|
||||
self.dispatcher.add_handler(StatusHandler(self.config))
|
||||
|
||||
self.dispatcher.add_handler(message_handler)
|
||||
self.dispatcher.add_handler(command_handler)
|
||||
self.updater.start_polling()
|
||||
self.updater.idle()
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import logging
|
||||
|
||||
from telegram.ext import Job
|
||||
from src.domain.chat import Chat
|
||||
|
||||
from src.entity.chat import Chat
|
||||
|
||||
|
||||
class ChatPurgeQueueHandler:
|
||||
class ChatPurgeQueue:
|
||||
queue = None
|
||||
default_interval = 99999.0
|
||||
jobs = {}
|
||||
|
@ -49,7 +51,7 @@ class ChatPurgeQueueHandler:
|
|||
chat_id = job.context
|
||||
logging.info("Removing chat #%d data..." % chat_id)
|
||||
|
||||
chat = Chat.find(job.context)
|
||||
chat = Chat.find(chat_id)
|
||||
if chat is not None:
|
||||
chat.pairs().delete()
|
||||
chat.delete()
|
|
@ -1,104 +0,0 @@
|
|||
from telegram.ext import Handler
|
||||
from telegram import Update
|
||||
from src.domain.chat import Chat
|
||||
|
||||
|
||||
class CommandHandler(Handler):
|
||||
def __init__(self, allow_edited = False, pass_update_queue = False, pass_job_queue=False,
|
||||
pass_user_data = False, pass_chat_data = False):
|
||||
super(CommandHandler, self).__init__(self.handle,
|
||||
pass_update_queue = pass_update_queue,
|
||||
pass_job_queue = pass_job_queue,
|
||||
pass_user_data = pass_user_data,
|
||||
pass_chat_data = pass_chat_data)
|
||||
|
||||
self.allow_edited = allow_edited
|
||||
self.commands = {
|
||||
'start': self.__start_command,
|
||||
'help': self.__help_command,
|
||||
'ping': self.__ping_command,
|
||||
'set_chance': self.__set_chance_command,
|
||||
'get_chance': self.__get_chance_command,
|
||||
'get_stats': self.__get_stats_command
|
||||
}
|
||||
|
||||
|
||||
def check_update(self, update):
|
||||
if isinstance(update, Update) and (update.message or update.edited_message and self.allow_edited):
|
||||
message = update.message or update.edited_message
|
||||
|
||||
return (message.text and message.text.startswith('/') and self.__parse_command_name(update) in self.commands)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher, update)
|
||||
message = update.message or update.edited_message
|
||||
optional_args['args'] = message.text.split()[1:]
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
|
||||
def handle(self, bot, update, args):
|
||||
try:
|
||||
command = self.__parse_command_name(update)
|
||||
method = self.commands[command]
|
||||
method(update, args)
|
||||
except (IndexError, ValueError):
|
||||
update.message.reply_text('Invalid command! Type /help')
|
||||
|
||||
|
||||
def __parse_command_name(self, update):
|
||||
message = update.message or update.edited_message
|
||||
|
||||
return message.text[1:].split(' ')[0].split('@')[0]
|
||||
|
||||
|
||||
def __start_command(self, update, args):
|
||||
update.message.reply_text('Hi! :3')
|
||||
|
||||
|
||||
def __help_command(self, update, args):
|
||||
update.message.reply_text(
|
||||
"""Add me to your group and let me listen to your chat for a while.
|
||||
When I learn enough word pairs, I'll start bringing fun and absurdity to your conversations.
|
||||
|
||||
Available commands:
|
||||
• /ping,
|
||||
• /get_stats: get the number of word pairs I've learned in this chat,
|
||||
• /set_chance: set the chance that I'll reply to a random message (must be in range 1-50, default: 5),
|
||||
• /get_chance: get the current chance of my random reply.
|
||||
|
||||
If you get tired of me, you can kick me from the group.
|
||||
In 12 hours, I'll forget everything that have been learned in your chat, so you can add me again and teach me new things!
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def __ping_command(self, update, args):
|
||||
update.message.reply_text('pong')
|
||||
|
||||
|
||||
def __set_chance_command(self, update, args):
|
||||
try:
|
||||
random_chance = int(args[0])
|
||||
|
||||
if random_chance < 1 or random_chance > 50:
|
||||
raise ValueError
|
||||
|
||||
chat = Chat.get_chat(update.message)
|
||||
chat.random_chance = random_chance
|
||||
chat.save()
|
||||
|
||||
update.message.reply_text('Set chance to: {}'.format(random_chance))
|
||||
except (IndexError, ValueError):
|
||||
update.message.reply_text('Usage: /set_chance 1-50.')
|
||||
|
||||
|
||||
def __get_chance_command(self, update, args):
|
||||
update.message.reply_text('Current chance: {}'.format(Chat.get_chat(update.message).random_chance))
|
||||
|
||||
|
||||
def __get_stats_command(self, update, args):
|
||||
update.message.reply_text('Pairs: {}'.format(Chat.get_chat(update.message).pairs().count()))
|
|
@ -0,0 +1,15 @@
|
|||
class Command:
|
||||
def __init__(self, chat, message, config):
|
||||
self.chat = chat
|
||||
self.message = message
|
||||
self.config = config
|
||||
self.name = Command.parse_name(message)
|
||||
self.args = Command.parse_args(message)
|
||||
|
||||
@staticmethod
|
||||
def parse_name(message):
|
||||
return message.text[1:].split(' ')[0].split('@')[0]
|
||||
|
||||
@staticmethod
|
||||
def parse_args(message):
|
||||
return message.text.split()[1:]
|
|
@ -1,14 +1,12 @@
|
|||
import random
|
||||
|
||||
import src.domain.chat as chat
|
||||
from src.utils import deep_get_attr
|
||||
|
||||
|
||||
class Message:
|
||||
def __init__(self, message, config):
|
||||
def __init__(self, chat, message, config):
|
||||
self.chat = chat
|
||||
self.message = message
|
||||
self.config = config
|
||||
self.chat = chat.Chat.get_chat(message)
|
||||
|
||||
if self.has_text():
|
||||
self.text = message.text
|
||||
|
@ -48,20 +46,6 @@ class Message:
|
|||
"""
|
||||
return self.message.chat.type == 'private'
|
||||
|
||||
def is_bot_kicked(self):
|
||||
"""Returns True if the bot was kicked from group.
|
||||
"""
|
||||
user_name = deep_get_attr(self.message, 'left_chat_member.username')
|
||||
|
||||
return user_name == self.config['bot']['name']
|
||||
|
||||
def is_bot_added(self):
|
||||
"""Returns True if the bot was added to group.
|
||||
"""
|
||||
user_name = deep_get_attr(self.message, 'new_chat_member.username')
|
||||
|
||||
return user_name == self.config['bot']['name']
|
||||
|
||||
def is_reply_to_bot(self):
|
||||
"""Returns True if the message is a reply to bot.
|
||||
"""
|
||||
|
@ -72,12 +56,7 @@ class Message:
|
|||
def is_random_answer(self):
|
||||
"""Returns True if reply chance for this chat is high enough
|
||||
"""
|
||||
return random.randint(0, 100) < getattr(self.chat, 'random_chance', 5)
|
||||
|
||||
def is_command(self):
|
||||
"""Returns True if the message is a command (`/start`, `/do_stuff`).
|
||||
"""
|
||||
return self.has_text() and self.text[0] == '/'
|
||||
return random.randint(0, 100) < getattr(self.chat, 'random_chance', self.config['bot']['default_chance'])
|
||||
|
||||
def __get_words(self):
|
||||
text = list(self.text)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
from src.utils import deep_get_attr
|
||||
|
||||
|
||||
class Status:
|
||||
def __init__(self, chat, message, config):
|
||||
self.chat = chat
|
||||
self.message = message
|
||||
self.config = config
|
||||
|
||||
def is_bot_kicked(self):
|
||||
"""Returns True if the bot was kicked from group.
|
||||
"""
|
||||
user_name = deep_get_attr(self.message, 'left_chat_member.username')
|
||||
|
||||
return user_name == self.config['bot']['name']
|
||||
|
||||
def is_bot_added(self):
|
||||
"""Returns True if the bot was added to group.
|
||||
"""
|
||||
user_name = deep_get_attr(self.message, 'new_chat_member.username')
|
||||
|
||||
return user_name == self.config['bot']['name']
|
|
@ -1,8 +1,9 @@
|
|||
from orator.orm import Model
|
||||
from orator.orm import has_many
|
||||
import logging
|
||||
|
||||
import src.domain.pair
|
||||
from orator.orm import Model
|
||||
from orator.orm import has_many
|
||||
|
||||
import src.entity.pair
|
||||
|
||||
|
||||
class Chat(Model):
|
||||
|
@ -10,7 +11,7 @@ class Chat(Model):
|
|||
|
||||
@has_many
|
||||
def pairs(self):
|
||||
return src.domain.pair.Pair
|
||||
return src.entity.pair.Pair
|
||||
|
||||
# def migrate_to_chat_id(self, new_id):
|
||||
# logging.info("[Chat %s %s] Migrating ID to %s" % (self.chat_type, self.telegram_id, new_id))
|
||||
|
@ -19,10 +20,7 @@ class Chat(Model):
|
|||
|
||||
@staticmethod
|
||||
def get_chat(message):
|
||||
telegram_id = message.chat.id
|
||||
type = message.chat.type
|
||||
|
||||
return Chat.first_or_create(telegram_id=telegram_id, chat_type=type)
|
||||
return Chat.first_or_create(telegram_id=message.chat.id, chat_type=message.chat.type)
|
||||
|
||||
# Events
|
||||
Chat.created(lambda chat: logging.info("[Chat %s %s] Created with internal ID #%s" %
|
|
@ -1,15 +1,13 @@
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
from orator.orm import Model
|
||||
from orator.orm import belongs_to
|
||||
from orator.orm import has_many
|
||||
|
||||
import src.entity.reply
|
||||
import src.entity.chat
|
||||
import src.entity.word
|
||||
from src.utils import *
|
||||
|
||||
import src.domain.chat
|
||||
import src.domain.reply
|
||||
import src.domain.word
|
||||
|
||||
|
||||
class Pair(Model):
|
||||
__guarded__ = ['id']
|
||||
|
@ -20,19 +18,19 @@ class Pair(Model):
|
|||
|
||||
@has_many
|
||||
def replies(self):
|
||||
return src.domain.reply.Reply
|
||||
return src.entity.reply.Reply
|
||||
|
||||
@belongs_to
|
||||
def chat(self):
|
||||
return src.domain.chat.Chat
|
||||
return src.entity.chat.Chat
|
||||
|
||||
@belongs_to
|
||||
def first(self):
|
||||
return src.domain.word.Word
|
||||
return src.entity.word.Word
|
||||
|
||||
@belongs_to
|
||||
def second(self):
|
||||
return src.domain.word.Word
|
||||
return src.entity.word.Word
|
||||
|
||||
@staticmethod
|
||||
def generate(message):
|
||||
|
@ -40,7 +38,7 @@ class Pair(Model):
|
|||
|
||||
@staticmethod
|
||||
def generate_story(message, words, sentences):
|
||||
words_ids = src.domain.word.Word.where_in('word', words).get().pluck('id').all()
|
||||
words_ids = src.entity.word.Word.where_in('word', words).get().pluck('id').all()
|
||||
|
||||
result = []
|
||||
for _ in range(0, sentences):
|
||||
|
@ -50,7 +48,7 @@ class Pair(Model):
|
|||
|
||||
@staticmethod
|
||||
def learn(message):
|
||||
src.domain.word.Word.learn(message.words)
|
||||
src.entity.word.Word.learn(message.words)
|
||||
|
||||
words = [None]
|
||||
for word in message.words:
|
||||
|
@ -63,7 +61,7 @@ class Pair(Model):
|
|||
while any(word for word in words):
|
||||
trigram = words[:3]
|
||||
first_word_id, second_word_id, *third_word_id = list(map(
|
||||
lambda x: None if x is None else src.domain.word.Word.where('word', x).first().id,
|
||||
lambda x: None if x is None else src.entity.word.Word.where('word', x).first().id,
|
||||
trigram
|
||||
))
|
||||
third_word_id = None if len(third_word_id) == 0 else third_word_id[0]
|
|
@ -2,8 +2,8 @@ from orator.orm import Model
|
|||
from orator.orm import belongs_to
|
||||
from orator.orm import belongs_to_many
|
||||
|
||||
import src.domain.pair
|
||||
import src.domain.word
|
||||
import src.entity.pair
|
||||
import src.entity.word
|
||||
|
||||
|
||||
class Reply(Model):
|
||||
|
@ -12,8 +12,8 @@ class Reply(Model):
|
|||
|
||||
@belongs_to_many
|
||||
def pairs(self):
|
||||
return src.domain.pair.Pair
|
||||
return src.entity.pair.Pair
|
||||
|
||||
@belongs_to
|
||||
def word(self):
|
||||
return src.domain.word.Word
|
||||
return src.entity.word.Word
|
|
@ -1,8 +1,9 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
from orator.orm import Model
|
||||
from orator.orm import has_many
|
||||
|
||||
import src.domain.chat
|
||||
from collections import OrderedDict
|
||||
import src.entity.chat
|
||||
|
||||
|
||||
class Word(Model):
|
||||
|
@ -11,7 +12,7 @@ class Word(Model):
|
|||
|
||||
@has_many
|
||||
def chats(self):
|
||||
return src.domain.chat.Chat
|
||||
return src.entity.chat.Chat
|
||||
|
||||
@staticmethod
|
||||
def learn(words):
|
|
@ -0,0 +1,83 @@
|
|||
from telegram import Update
|
||||
from telegram.ext import Handler
|
||||
|
||||
from src.entity.chat import Chat
|
||||
from src.domain.command import Command
|
||||
|
||||
|
||||
class CommandHandler(Handler):
|
||||
def __init__(self, config):
|
||||
super(CommandHandler, self).__init__(self.handle)
|
||||
self.config = config
|
||||
self.commands = {
|
||||
'start': self.__start_command,
|
||||
'help': self.__help_command,
|
||||
'ping': self.__ping_command,
|
||||
'set_chance': self.__set_chance_command,
|
||||
'get_chance': self.__get_chance_command,
|
||||
'get_stats': self.__get_stats_command
|
||||
}
|
||||
|
||||
def check_update(self, update):
|
||||
if isinstance(update, Update) and update.message:
|
||||
message = update.message
|
||||
|
||||
return message.text and message.text.startswith('/') and Command.parse_name(message) in self.commands
|
||||
else:
|
||||
return False
|
||||
|
||||
def handle_update(self, update, dispatcher):
|
||||
optional_args = self.collect_optional_args(dispatcher, update)
|
||||
|
||||
return self.callback(dispatcher.bot, update, **optional_args)
|
||||
|
||||
def handle(self, bot, update):
|
||||
try:
|
||||
chat = Chat.get_chat(update.message)
|
||||
command = Command(chat=chat, message=update.message, config=self.config)
|
||||
|
||||
callback = self.commands[command.name]
|
||||
callback(update, command)
|
||||
except (IndexError, ValueError):
|
||||
update.message.reply_text('Invalid command! Type /help')
|
||||
|
||||
def __start_command(self, update, command):
|
||||
update.message.reply_text('Hi! :3')
|
||||
|
||||
def __help_command(self, update, command):
|
||||
update.message.reply_text(
|
||||
"""Add me to your group and let me listen to your chat for a while.
|
||||
When I learn enough word pairs, I'll start bringing fun and absurdity to your conversations.
|
||||
|
||||
Available commands:
|
||||
• /ping,
|
||||
• /get_stats: get the number of word pairs I've learned in this chat,
|
||||
• /set_chance: set the chance that I'll reply to a random message (must be in range 1-50, default: 5),
|
||||
• /get_chance: get the current chance of my random reply.
|
||||
|
||||
If you get tired of me, you can kick me from the group.
|
||||
In 12 hours, I'll forget everything that have been learned in your chat, so you can add me again and teach me new things!
|
||||
"""
|
||||
)
|
||||
|
||||
def __ping_command(self, update, command):
|
||||
update.message.reply_text('pong')
|
||||
|
||||
def __set_chance_command(self, update, command):
|
||||
try:
|
||||
random_chance = int(command.args[0])
|
||||
|
||||
if random_chance < 1 or random_chance > 50:
|
||||
raise ValueError
|
||||
|
||||
command.chat.update(random_chance=random_chance)
|
||||
|
||||
update.message.reply_text('Set chance to: {}'.format(random_chance))
|
||||
except (IndexError, ValueError):
|
||||
update.message.reply_text('Usage: /set_chance 1-50.')
|
||||
|
||||
def __get_chance_command(self, update, command):
|
||||
update.message.reply_text('Current chance: {}'.format(command.chat.random_chance))
|
||||
|
||||
def __get_stats_command(self, update, command):
|
||||
update.message.reply_text('Pairs: {}'.format(command.chat.pairs().count()))
|
|
@ -3,30 +3,21 @@ import logging
|
|||
from telegram.ext import MessageHandler as ParentHandler, Filters
|
||||
|
||||
from src.domain.message import Message
|
||||
from src.domain.pair import Pair
|
||||
from . import chat_purge_queue_handler
|
||||
from src.entity.pair import Pair
|
||||
from src.entity.chat import Chat
|
||||
|
||||
|
||||
class MessageHandler(ParentHandler):
|
||||
def __init__(self,
|
||||
config,
|
||||
allow_edited=False,
|
||||
pass_update_queue=False,
|
||||
pass_user_data=False,
|
||||
pass_chat_data=False):
|
||||
def __init__(self, config):
|
||||
super(MessageHandler, self).__init__(
|
||||
Filters.text | Filters.sticker | Filters.status_update,
|
||||
self.handle,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=False,
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data,
|
||||
allow_edited=allow_edited)
|
||||
Filters.text | Filters.sticker,
|
||||
self.handle)
|
||||
|
||||
self.config = config
|
||||
|
||||
def handle(self, bot, update):
|
||||
message = Message(message=update.message, config=self.config)
|
||||
chat = Chat.get_chat(update.message)
|
||||
message = Message(chat=chat, message=update.message, config=self.config)
|
||||
|
||||
if message.has_text():
|
||||
logging.debug("[Chat %s %s bare_text] %s" %
|
||||
|
@ -34,14 +25,10 @@ class MessageHandler(ParentHandler):
|
|||
message.chat.telegram_id,
|
||||
message.text))
|
||||
|
||||
if message.has_text() and not (message.is_editing() or message.is_command()):
|
||||
if message.has_text() and not message.is_editing():
|
||||
return self.__process_message(bot, message)
|
||||
elif message.is_sticker():
|
||||
return self.__process_sticker(bot, message)
|
||||
elif message.is_bot_added():
|
||||
return self.__process_bot_add(message)
|
||||
elif message.is_bot_kicked():
|
||||
return self.__process_bot_kick(message)
|
||||
|
||||
def __process_message(self, bot, message):
|
||||
Pair.learn(message)
|
||||
|
@ -88,15 +75,3 @@ class MessageHandler(ParentHandler):
|
|||
bot.sendSticker(chat_id=message.chat.telegram_id,
|
||||
reply_to_message_id=message.message.message_id,
|
||||
sticker=sticker_id)
|
||||
|
||||
def __process_bot_kick(self, message):
|
||||
logging.debug("[Chat %s %s bot_kicked]" %
|
||||
(message.chat.chat_type, message.chat.telegram_id))
|
||||
|
||||
chat_purge_queue_handler.add(message.chat.id)
|
||||
|
||||
def __process_bot_add(self, message):
|
||||
logging.debug("[Chat %s %s bot_added]" %
|
||||
(message.chat.chat_type, message.chat.telegram_id))
|
||||
|
||||
chat_purge_queue_handler.remove(message.chat.id)
|
|
@ -0,0 +1,38 @@
|
|||
import logging
|
||||
|
||||
from telegram.ext import MessageHandler, Filters
|
||||
|
||||
from src.domain.status import Status
|
||||
from src.entity.chat import Chat
|
||||
from src import chat_purge_queue
|
||||
|
||||
|
||||
class StatusHandler(MessageHandler):
|
||||
def __init__(self, config):
|
||||
super(StatusHandler, self).__init__(
|
||||
Filters.status_update,
|
||||
self.handle,
|
||||
pass_job_queue=True)
|
||||
|
||||
self.config = config
|
||||
|
||||
def handle(self, bot, update, job_queue):
|
||||
chat = Chat.get_chat(update.message)
|
||||
status = Status(chat=chat, message=update.message, config=self.config)
|
||||
|
||||
if status.is_bot_added():
|
||||
return self.__process_bot_add(status, job_queue)
|
||||
elif status.is_bot_kicked():
|
||||
return self.__process_bot_kick(status, job_queue)
|
||||
|
||||
def __process_bot_kick(self, status, job_queue):
|
||||
logging.debug("[Chat %s %s bot_kicked]" %
|
||||
(status.chat.chat_type, status.chat.telegram_id))
|
||||
|
||||
chat_purge_queue.add(status.chat.id)
|
||||
|
||||
def __process_bot_add(self, status, job_queue):
|
||||
logging.debug("[Chat %s %s bot_added]" %
|
||||
(status.chat.chat_type, status.chat.telegram_id))
|
||||
|
||||
chat_purge_queue.remove(status.chat.id)
|
Loading…
Reference in New Issue