27
27
28
28
class ConversationHandler (Handler ):
29
29
"""
30
- A handler to hold a conversation with a user by managing three collections of other handlers.
30
+ A handler to hold a conversation with a user by managing four collections of other handlers.
31
31
32
32
The first collection, a ``list`` named ``entry_points``, is used to initiate the conversation,
33
33
for example with a ``CommandHandler`` or ``RegexHandler``.
@@ -43,34 +43,64 @@ class ConversationHandler(Handler):
43
43
a regular text message is expected. You could use this for a ``/cancel`` command or to let the
44
44
user know their message was not recognized.
45
45
46
+ The fourth, optional collection of handlers, a ``list`` named ``timed_out_behavior`` is used if
47
+ the wait for ``run_async`` takes longer than defined in ``run_async_timeout``. For example,
48
+ you can let the user know that they should wait for a bit before they can continue.
49
+
46
50
To change the state of conversation, the callback function of a handler must return the new
47
51
state after responding to the user. If it does not return anything (returning ``None`` by
48
52
default), the state will not change. To end the conversation, the callback function must
49
- return ``CallbackHandler.END`` or -1 .
53
+ return ``CallbackHandler.END`` or ``-1`` .
50
54
51
55
Args:
52
56
entry_points (list): A list of ``Handler`` objects that can trigger the start of the
53
- conversation.
57
+ conversation. The first handler which ``check_update`` method returns ``True`` will be
58
+ used. If all return ``False``, the update is not handled.
54
59
states (dict): A ``dict[object: list[Handler]]`` that defines the different states of
55
60
conversation a user can be in and one or more associated ``Handler`` objects that
56
61
should be used in that state. The first handler which ``check_update`` method returns
57
62
``True`` will be used.
58
63
fallbacks (list): A list of handlers that might be used if the user is in a conversation,
59
64
but every handler for their current state returned ``False`` on ``check_update``.
65
+ The first handler which ``check_update`` method returns ``True`` will be used. If all
66
+ return ``False``, the update is not handled.
60
67
allow_reentry (Optional[bool]): If set to ``True``, a user that is currently in a
61
68
conversation can restart the conversation by triggering one of the entry points.
69
+ run_async_timeout (Optional[float]): If the previous handler for this user was running
70
+ asynchronously using the ``run_async`` decorator, it might not be finished when the
71
+ next message arrives. This timeout defines how long the conversation handler should
72
+ wait for the next state to be computed. The default is ``None`` which means it will
73
+ wait indefinitely.
74
+ timed_out_behavior (Optional[list]): A list of handlers that might be used if
75
+ the wait for ``run_async`` timed out. The first handler which ``check_update`` method
76
+ returns ``True`` will be used. If all return ``False``, the update is not handled.
77
+
62
78
"""
63
79
64
80
END = - 1
65
81
66
- def __init__ (self , entry_points , states , fallbacks , allow_reentry = False ):
82
+ def __init__ (self ,
83
+ entry_points ,
84
+ states ,
85
+ fallbacks ,
86
+ allow_reentry = False ,
87
+ run_async_timeout = None ,
88
+ timed_out_behavior = None ):
89
+
67
90
self .entry_points = entry_points
68
91
""":type: list[telegram.ext.Handler]"""
92
+
69
93
self .states = states
70
94
""":type: dict[str: telegram.ext.Handler]"""
95
+
71
96
self .fallbacks = fallbacks
72
97
""":type: list[telegram.ext.Handler]"""
98
+
73
99
self .allow_reentry = allow_reentry
100
+ self .run_async_timeout = run_async_timeout
101
+
102
+ self .timed_out_behavior = timed_out_behavior
103
+ """:type: list[telegram.ext.Handler]"""
74
104
75
105
self .conversations = dict ()
76
106
""":type: dict[(int, int): str]"""
@@ -112,9 +142,26 @@ def check_update(self, update):
112
142
key = (chat .id , user .id ) if chat else (None , user .id )
113
143
state = self .conversations .get (key )
114
144
145
+ # Resolve promises
115
146
if isinstance (state , Promise ):
116
147
self .logger .debug ('waiting for promise...' )
117
- state = state .result ()
148
+ state .result (timeout = self .run_async_timeout )
149
+
150
+ if state .done .is_set ():
151
+ self .update_state (state .result ())
152
+ state = self .conversations .get (key )
153
+
154
+ else :
155
+ for candidate in (self .timed_out_behavior or []):
156
+ if candidate .check_update (update ):
157
+ # Save the current user and the selected handler for handle_update
158
+ self .current_conversation = key
159
+ self .current_handler = candidate
160
+
161
+ return True
162
+
163
+ else :
164
+ return False
118
165
119
166
self .logger .debug ('selecting conversation %s with state %s' % (str (key ), str (state )))
120
167
@@ -160,6 +207,9 @@ def handle_update(self, update, dispatcher):
160
207
161
208
new_state = self .current_handler .handle_update (update , dispatcher )
162
209
210
+ self .update_state (new_state )
211
+
212
+ def update_state (self , new_state ):
163
213
if new_state == self .END :
164
214
del self .conversations [self .current_conversation ]
165
215
elif new_state is not None :
0 commit comments