Skip to content

Commit

Permalink
feat!: add support for having multiple text commands (resolve #4)
Browse files Browse the repository at this point in the history
  • Loading branch information
hearot committed Jun 14, 2020
1 parent ec6bab8 commit 80bfb7d
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 57 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@

### Other changes

- Do not restrict callbacks to `Message` and `CallbackQuery`
- Do not restrict callbacks to `Message` and `CallbackQuery` ([ec6bab8600efa7015964a79839046c46a91ebe29](https://github.com/hearot/pyrubrum/commit/ec6bab8600efa7015964a79839046c46a91ebe29))
- Rename all variables named `tree`to `handler` ([b9acc90cc899cf4c08315a7123017cf8e7169106](https://github.com/hearot/pyrubrum/commit/b9acc90cc899cf4c08315a7123017cf8e7169106))
- Update version to 0.1a1.dev6 ([477293742e2cc5b238d3bea9503045f2f41f8514](https://github.com/hearot/pyrubrum/commit/477293742e2cc5b238d3bea9503045f2f41f8514))

### ‼️ Breaking changes

- Add support for having multiple text commands (resolve #4)
- Create specific directories for module entities ([25593e6d40fa34dbc47528ff3fa6fdc30c0a41b9](https://github.com/hearot/pyrubrum/commit/25593e6d40fa34dbc47528ff3fa6fdc30c0a41b9))
- Move `recursive_add` and `transform`to `pyrubrum.tree.node` ([74fa78d207cac97c7f01fcdc562c75b0bc81db7c](https://github.com/hearot/pyrubrum/commit/74fa78d207cac97c7f01fcdc562c75b0bc81db7c))
- Move `types` to a brand new directory ([7d6b33b3af33a7a69999966aa4caf6728ade7598](https://github.com/hearot/pyrubrum/commit/7d6b33b3af33a7a69999966aa4caf6728ade7598))
Expand Down
3 changes: 2 additions & 1 deletion FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@

### Other changes

- Do not restrict callbacks to `Message` and `CallbackQuery`
- Do not restrict callbacks to `Message` and `CallbackQuery` ([ec6bab8600efa7015964a79839046c46a91ebe29](https://github.com/hearot/pyrubrum/commit/ec6bab8600efa7015964a79839046c46a91ebe29))
- Rename all variables named `tree`to `handler` ([b9acc90cc899cf4c08315a7123017cf8e7169106](https://github.com/hearot/pyrubrum/commit/b9acc90cc899cf4c08315a7123017cf8e7169106))
- Update version to 0.1a1.dev6 ([477293742e2cc5b238d3bea9503045f2f41f8514](https://github.com/hearot/pyrubrum/commit/477293742e2cc5b238d3bea9503045f2f41f8514))

### ‼️ Breaking changes

- Add support for having multiple text commands (resolve #4)
- Create specific directories for module entities ([25593e6d40fa34dbc47528ff3fa6fdc30c0a41b9](https://github.com/hearot/pyrubrum/commit/25593e6d40fa34dbc47528ff3fa6fdc30c0a41b9))
- Move `recursive_add` and `transform`to `pyrubrum.tree.node` ([74fa78d207cac97c7f01fcdc562c75b0bc81db7c](https://github.com/hearot/pyrubrum/commit/74fa78d207cac97c7f01fcdc562c75b0bc81db7c))
- Move `types` to a brand new directory ([7d6b33b3af33a7a69999966aa4caf6728ade7598](https://github.com/hearot/pyrubrum/commit/7d6b33b3af33a7a69999966aa4caf6728ade7598))
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ bot = Client(...)

handler = Handler(transform(
{
Menu("Main", "main", "Hello!"): {
Menu("Start", "start", "Hello!", default=True): {
Menu("About me", "about_me", "I'm just a bot!"),
Menu("Thoughts", "thoughts",
"I'm a bot, I cannot think properly...")
Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Welcome to Pyrubrum
handler = Handler(transform(
{
Menu("Main", "main", "Hello!"): {
Menu("Start", "start", "Hello!"): {
Menu("About me", "about_me", "I'm just a bot!"),
Menu("Thoughts", "thoughts",
"I'm a bot, I cannot think properly...")
Expand Down
8 changes: 5 additions & 3 deletions examples/calendar_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from calendar import monthrange
from datetime import datetime
from typing import Set
from typing import Union

from environs import Env
Expand Down Expand Up @@ -63,10 +64,11 @@ def tell_about_the_day(handler, client, context, parameters):
tree = transform(
{
PageMenu(
"Main menu",
"main",
"Start",
"start",
"📅 Choose a year.",
generate_years(1970, 2044),
default=True,
limit_page=15,
limit=4,
): {
Expand Down Expand Up @@ -98,7 +100,7 @@ def main(
bot_token: str,
database: Union[DictDatabase, RedisDatabase],
session_name: str,
tree: Node,
tree: Set[Node],
):
bot = Client(
session_name, api_hash=api_hash, api_id=api_id, bot_token=bot_token
Expand Down
2 changes: 1 addition & 1 deletion examples/hitchhiker_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def main(

tree = transform(
{
Menu("Main", "main", answer_image_id): {
Menu("Start", "start", answer_image_id, default=True): {
Menu("🌌 Get the answer", "forty_two", forty_two_image_id),
}
}
Expand Down
10 changes: 8 additions & 2 deletions examples/sample_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrubrum. If not, see <http://www.gnu.org/licenses/>.

from typing import Set

from environs import Env
from pyrogram import Client

Expand All @@ -26,7 +28,7 @@

tree = transform(
{
Menu("Main", "main", "Hello!"): {
Menu("Start", "start", "Hello!", default=True): {
Menu("About me", "about_me", "I'm just a bot!"),
Menu(
"Thoughts", "thoughts", "I'm a bot, I cannot think properly..."
Expand All @@ -37,7 +39,11 @@


def main(
api_hash: str, api_id: int, bot_token: str, session_name: str, tree: Node
api_hash: str,
api_id: int,
bot_token: str,
session_name: str,
tree: Set[Node],
):
bot = Client(
session_name, api_hash=api_hash, api_id=api_id, bot_token=bot_token
Expand Down
75 changes: 52 additions & 23 deletions pyrubrum/handlers/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from typing import Tuple

from pyrogram import Client
from pyrogram import Filters
from pyrogram import MessageHandler

from pyrubrum.menus import BaseMenu
Expand All @@ -32,32 +33,33 @@

class Handler(BaseHandler):
"""Implementation of a simple handler for non parameterized menus which has
got, by definition, a main node whose linked menu is displayed to the user
whenever a message is being handled.
got, by definition, multiple top-level nodes whose linked menus are
displayed to the user whenever a message is being handled and matches one
of their filters.
Parameters:
main_node (Node): The node whose linked menu is used when the user
texts the bot (i.e. when a `Message` object is being handled).
In other words, it represents the ``/start`` menu.
nodes (Set[Node]): The top-level nodes, which represent the text
commands that are available to the user.
Note:
In order to make use of parameterized menus (e.g. `PageMenu`) or to
pass parameters between menus, you will have to use an handler which
supports such feature (e.g. `ParameterizedHandler`).
"""

def __init__(self, main_node: Node):
self.main_node = main_node
def __init__(self, nodes: Set[Node]):
self.nodes = nodes

@lru_cache
def get_family(
self, menu_id: str
) -> Tuple[Optional[BaseMenu], Optional[Set[BaseMenu]]]:
"""Retrieve the menus which are linked to both parent and children of the main
node of this instance if this instance matches the provided identifier.
Otherwise it will search the menu matching it in its children and
return its family, if matched. On failure, it will return a tuple of
length two filled with null values (i.e. ``None``).
"""Retrieve the menus which are linked to both parent and children of the
top-level nodes of this instance if this instance matches the provided
identifier. Otherwise it will search the menu matching it in the
children of the top-level nodes and will return their own families,
if matched. On failure, it will return a tuple of length two filled
with null values (i.e. ``None``).
Parameters:
menu_id (str): The identifier which must be matched.
Expand All @@ -69,31 +71,58 @@ def get_family(
If no `Node` is found, the tuple will be filled with null
values (i.e. ``None``).
"""
return self.main_node.get_family(menu_id, None)
for node in self.nodes:
family = node.get_family(menu_id, None)

if family[0] or family[1]:
return family

return (None, None)

@lru_cache(maxsize=1)
def get_menus(self) -> Set[BaseMenu]:
"""Retrieve the set of all the menus which are linked to the nodes belonging
to the descent of the main node of this class (i.e. the children, the
children of the children, etc...). In other words, it retrieves all the
menus which were defined at the initialization of this instance.
to the descent of the top-level nodes of this class (i.e. the children,
the children of the children, etc...). In other words, it retrieves all
the menus which were defined at the initialization of this instance.
Returns:
Set[BaseMenu]: The set of all the retrieved menus.
"""
return self.main_node.get_menus()
menus = set()

for node in self.nodes:
menus |= node.get_menus()

return menus

def setup(self, client: Client):
"""Set up a client instance by adding filters for handling callbacks and
messages. If a message is being handled, the menu which is linked to
the main node of this instance will be displayed.
messages. If there is a default menu, it adds it using
`MessageHandler <pyrogram.MessageHandler>`.
Parameters:
client (Client): The client you are setting up. It must be a bot
instance in order to work.
"""
BaseHandler.setup(self, client)

client.add_handler(
MessageHandler(pass_handler(self.main_node.menu.on_message, self))
)
default_menu = None

for node in self.nodes:
if not node.menu.message_filter:
node.menu.message_filter = Filters.command(node.menu.menu_id)

if node.menu.default:
default_menu = node.menu

client.add_handler(
MessageHandler(
pass_handler(node.menu.on_message, self),
node.menu.message_filter,
)
)

if default_menu:
client.add_handler(
MessageHandler(pass_handler(default_menu.on_message, self))
)
53 changes: 35 additions & 18 deletions pyrubrum/handlers/parameterized_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrubrum. If not, see <http://www.gnu.org/licenses/>.

from typing import Set

from pyrogram import Client
from pyrogram import Filters
from pyrogram import MessageHandler

from pyrubrum.database import BaseDatabase
Expand All @@ -28,23 +31,21 @@

class ParameterizedHandler(Handler, ParameterizedBaseHandler):
"""Implementation of an handler which mixes the features of `Handler` and
`ParameterizedBaseHandler` and has got, by definition, a main node whose
linked menu is displayed to the user whenever a message is being handled
and a database with which it is able to perform parameterization (i.e.
it supports parameters).
Parameters:
main_node (Node): The node whose linked menu is used when the user
texts the bot (i.e. when a `Message` object is being handled).
In other words, it represents the ``/start`` menu. See
`Handler` for more information.
`ParameterizedBaseHandler` and has got, by definition, multiple top-level
nodes whose linked menu are displayed to the user whenever a message is
being handled and matches one of their filters, and a database with which
it is possible to perform parameterization (i.e. it supports parameters).
nodes (Set[Node]): The top-level nodes, which represent the text
commands that are available to the user. See `Handler` for more
information
database (BaseDatabase): The storage for all the query parameters.
It is used to pass parameters between menus. See
`ParameterizedBaseHandler` for more information.
"""

def __init__(self, main_node: Node, database: BaseDatabase):
Handler.__init__(self, main_node)
def __init__(self, nodes: Set[Node], database: BaseDatabase):
Handler.__init__(self, nodes)
ParameterizedBaseHandler.__init__(self, database)

def setup(self, client: Client):
Expand All @@ -56,8 +57,8 @@ def setup(self, client: Client):
handled callback queries from the database relying on the passed
identifiers.
Finally, it makes the main menu (i.e. the menu which is linked to the
main node) reachable whenever a message is sent to the bot.
Finally, it makes the top-level menus reachable whenever a message is
sent to the bot and matches one of their filters.
Parameters:
client (Client): The client which is being set up.
Expand All @@ -70,9 +71,25 @@ def setup(self, client: Client):
callback(handler, client, context, parameters)
"""
ParameterizedBaseHandler.setup(self, client)
default_menu = None

for node in self.nodes:
if not node.menu.message_filter:
node.menu.message_filter = Filters.command(node.menu.menu_id)

if node.menu.default:
default_menu = node.menu

client.add_handler(
MessageHandler(
pass_handler_and_clean(node.menu.on_message, self),
node.menu.message_filter,
)
)

client.add_handler(
MessageHandler(
pass_handler_and_clean(self.main_node.menu.on_message, self)
if default_menu:
client.add_handler(
MessageHandler(
pass_handler_and_clean(default_menu.on_message, self)
)
)
)
15 changes: 15 additions & 0 deletions pyrubrum/menus/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from pyrogram import InlineKeyboardMarkup
from pyrogram import InputMedia
from pyrogram import Message
from pyrogram.client.filters.filters import Filter

from pyrubrum.keyboard import Keyboard
from pyrubrum.types import Types
Expand Down Expand Up @@ -58,7 +59,17 @@ class Menu(BaseMenu):
back_button_text (Optional[str]): The text which will be displayed
inside the button that lets the user go back to the parent
menu. Defaults to "🔙".
default (Optional[bool]): If the message shall be displayed by default
if if doesn't match any other menu.
limit (Optional[int]): The limit of buttons per row. Defaults to 2.
message_filter (Optional[Filter]): A filter for telling Pyrogram
when a message should be associated to this menu. It works only
for top-level menus (see `Handler.setup`). Defaults to ``None``,
which automatically makes this menu reachable when the user texts
a message that follows this pattern::
/[MENU_ID]
preliminary (Types.Preliminary): A function which is executed each time
before doing anything else in `on_callback` and `on_message`.
You can provide a list of such functions as well, which will be
Expand Down Expand Up @@ -87,13 +98,17 @@ def __init__(
],
],
back_button_text: Optional[str] = "🔙",
default: Optional[bool] = False,
limit: Optional[int] = 2,
message_filter: Optional[Filter] = None,
preliminary: Types.Preliminary = None,
):
BaseMenu.__init__(self, name, menu_id)
self.back_button_text = back_button_text
self.content = content
self.default = default
self.limit = limit
self.message_filter = message_filter
self.preliminary = preliminary

def get_content(
Expand Down
Loading

0 comments on commit 80bfb7d

Please sign in to comment.