Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context layout #1

Open
wants to merge 3 commits into
base: new-app-architecture
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 88 additions & 72 deletions new_app/action.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import uuid

from .context_manager import ContextManager
from new_app.storage import StorageManager


class Action(object):
Expand All @@ -13,11 +13,13 @@ def __init__(self, trigger, kind, callback=None):
self.trigger = trigger
# self.complex_trigger = complex_trigger
self.kind = kind.upper()
self.next_action_ids = set()
# self.next_action_ids = set()
self.next_actions = set()
self.callback = callback or self.none_callback

ContextManager.add_actions(self)
if 'actions' not in StorageManager.store:
StorageManager.store['actions'] = set()
StorageManager.store['actions'].add(self)

def __repr__(self):
return "Action(id={}, trigger={}, kind={})".format(self.id, self.trigger, self.kind)
Expand All @@ -32,79 +34,93 @@ def get_next_actions(self):
return self.next_actions

def add_actions(self, actions):
_actions = []
if not isinstance(actions, (list, tuple)):
_actions.append(actions)
# _actions = []
if not isinstance(actions, (list, tuple, set)):
# _actions.append(actions)
actions = [actions]

for act in _actions:
for act in actions:
self.next_actions.add(act)
self.next_action_ids.add(act.id)
# self.next_action_ids.add(act.id)

@staticmethod
def none_callback(*args, **kwargs):
return None

# def export_action(self):
# # self.handler = json.dumps
# # action_dict = dict()
# # # print(self.next_actions)
# # print(self)
# # for a in self.next_actions:
# # print(a)
# # print(a.next_actions)
# action_dict = {
# 'text': self.trigger,
# 'kind': self.kind,
# 'handler': {
# 'module': self.callback.__module__,
# 'name': self.callback.__name__
# },
# 'next_actions': [next_action.export_action() for next_action in self.next_actions]
# }
# return action_dict
#
# @staticmethod
# def is_next_actions_correct(next_actions):
# if type(next_actions) == list:
# for action in next_actions:
# # if isinstance(action, Action):
# if not type(action) == Action:
# return False
# return True
# else:
# return False
#
# @staticmethod
# def import_action(action_dict):
# if type(action_dict) is not dict:
# raise TypeError("'action_dict' must be a dict")
# else:
# next_actions = [Action.import_action(action) for action in action_dict.get('next_actions', [])]
# action = Action(action_dict['trigger'],
# action_dict.get('type') or 'M',
# handler=Action.get_handler(action_dict.get('handler', None)))
# action.add_actions(next_actions)
# return action
#
# @staticmethod
# def get_handler(handler):
# """
# To obtain proper handler for the action
# :param None/function/dict handler:
# Returns handler if it is None or a function.
# If dict is provided, it loads the function from the `module` and `name` of function provided
# :return: None or function
# """
# if handler is None or type(handler) == function:
# func = handler
# elif type(handler) == dict:
# try:
# module = __import__(handler['module'])
# func = getattr(module, handler['name'])
# except Exception as e:
# print(e)
# func = None
# else:
# func = None
#
# return func
@staticmethod
def start_callback(*args, **kwargs):
return "Welcome to my humble home :)"

@staticmethod
def unknown_callback(*args, **kwargs):
return "Unknown action"

# def export_action(self):
# # self.handler = json.dumps
# # action_dict = dict()
# # # print(self.next_actions)
# # print(self)
# # for a in self.next_actions:
# # print(a)
# # print(a.next_actions)
# action_dict = {
# 'text': self.trigger,
# 'kind': self.kind,
# 'handler': {
# 'module': self.callback.__module__,
# 'name': self.callback.__name__
# },
# 'next_actions': [next_action.export_action() for next_action in self.next_actions]
# }
# return action_dict
#
# @staticmethod
# def is_next_actions_correct(next_actions):
# if type(next_actions) == list:
# for action in next_actions:
# # if isinstance(action, Action):
# if not type(action) == Action:
# return False
# return True
# else:
# return False
#
# @staticmethod
# def import_action(action_dict):
# if type(action_dict) is not dict:
# raise TypeError("'action_dict' must be a dict")
# else:
# next_actions = [Action.import_action(action) for action in action_dict.get('next_actions', [])]
# action = Action(action_dict['trigger'],
# action_dict.get('type') or 'M',
# handler=Action.get_handler(action_dict.get('handler', None)))
# action.add_actions(next_actions)
# return action
#
# @staticmethod
# def get_handler(handler):
# """
# To obtain proper handler for the action
# :param None/function/dict handler:
# Returns handler if it is None or a function.
# If dict is provided, it loads the function from the `module` and `name` of function provided
# :return: None or function
# """
# if handler is None or type(handler) == function:
# func = handler
# elif type(handler) == dict:
# try:
# module = __import__(handler['module'])
# func = getattr(module, handler['name'])
# except Exception as e:
# print(e)
# func = None
# else:
# func = None
#
# return func


