1818# along with this program. If not, see [http://www.gnu.org/licenses/].
1919"""This module contains objects helps with creating menus for telegram bots."""
2020
21- # pylint: disable=undefined-variable, not-callable
21+ # pylint: disable=not-callable
2222
2323import uuid
24+ from collections import defaultdict
2425from itertools import chain
2526
2627from telegram import InlineKeyboardButton , InlineKeyboardMarkup
3031try :
3132 str_type = str
3233except NameError :
33- str_type = basestring # noqa
34+ str_type = basestring # noqa pylint: disable=undefined-variable
3435
3536
3637class Menu (object ):
@@ -39,13 +40,18 @@ class Menu(object):
3940 text = ''
4041 buttons = None
4142 data = {}
43+ root_menu = None # populated in menuhandler
44+ stack = None # Only used in root menu assigned in menuhandler
4245
4346 def __new__ (cls ):
4447 if not cls ._instance :
4548 cls ._instance = super (Menu , cls ).__new__ (cls )
4649 return cls ._instance
4750
4851 def callback (self , bot , update , user_data , chat_data ):
52+ _id = (update .callback_query .message .chat_id , update .callback_query .message .message_id
53+ ) if update .callback_query .message else update .callback_query .inline_message_id
54+ self .root_menu ().stack [_id ].append (self )
4955 try :
5056 return update .callback_query .edit_message_text (self .get_text (update ),
5157 reply_markup = self .keyboard (user_data ,
@@ -70,7 +76,7 @@ def keyboard(self, user_data, chat_data):
7076 def get_buttons (self ):
7177 if self ._buttons is None :
7278 if callable (self .buttons ):
73- self ._buttons = self .buttons ()
79+ self ._buttons = self .buttons () # noqa
7480 else :
7581 self ._buttons = self .buttons
7682 return self ._buttons
@@ -119,6 +125,7 @@ def __init__(self,
119125 self .name = str (uuid .uuid4 ())
120126
121127 self .parent_menu = None
128+ self .root_menu = None
122129
123130 super (Button , self ).__init__ (
124131 pass_update_queue = pass_update_queue ,
@@ -145,19 +152,40 @@ def text(self, user_data, chat_data):
145152 return self ._text
146153
147154
155+ class BackButton (Button ):
156+ def __init__ (self , text , name = None ):
157+ super (BackButton , self ).__init__ (text , callback = self ._callback ,
158+ pass_user_data = True , pass_chat_data = True , name = name )
159+
160+ def _callback (self , bot , update , user_data , chat_data ):
161+ _id = (update .callback_query .message .chat_id , update .callback_query .message .message_id
162+ ) if update .callback_query .message else update .callback_query .inline_message_id
163+
164+ stack = self .parent_menu ().root_menu ().stack [_id ]
165+ try :
166+ stack .pop ()
167+ last_menu = stack .pop ()
168+ except IndexError :
169+ last_menu = self .parent_menu ().root_menu ()
170+ last_menu .callback (bot , update , user_data , chat_data )
171+
172+
148173class MenuHandler (Handler ):
149174 def __init__ (self , menu ):
150175 self .menu = menu
151176 self .buttons = {}
152177 self .collect_buttons (self .menu )
153178
179+ menu .stack = defaultdict (list )
180+
154181 super (MenuHandler , self ).__init__ (
155182 pass_update_queue = None ,
156183 pass_job_queue = None ,
157184 pass_user_data = None ,
158185 pass_chat_data = None )
159186
160187 def collect_buttons (self , menu ):
188+ menu .root_menu = self .menu
161189 for button in chain .from_iterable (menu ().get_buttons ()):
162190 button .parent_menu = menu
163191 if button .name not in self .buttons and (button .callback is not None or
0 commit comments