python-zatolox-bot 0.1.1__tar.gz → 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-zatolox-bot
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: SDK ufficiale per costruire bot su Zatolox
5
5
  Home-page: https://github.com/zatolox/python-zatolox-bot
6
6
  Author: Zatolox
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-zatolox-bot
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: SDK ufficiale per costruire bot su Zatolox
5
5
  Home-page: https://github.com/zatolox/python-zatolox-bot
6
6
  Author: Zatolox
@@ -5,7 +5,7 @@ from setuptools import setup, find_packages
5
5
  # sono, meno se ne rompono.
6
6
  setup(
7
7
  name="python-zatolox-bot",
8
- version="0.1.1",
8
+ version="0.1.3",
9
9
  description="SDK ufficiale per costruire bot su Zatolox",
10
10
  long_description=(
11
11
  "Wrapper leggero attorno al gateway HTTP dei bot di Zatolox. "
@@ -3,5 +3,5 @@
3
3
  from .bot import ZatoloxBot, ZatoloxApiError
4
4
  from .types import User, Message, CallbackQuery, Update
5
5
 
6
- __version__ = "0.1.1"
6
+ __version__ = "0.1.3"
7
7
  __all__ = ["ZatoloxBot", "ZatoloxApiError", "User", "Message", "CallbackQuery", "Update"]
@@ -45,6 +45,9 @@ class ZatoloxBot:
45
45
  self.command_handlers: Dict[str, Callable[[Message], None]] = {}
46
46
  self._message_handler: Optional[Callable[[Message], None]] = None
47
47
  self._callback_handler: Optional[Callable[[CallbackQuery], None]] = None
48
+ # Handler di callback CON PREDICATO (@bot.callback_query_handler(func=...)): coppie
49
+ # (predicato, funzione). Il primo il cui predicato e' vero riceve il click.
50
+ self._callback_query_handlers: List[tuple] = []
48
51
 
49
52
  # Offset del prossimo getUpdates. Avanza a `ultimo update_id + 1`: e' cosi' che si conferma
50
53
  # al server di aver ricevuto tutto cio' che precede, e non lo si riceve piu'.
@@ -111,6 +114,41 @@ class ZatoloxBot:
111
114
  norm.append({"command": str(c[0]), "description": str(c[1])})
112
115
  return self._call("setMyCommands", {"commands": norm})
113
116
 
117
+ def edit_message_text(
118
+ self, chat_id: str, message_id: str, text: str, reply_markup: Optional[Any] = None
119
+ ) -> Optional[Message]:
120
+ """
121
+ Riscrive un PROPRIO messaggio (testo + eventuali pulsanti). La via per navigare un menu:
122
+ clic -> callback -> edit del testo e dei pulsanti, senza inviare un nuovo messaggio.
123
+
124
+ Se passi `reply_markup` aggiorna la tastiera (None esplicito la rimuove); se lo ometti, i
125
+ pulsanti restano quelli di prima. Un bot puo' modificare solo i messaggi che ha inviato lui.
126
+ """
127
+ payload: Dict[str, Any] = {"chat_id": chat_id, "message_id": message_id, "text": text}
128
+ if reply_markup is not None:
129
+ payload["reply_markup"] = reply_markup
130
+ return Message.from_dict(self._call("editMessageText", payload))
131
+
132
+ def edit_message_reply_markup(
133
+ self, chat_id: str, message_id: str, reply_markup: Optional[Any] = None
134
+ ) -> Optional[Message]:
135
+ """Cambia SOLO i pulsanti di un proprio messaggio, senza toccarne il testo (utile per i
136
+ sottomenu). `reply_markup=None` toglie la tastiera."""
137
+ return Message.from_dict(self._call(
138
+ "editMessageReplyMarkup",
139
+ {"chat_id": chat_id, "message_id": message_id, "reply_markup": reply_markup}
140
+ ))
141
+
142
+ def answer_callback_query(self, callback_query_id: str, text: Optional[str] = None) -> Any:
143
+ """
144
+ Conferma un click su un pulsante inline. Con `text` mostra un piccolo avviso a chi ha
145
+ toccato; senza, e' solo un ack. Da chiamare dentro l'handler della callback.
146
+ """
147
+ payload: Dict[str, Any] = {"callback_query_id": callback_query_id}
148
+ if text is not None:
149
+ payload["text"] = text
150
+ return self._call("answerCallbackQuery", payload)
151
+
114
152
  # ------------------------------------------------------------------ handler
115
153
 
116
154
  def add_command_handler(self, command: str, callback: Callable[[Message], None]) -> None:
@@ -152,6 +190,24 @@ class ZatoloxBot:
152
190
  # Uso nudo (@bot.message_handler): Python passa gia' la funzione qui.
153
191
  return register(fn) if fn is not None else register
154
192
 
193
+ def callback_query_handler(self, func: Optional[Callable[[CallbackQuery], bool]] = None):
194
+ """
195
+ Decoratore per i click sui pulsanti inline, con un filtro:
196
+
197
+ @bot.callback_query_handler(func=lambda call: call.data == "next")
198
+ def on_next(call): ...
199
+
200
+ `func` e' un predicato sul CallbackQuery: il PRIMO handler il cui predicato e' vero riceve il
201
+ click. Ometti `func` (o passa None) per prenderli tutti. Coesiste con add_callback_handler,
202
+ che resta come fallback finale.
203
+ """
204
+ predicate = func if callable(func) else (lambda call: True)
205
+
206
+ def deco(fn: Callable[[CallbackQuery], None]) -> Callable[[CallbackQuery], None]:
207
+ self._callback_query_handlers.append((predicate, fn))
208
+ return fn
209
+ return deco
210
+
155
211
  # ------------------------------------------------------------------ instradamento
156
212
 
157
213
  def _dispatch(self, update: Update) -> None:
@@ -159,8 +215,16 @@ class ZatoloxBot:
159
215
  stampata, non propagata, cosi' un handler difettoso non ferma il polling."""
160
216
  try:
161
217
  if update.callback_query is not None:
218
+ cb = update.callback_query
219
+ # Prima gli handler con predicato, in ordine di registrazione: vince il primo che
220
+ # accetta. E' cio' che permette di smistare per callback_data (menu/sottomenu).
221
+ for predicate, fn in self._callback_query_handlers:
222
+ if predicate(cb):
223
+ fn(cb)
224
+ return
225
+ # Nessuno ha accettato: fallback al singolo handler generico, se c'e'.
162
226
  if self._callback_handler:
163
- self._callback_handler(update.callback_query)
227
+ self._callback_handler(cb)
164
228
  return
165
229
 
166
230
  msg = update.message
@@ -38,6 +38,9 @@ class Message:
38
38
  type: str = "TEXT"
39
39
  date: Optional[int] = None
40
40
  from_user: Optional[User] = None
41
+ # URL del media per foto/file (type IMAGE/FILE/...); None per il testo. Serve, p.es., a un bot
42
+ # che accetta un'immagine e la usa come avatar.
43
+ media_url: Optional[str] = None
41
44
 
42
45
  @classmethod
43
46
  def from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["Message"]:
@@ -50,6 +53,7 @@ class Message:
50
53
  type=d.get("type", "TEXT"),
51
54
  date=d.get("date"),
52
55
  from_user=User.from_dict(d.get("from")),
56
+ media_url=d.get("media_url"),
53
57
  )
54
58
 
55
59
  @property