StartAction = Action('start', 'C', Action.start_callback)
UnknownAction = Action('unknown', 'M', callback=Action.unknown_callback)
UnknownAction.add_actions(StartAction)
25 changes: 8 additions & 17 deletions new_app/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,12 @@
from bot_action import Action
from database_handler import store_doc
from app.schema.telegram.message import MessageSchema
from .brain import respond
from new_app.brain import respond

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
TOKEN = '617361775:AAHS0S6aUQ_gLFmnfOKv72xQj5EBlhBUfos'


def keyboard_layout(triggers, row_size=3):
"""
Returns the layout for the keyboard

:param actions: list of actions that each key represent
:param row_size: no. of keys in a single row
Layout: list of lists specifying the position of each key (with given action) on the keyboard
"""
layout = []
for i in range(0, len(triggers), row_size):
layout.append([triggers for triggers in triggers[i:i + row_size]])
return layout


class BotApp(object):
def __init__(self, start_action=None):
self._updater = Updater(token=TOKEN)
Expand Down Expand Up @@ -95,7 +81,10 @@ def send_message(self, chat_id, message, keyboard=None):
return self.bot.send_message(chat_id=chat_id, text=message, reply_markup=keyboard)

def message_handler(self, bot, update):
respond(update)
from .publishers.news_publisher import NewsPublisher
NewsPublisher.subscribers.add(update.message.chat_id)
chat_id, message, keyboard = respond(update)
self.send_message(chat_id, message, keyboard)

def start_app(self):
# if start_action:
Expand All @@ -107,9 +96,11 @@ def start_app(self):
command_handler = MessageHandler(Filters.all, self.message_handler)
# message_handler = MessageHandler(Filters.text, self.parent_msg_handler)
self.dispatcher.add_handler(command_handler)
# self.dispatcher.add_handler(message_handler)
self._updater.start_polling()

def stop_app(self):
self._updater.stop()


BotApp = BotApp()

Expand Down
88 changes: 74 additions & 14 deletions new_app/brain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
from .context_manager import ContextManager
from .bot import BotApp
from new_app.storage import StorageManager
# from new_app.bot import keyboard_layout
from telegram.replykeyboardmarkup import ReplyKeyboardMarkup
from new_app.action import StartAction, UnknownAction


def keyboard_layout(triggers, row_size=3):
"""
Returns the layout for the keyboard

:param actions: list of actions that each key represent
:param row_size: no. of keys in a single row
Layout: list of lists specifying the position of each key (with given action) on the keyboard
"""
layout = []
for i in range(0, len(triggers), row_size):
layout.append([triggers for triggers in triggers[i:i + row_size]])
return layout


def strip_command(command):
Expand All @@ -19,24 +35,68 @@ def action_resolver(actions, message):
:returns: class:Action instance or None
"""
message = strip_command(message)
if message == 'start':
return StartAction
for action in actions:
if action.trigger == message:
return action
return UnknownAction


def get_keyboard_for_actions(actions):
_actions = []
if not isinstance(actions, (list, tuple, set)):
_actions.append(actions)
else:
_actions = actions
triggers = []
for action in _actions:
# TODO: change the name from handler to callback in Action
kind = action.kind
if kind == 'C':
trigger = '/' + action.trigger
triggers.append(trigger)
if len(triggers) > 0:
keyboard = ReplyKeyboardMarkup(keyboard_layout(triggers))
return keyboard
return None


def get_action(context, message):
last_action = ContextManager.actions[context['last_action']]
next_actions = last_action.get_next_actions()
current_action = action_resolver(next_actions, message)
return current_action
from new_app.sessions import SessionManager


# from telegram.update import Update


def respond(update):
context = ContextManager.resolve(update)
message = update.message.text
current_action = get_action(context, message)
# TODO: handle the case of None instead of an Action instance
context['last_action'] = current_action.id
response = current_action.callback(message)
return BotApp.sendMessage(context.chat_id, response)
"""
:param update:
:return: (chat_id, response_message, keyboard)
"""
# context = ContextManager.resolve(update)
# message = update.message.text
# current_action = get_action(context, message)
#
# context['last_action'] = current_action.id
#
# chat_id = context['chat_id']
# response = current_action.callback(message)
# next_actions = current_action.get_next_actions()
# keyboard = get_keyboard_for_actions(next_actions)
# return chat_id, response, keyboard
user = update.effective_user
session = SessionManager.get_or_create(chat_id=update.message.chat_id, user_id=user.id)
received_message = update.message.text
session_data = session.data
last_action = session_data['action']['last_action']
current_action = action_resolver(last_action.next_actions, received_message)
session_data['action']['action'] = current_action

response = current_action.callback(received_message, session_data) or "No handler was assigned to this action =_="
next_actions = current_action.get_next_actions()
chat_id = update.message.chat_id
keyboard = get_keyboard_for_actions(next_actions)

# print(current_action)
# print(next_actions)
return chat_id, response, keyboard
40 changes: 0 additions & 40 deletions new_app/context_manager.py

This file was deleted.

Loading