todus-lib 1.3.2__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.
Files changed (35) hide show
  1. todus_lib-1.3.2/CHANGELOG.md +61 -0
  2. todus_lib-1.3.2/LICENSE +21 -0
  3. todus_lib-1.3.2/MANIFEST.in +4 -0
  4. todus_lib-1.3.2/PKG-INFO +298 -0
  5. todus_lib-1.3.2/README.md +267 -0
  6. todus_lib-1.3.2/pyproject.toml +56 -0
  7. todus_lib-1.3.2/setup.cfg +4 -0
  8. todus_lib-1.3.2/tests/test_proxy.py +65 -0
  9. todus_lib-1.3.2/tests/test_stanzas.py +170 -0
  10. todus_lib-1.3.2/tests/test_types.py +73 -0
  11. todus_lib-1.3.2/tests/test_util.py +175 -0
  12. todus_lib-1.3.2/todus/__init__.py +52 -0
  13. todus_lib-1.3.2/todus/client/__init__.py +277 -0
  14. todus_lib-1.3.2/todus/client/auth.py +89 -0
  15. todus_lib-1.3.2/todus/client/base.py +183 -0
  16. todus_lib-1.3.2/todus/client/file.py +190 -0
  17. todus_lib-1.3.2/todus/client/message.py +202 -0
  18. todus_lib-1.3.2/todus/client/profile.py +56 -0
  19. todus_lib-1.3.2/todus/constants.py +10 -0
  20. todus_lib-1.3.2/todus/errors.py +41 -0
  21. todus_lib-1.3.2/todus/group.py +370 -0
  22. todus_lib-1.3.2/todus/parser.py +415 -0
  23. todus_lib-1.3.2/todus/stanza.py +54 -0
  24. todus_lib-1.3.2/todus/stanzas/__init__.py +1 -0
  25. todus_lib-1.3.2/todus/stanzas/group.py +175 -0
  26. todus_lib-1.3.2/todus/stanzas/presence.py +37 -0
  27. todus_lib-1.3.2/todus/stanzas/private.py +203 -0
  28. todus_lib-1.3.2/todus/stanzas/utils.py +122 -0
  29. todus_lib-1.3.2/todus/types.py +54 -0
  30. todus_lib-1.3.2/todus/util.py +177 -0
  31. todus_lib-1.3.2/todus_lib.egg-info/PKG-INFO +298 -0
  32. todus_lib-1.3.2/todus_lib.egg-info/SOURCES.txt +33 -0
  33. todus_lib-1.3.2/todus_lib.egg-info/dependency_links.txt +1 -0
  34. todus_lib-1.3.2/todus_lib.egg-info/requires.txt +7 -0
  35. todus_lib-1.3.2/todus_lib.egg-info/top_level.txt +1 -0
@@ -0,0 +1,61 @@
1
+ # Changelog
2
+
3
+ Todos los cambios notables en este proyecto se documentan en este archivo.
4
+
5
+ El formato está basado en [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/),
6
+ y este proyecto sigue [Semantic Versioning](https://semver.org/lang/es/).
7
+
8
+ ## [1.3.0] - 2026-06-19
9
+
10
+ ### Added
11
+ - Soporte completo para grupos MUC Light (`GroupClient`)
12
+ - Roles de grupo (`GroupRole`) y eventos de grupo (`GroupEvent`)
13
+ - Auto-detección de destino privado/grupo en `ToDusClient2`
14
+ - Envío de mensajes de ubicación (`send_location_message`)
15
+ - Envío de mensajes de eventos/calendario (`send_event_message`)
16
+ - Callbacks específicos por grupo (`on_group_message`)
17
+ - Firma de URLs con nombre legible de archivo (`sanitize_filename`)
18
+ - Soporte de progreso en subidas (`progress_callback`)
19
+ - Módulo `stanzas/` reorganizado en subdirectorio
20
+ - Módulo `client/` reorganizado en subdirectorio con mixins
21
+ - `pyproject.toml` moderno (PEP 621)
22
+ - Tests unitarios con pytest
23
+ - CI/CD con GitHub Actions
24
+
25
+ ### Changed
26
+ - Estructura interna reorganizada para claridad
27
+ - Migración de `setup.py` a `pyproject.toml`
28
+
29
+ ## [1.2.0] - 2026-06-01
30
+
31
+ ### Added
32
+ - Envío de stickers (`send_sticker_message`)
33
+ - Envío de contactos (`send_contact_message`)
34
+ - Envío de botones interactivos (`send_button_message`)
35
+ - Edición de mensajes (`edit_message`)
36
+ - Eliminación de mensajes (`delete_message`)
37
+ - Parser incremental de stanzas XMPP (`IncrementalParser`)
38
+
39
+ ## [1.1.0] - 2026-05-15
40
+
41
+ ### Added
42
+ - Envío de imágenes con dimensiones y thumbnail
43
+ - Envío de videos con metadata
44
+ - Subida y descarga de archivos con `FileType`
45
+ - Perfil de usuario (alias, bio, avatar)
46
+ - Utilidades: `format_size`, `get_image_dimensions`, `generate_blurhash`
47
+
48
+ ## [1.0.0] - 2026-05-01
49
+
50
+ ### Added
51
+ - Cliente básico XMPP para ToDus (`ToDusClient`)
52
+ - Cliente stateful con auto-login (`ToDusClient2`)
53
+ - Autenticación por SMS + JWT
54
+ - Envío y recepción de mensajes de texto
55
+ - Manejo de excepciones personalizadas
56
+ - Constantes del protocolo ToDus
57
+
58
+ [1.3.0]: https://github.com/ElJoker63/toDus-API/compare/v1.2.0...v1.3.0
59
+ [1.2.0]: https://github.com/ElJoker63/toDus-API/compare/v1.1.0...v1.2.0
60
+ [1.1.0]: https://github.com/ElJoker63/toDus-API/compare/v1.0.0...v1.1.0
61
+ [1.0.0]: https://github.com/ElJoker63/toDus-API/releases/tag/v1.0.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ElJoker63
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ include CHANGELOG.md
4
+ include pyproject.toml
@@ -0,0 +1,298 @@
1
+ Metadata-Version: 2.4
2
+ Name: todus-lib
3
+ Version: 1.3.2
4
+ Summary: Cliente Python para ToDus (mensajería cubana) — chat, grupos, archivos, stickers y más
5
+ Author: ElJoker63
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ElJoker63/toDus-API
8
+ Project-URL: Repository, https://github.com/ElJoker63/toDus-API
9
+ Project-URL: Issues, https://github.com/ElJoker63/toDus-API/issues
10
+ Project-URL: Changelog, https://github.com/ElJoker63/toDus-API/blob/main/CHANGELOG.md
11
+ Keywords: todus,cuba,xmpp,messaging,chat
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Communications :: Chat
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: requests
25
+ Requires-Dist: pysocks
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7.0; extra == "dev"
28
+ Requires-Dist: build; extra == "dev"
29
+ Requires-Dist: twine; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ <h1>📱 todus-lib</h1>
33
+
34
+ <p align="center">
35
+ <a href="https://pypi.org/project/todus-lib/"><img src="https://img.shields.io/pypi/v/todus-lib.svg" alt="PyPI"></a>
36
+ <a href="https://pypi.org/project/todus-lib/"><img src="https://img.shields.io/pypi/pyversions/todus-lib.svg" alt="Python"></a>
37
+ <a href="https://github.com/ElJoker63/toDus-API/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ElJoker63/toDus-API.svg" alt="License"></a>
38
+ <a href="https://github.com/ElJoker63/toDus-API/actions"><img src="https://github.com/ElJoker63/toDus-API/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
39
+ </p>
40
+
41
+ <p><strong>Cliente Python para ToDus</strong> — la plataforma de mensajería instantánea cubana. Soporta chat privado, grupos MUC Light, archivos, imágenes, videos, stickers, botones interactivos y más.</p>
42
+
43
+ <ul>
44
+ <li><strong>Versión:</strong> 1.3.2</li>
45
+ <li><strong>Python:</strong> >= 3.11</li>
46
+ <li><strong>Autor:</strong> ElJoker63</li>
47
+ <li><strong>Licencia:</strong> MIT</li>
48
+ </ul>
49
+
50
+ <hr>
51
+
52
+ <h2>📦 Instalación</h2>
53
+
54
+ <pre><code>pip install todus-lib</code></pre>
55
+
56
+ <p>O directamente desde el repositorio:</p>
57
+
58
+ <pre><code>pip install git+https://github.com/ElJoker63/toDus-API.git</code></pre>
59
+
60
+ <p>Para desarrollo:</p>
61
+
62
+ <pre><code>git clone https://github.com/ElJoker63/toDus-API.git
63
+ cd toDus-API
64
+ pip install -e ".[dev]"</code></pre>
65
+
66
+ <hr>
67
+
68
+ <h2>🔐 Autenticación (¡Importante!)</h2>
69
+
70
+ <p>ToDus no usa contraseñas elegidas por el usuario. El proceso de autenticación tiene dos pasos:</p>
71
+
72
+ <ol>
73
+ <li>Obtener un token largo (96 caracteres) mediante validación por SMS.</li>
74
+ <li>Usar ese token para hacer login y obtener un JWT de sesión, que se usa para todas las comunicaciones.</li>
75
+ </ol>
76
+
77
+ <p>El cliente <code>ToDusClient2</code> guarda ese token largo en el atributo <code>password</code> (nombre histórico), pero en la práctica debes entenderlo como <code>auth_token</code>.</p>
78
+
79
+ <h3>🔹 Flujo para primera vez (SMS)</h3>
80
+
81
+ <pre><code>from todus import ToDusClient2
82
+
83
+ client = ToDusClient2(phone_number="535xxxxxxx") # sin password aún
84
+
85
+ # 1. Pedir código SMS
86
+ client.request_code() # te llega un PIN de 6 dígitos
87
+
88
+ # 2. Validar el código
89
+ client.validate_code("123456")
90
+ # Ahora client.password contiene el token largo (96 caracteres)
91
+ # ¡Guárdalo en un lugar seguro para futuras sesiones!
92
+
93
+ # 3. Obtener el JWT de sesión
94
+ client.login() # ahora client.logged == True</code></pre>
95
+
96
+ <h3>🔹 Si ya tienes el token largo (de una sesión anterior)</h3>
97
+
98
+ <pre><code>client = ToDusClient2(phone_number="535xxxxxxx", password="ese_token_largo_que_guardaste")
99
+ client.login() # obtiene el JWT internamente</code></pre>
100
+
101
+ <p><strong>⚠️ Importante:</strong> Nunca pases un JWT directamente al constructor. El cliente se encarga de renovarlo automáticamente cuando expira. El parámetro <code>password</code> espera el token largo de 96 caracteres.</p>
102
+
103
+ <hr>
104
+
105
+ <h2>🚀 Uso Rápido</h2>
106
+
107
+ <h3>Enviar mensajes (privados y grupos automáticamente)</h3>
108
+
109
+ <pre><code># Asumiendo que ya hiciste login
110
+ client.send_message("535yyyyyyy", "¡Hola mundo!") # privado
111
+ client.send_message("mi-grupo-id", "Hola grupo!") # grupo (auto-detectado)</code></pre>
112
+
113
+ <h3>Enviar imagen (con subida previa)</h3>
114
+
115
+ <pre><code>from todus import FileType
116
+
117
+ # 1. Subir la imagen
118
+ with open("foto.jpg", "rb") as f:
119
+ image_data = f.read()
120
+ url = client.upload_file(image_data, FileType.PICTURE)
121
+
122
+ # 2. Enviar el mensaje con la imagen
123
+ client.send_image_message(
124
+ "535yyyyyyy",
125
+ url=url,
126
+ file_name="foto.jpg",
127
+ file_size=len(image_data),
128
+ caption="Mi foto"
129
+ )</code></pre>
130
+
131
+ <h3>Escuchar mensajes entrantes</h3>
132
+
133
+ <pre><code>def on_message(msg):
134
+ if msg.get("body"):
135
+ print(f"{msg['from']}: {msg['body']}")
136
+
137
+ client.listen_messages(on_message) # bucle infinito</code></pre>
138
+
139
+ <hr>
140
+
141
+ <h2>🤖 Bot de Ejemplo</h2>
142
+
143
+ <p>En la carpeta <code>examples/</code> encontrarás un bot funcional con comandos:</p>
144
+
145
+ <table>
146
+ <thead>
147
+ <tr>
148
+ <th>Comando</th>
149
+ <th>Respuesta</th>
150
+ </tr>
151
+ </thead>
152
+ <tbody>
153
+ <tr><td><code>/start</code></td><td>Mensaje de bienvenida con lista de comandos</td></tr>
154
+ <tr><td><code>/info</code></td><td>Información sobre la librería</td></tr>
155
+ <tr><td><code>/ping</code></td><td><code>pong</code></td></tr>
156
+ </tbody>
157
+ </table>
158
+
159
+ <p>Ejecútalo con:</p>
160
+
161
+ <pre><code>export TODUS_PHONE=535xxxxxxx
162
+ export TODUS_AUTH_TOKEN=token_largo_de_96_caracteres
163
+ python examples/bot.py</code></pre>
164
+
165
+ <hr>
166
+
167
+ <h2>📡 Tipos de Mensaje Soportados</h2>
168
+
169
+ <p>La librería soporta los siguientes tipos de mensaje:</p>
170
+
171
+ <table>
172
+ <thead>
173
+ <tr>
174
+ <th>Tipo</th>
175
+ <th>Método (ToDusClient2)</th>
176
+ </tr>
177
+ </thead>
178
+ <tbody>
179
+ <tr><td>Texto</td><td><code>send_message(to, body)</code></td></tr>
180
+ <tr><td>Imagen</td><td><code>send_image_message(to, url, file_name, file_size, ...)</code></td></tr>
181
+ <tr><td>Video</td><td><code>send_video_message(to, url, video_id, file_name, ...)</code></td></tr>
182
+ <tr><td>Archivo</td><td><code>send_file_message(to, url, file_type, ...)</code></td></tr>
183
+ <tr><td>Sticker</td><td><code>send_sticker_message(to, sticker_id, ...)</code></td></tr>
184
+ <tr><td>Contacto</td><td><code>send_contact_message(to, contact_id, ...)</code></td></tr>
185
+ <tr><td>Botones</td><td><code>send_button_message(to, text, buttons)</code></td></tr>
186
+ <tr><td>Ubicación</td><td><code>send_location_message(to, lat, lon, ...)</code></td></tr>
187
+ <tr><td>Evento</td><td><code>send_event_message(to, title, start, end, ...)</code></td></tr>
188
+ <tr><td>Editar</td><td><code>edit_message(to, new_body, original_msg_id)</code></td></tr>
189
+ <tr><td>Eliminar</td><td><code>delete_message(to, message_id)</code></td></tr>
190
+ </tbody>
191
+ </table>
192
+
193
+ <p><strong>Auto-detección de destino:</strong> si el <code>to</code> no es un número cubano (10 dígitos empezando por 53), se asume que es un <code>group_id</code> y el mensaje se envía al grupo automáticamente.</p>
194
+
195
+ <hr>
196
+
197
+ <h2>👥 Grupos MUC Light</h2>
198
+
199
+ <pre><code># Unirse a un grupo
200
+ client.groups.join("mi-grupo-id")
201
+
202
+ # Enviar mensaje al grupo (auto-detectado)
203
+ client.send_message("mi-grupo-id", "Hola grupo!")
204
+
205
+ # Callback específico para mensajes de ese grupo
206
+ def on_group_msg(msg):
207
+ print(f"{msg['sender_phone']}: {msg['body']}")
208
+
209
+ client.groups.on_group_message("mi-grupo-id", on_group_msg)
210
+
211
+ # Salir del grupo
212
+ client.groups.leave("mi-grupo-id")</code></pre>
213
+
214
+ <hr>
215
+
216
+ <h2>📤 Subir y Descargar Archivos</h2>
217
+
218
+ <pre><code># Subir cualquier archivo
219
+ with open("documento.pdf", "rb") as f:
220
+ url = client.upload_file(f.read(), FileType.FILE)
221
+
222
+ # Descargar a una carpeta
223
+ size, path = client.download_file_to_folder(url, "./descargas")
224
+ print(f"Descargado {size} bytes en {path}")</code></pre>
225
+
226
+ <hr>
227
+
228
+ <h2>⚠️ Excepciones</h2>
229
+
230
+ <table>
231
+ <thead>
232
+ <tr>
233
+ <th>Excepción</th>
234
+ <th>Cuándo ocurre</th>
235
+ </tr>
236
+ </thead>
237
+ <tbody>
238
+ <tr><td><code>AuthenticationError</code></td><td>Credenciales inválidas o falta autenticación</td></tr>
239
+ <tr><td><code>TokenExpiredError</code></td><td>El token JWT expiró (el cliente lo renueva automáticamente si usas ToDusClient2)</td></tr>
240
+ <tr><td><code>ConnectionLostError</code></td><td>Se perdió la conexión XMPP</td></tr>
241
+ <tr><td><code>UploadError</code></td><td>Error al subir/descargar archivo</td></tr>
242
+ <tr><td><code>GroupError</code></td><td>Error en operación de grupo</td></tr>
243
+ <tr><td><code>ParseError</code></td><td>Stanza malformada</td></tr>
244
+ </tbody>
245
+ </table>
246
+
247
+ <hr>
248
+
249
+ <h2>🗂️ Estructura del Proyecto</h2>
250
+
251
+ <pre><code>toDus-API/
252
+ ├── todus/ # Paquete principal
253
+ │ ├── __init__.py # Exports y versión
254
+ │ ├── client/ # Cliente XMPP/HTTP
255
+ │ │ ├── __init__.py # ToDusClient y ToDusClient2
256
+ │ │ ├── base.py # Conexión y transporte
257
+ │ │ ├── auth.py # Autenticación SMS/JWT
258
+ │ │ ├── message.py # Envío/recepción de mensajes
259
+ │ │ ├── file.py # Subida/descarga de archivos
260
+ │ │ └── profile.py # Perfil de usuario
261
+ │ ├── stanzas/ # Generadores de stanzas XML
262
+ │ │ ├── __init__.py
263
+ │ │ ├── private.py # Chat privado
264
+ │ │ ├── group.py # Chat grupal
265
+ │ │ ├── presence.py # Presencia XMPP
266
+ │ │ └── utils.py # Utilidades de protocolo
267
+ │ ├── group.py # Cliente de grupos MUC Light
268
+ │ ├── parser.py # Parser incremental de stanzas
269
+ │ ├── stanza.py # Re-exports unificados
270
+ │ ├── types.py # Enums (FileType, ChatState, etc.)
271
+ │ ├── util.py # Utilidades (JID, XML, JWT)
272
+ │ ├── constants.py # Hosts, puertos, versiones
273
+ │ └── errors.py # Excepciones personalizadas
274
+ ├── tests/ # Tests unitarios
275
+ │ ├── test_util.py
276
+ │ ├── test_types.py
277
+ │ └── test_stanzas.py
278
+ ├── examples/ # Ejemplos de uso
279
+ │ └── bot.py
280
+ ├── .github/workflows/ # CI/CD
281
+ │ ├── ci.yml # Tests en push/PR
282
+ │ └── pypi-publish.yml # Publicación a PyPI
283
+ ├── pyproject.toml # Configuración del paquete
284
+ ├── LICENSE # Licencia MIT
285
+ ├── CHANGELOG.md # Registro de cambios
286
+ ├── CONTRIBUTING.md # Guía para contribuir
287
+ ├── MANIFEST.in # Archivos incluidos en sdist
288
+ └── README.md # Este archivo</code></pre>
289
+
290
+ <hr>
291
+
292
+ <h2>🔗 Recursos</h2>
293
+
294
+ - **ToDus oficial:** [ToDus](https://web.todus.cu)
295
+ - **Apklis:** [Apklis](https://www.apklis.cu/application/cu.todus.android)
296
+ - **PyPI:** [todus-lib](https://pypi.org/project/todus-lib/)
297
+ - **Changelog:** [CHANGELOG.md](CHANGELOG.md)
298
+ - **Contribuir:** [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -0,0 +1,267 @@
1
+ <h1>📱 todus-lib</h1>
2
+
3
+ <p align="center">
4
+ <a href="https://pypi.org/project/todus-lib/"><img src="https://img.shields.io/pypi/v/todus-lib.svg" alt="PyPI"></a>
5
+ <a href="https://pypi.org/project/todus-lib/"><img src="https://img.shields.io/pypi/pyversions/todus-lib.svg" alt="Python"></a>
6
+ <a href="https://github.com/ElJoker63/toDus-API/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ElJoker63/toDus-API.svg" alt="License"></a>
7
+ <a href="https://github.com/ElJoker63/toDus-API/actions"><img src="https://github.com/ElJoker63/toDus-API/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
8
+ </p>
9
+
10
+ <p><strong>Cliente Python para ToDus</strong> — la plataforma de mensajería instantánea cubana. Soporta chat privado, grupos MUC Light, archivos, imágenes, videos, stickers, botones interactivos y más.</p>
11
+
12
+ <ul>
13
+ <li><strong>Versión:</strong> 1.3.2</li>
14
+ <li><strong>Python:</strong> >= 3.11</li>
15
+ <li><strong>Autor:</strong> ElJoker63</li>
16
+ <li><strong>Licencia:</strong> MIT</li>
17
+ </ul>
18
+
19
+ <hr>
20
+
21
+ <h2>📦 Instalación</h2>
22
+
23
+ <pre><code>pip install todus-lib</code></pre>
24
+
25
+ <p>O directamente desde el repositorio:</p>
26
+
27
+ <pre><code>pip install git+https://github.com/ElJoker63/toDus-API.git</code></pre>
28
+
29
+ <p>Para desarrollo:</p>
30
+
31
+ <pre><code>git clone https://github.com/ElJoker63/toDus-API.git
32
+ cd toDus-API
33
+ pip install -e ".[dev]"</code></pre>
34
+
35
+ <hr>
36
+
37
+ <h2>🔐 Autenticación (¡Importante!)</h2>
38
+
39
+ <p>ToDus no usa contraseñas elegidas por el usuario. El proceso de autenticación tiene dos pasos:</p>
40
+
41
+ <ol>
42
+ <li>Obtener un token largo (96 caracteres) mediante validación por SMS.</li>
43
+ <li>Usar ese token para hacer login y obtener un JWT de sesión, que se usa para todas las comunicaciones.</li>
44
+ </ol>
45
+
46
+ <p>El cliente <code>ToDusClient2</code> guarda ese token largo en el atributo <code>password</code> (nombre histórico), pero en la práctica debes entenderlo como <code>auth_token</code>.</p>
47
+
48
+ <h3>🔹 Flujo para primera vez (SMS)</h3>
49
+
50
+ <pre><code>from todus import ToDusClient2
51
+
52
+ client = ToDusClient2(phone_number="535xxxxxxx") # sin password aún
53
+
54
+ # 1. Pedir código SMS
55
+ client.request_code() # te llega un PIN de 6 dígitos
56
+
57
+ # 2. Validar el código
58
+ client.validate_code("123456")
59
+ # Ahora client.password contiene el token largo (96 caracteres)
60
+ # ¡Guárdalo en un lugar seguro para futuras sesiones!
61
+
62
+ # 3. Obtener el JWT de sesión
63
+ client.login() # ahora client.logged == True</code></pre>
64
+
65
+ <h3>🔹 Si ya tienes el token largo (de una sesión anterior)</h3>
66
+
67
+ <pre><code>client = ToDusClient2(phone_number="535xxxxxxx", password="ese_token_largo_que_guardaste")
68
+ client.login() # obtiene el JWT internamente</code></pre>
69
+
70
+ <p><strong>⚠️ Importante:</strong> Nunca pases un JWT directamente al constructor. El cliente se encarga de renovarlo automáticamente cuando expira. El parámetro <code>password</code> espera el token largo de 96 caracteres.</p>
71
+
72
+ <hr>
73
+
74
+ <h2>🚀 Uso Rápido</h2>
75
+
76
+ <h3>Enviar mensajes (privados y grupos automáticamente)</h3>
77
+
78
+ <pre><code># Asumiendo que ya hiciste login
79
+ client.send_message("535yyyyyyy", "¡Hola mundo!") # privado
80
+ client.send_message("mi-grupo-id", "Hola grupo!") # grupo (auto-detectado)</code></pre>
81
+
82
+ <h3>Enviar imagen (con subida previa)</h3>
83
+
84
+ <pre><code>from todus import FileType
85
+
86
+ # 1. Subir la imagen
87
+ with open("foto.jpg", "rb") as f:
88
+ image_data = f.read()
89
+ url = client.upload_file(image_data, FileType.PICTURE)
90
+
91
+ # 2. Enviar el mensaje con la imagen
92
+ client.send_image_message(
93
+ "535yyyyyyy",
94
+ url=url,
95
+ file_name="foto.jpg",
96
+ file_size=len(image_data),
97
+ caption="Mi foto"
98
+ )</code></pre>
99
+
100
+ <h3>Escuchar mensajes entrantes</h3>
101
+
102
+ <pre><code>def on_message(msg):
103
+ if msg.get("body"):
104
+ print(f"{msg['from']}: {msg['body']}")
105
+
106
+ client.listen_messages(on_message) # bucle infinito</code></pre>
107
+
108
+ <hr>
109
+
110
+ <h2>🤖 Bot de Ejemplo</h2>
111
+
112
+ <p>En la carpeta <code>examples/</code> encontrarás un bot funcional con comandos:</p>
113
+
114
+ <table>
115
+ <thead>
116
+ <tr>
117
+ <th>Comando</th>
118
+ <th>Respuesta</th>
119
+ </tr>
120
+ </thead>
121
+ <tbody>
122
+ <tr><td><code>/start</code></td><td>Mensaje de bienvenida con lista de comandos</td></tr>
123
+ <tr><td><code>/info</code></td><td>Información sobre la librería</td></tr>
124
+ <tr><td><code>/ping</code></td><td><code>pong</code></td></tr>
125
+ </tbody>
126
+ </table>
127
+
128
+ <p>Ejecútalo con:</p>
129
+
130
+ <pre><code>export TODUS_PHONE=535xxxxxxx
131
+ export TODUS_AUTH_TOKEN=token_largo_de_96_caracteres
132
+ python examples/bot.py</code></pre>
133
+
134
+ <hr>
135
+
136
+ <h2>📡 Tipos de Mensaje Soportados</h2>
137
+
138
+ <p>La librería soporta los siguientes tipos de mensaje:</p>
139
+
140
+ <table>
141
+ <thead>
142
+ <tr>
143
+ <th>Tipo</th>
144
+ <th>Método (ToDusClient2)</th>
145
+ </tr>
146
+ </thead>
147
+ <tbody>
148
+ <tr><td>Texto</td><td><code>send_message(to, body)</code></td></tr>
149
+ <tr><td>Imagen</td><td><code>send_image_message(to, url, file_name, file_size, ...)</code></td></tr>
150
+ <tr><td>Video</td><td><code>send_video_message(to, url, video_id, file_name, ...)</code></td></tr>
151
+ <tr><td>Archivo</td><td><code>send_file_message(to, url, file_type, ...)</code></td></tr>
152
+ <tr><td>Sticker</td><td><code>send_sticker_message(to, sticker_id, ...)</code></td></tr>
153
+ <tr><td>Contacto</td><td><code>send_contact_message(to, contact_id, ...)</code></td></tr>
154
+ <tr><td>Botones</td><td><code>send_button_message(to, text, buttons)</code></td></tr>
155
+ <tr><td>Ubicación</td><td><code>send_location_message(to, lat, lon, ...)</code></td></tr>
156
+ <tr><td>Evento</td><td><code>send_event_message(to, title, start, end, ...)</code></td></tr>
157
+ <tr><td>Editar</td><td><code>edit_message(to, new_body, original_msg_id)</code></td></tr>
158
+ <tr><td>Eliminar</td><td><code>delete_message(to, message_id)</code></td></tr>
159
+ </tbody>
160
+ </table>
161
+
162
+ <p><strong>Auto-detección de destino:</strong> si el <code>to</code> no es un número cubano (10 dígitos empezando por 53), se asume que es un <code>group_id</code> y el mensaje se envía al grupo automáticamente.</p>
163
+
164
+ <hr>
165
+
166
+ <h2>👥 Grupos MUC Light</h2>
167
+
168
+ <pre><code># Unirse a un grupo
169
+ client.groups.join("mi-grupo-id")
170
+
171
+ # Enviar mensaje al grupo (auto-detectado)
172
+ client.send_message("mi-grupo-id", "Hola grupo!")
173
+
174
+ # Callback específico para mensajes de ese grupo
175
+ def on_group_msg(msg):
176
+ print(f"{msg['sender_phone']}: {msg['body']}")
177
+
178
+ client.groups.on_group_message("mi-grupo-id", on_group_msg)
179
+
180
+ # Salir del grupo
181
+ client.groups.leave("mi-grupo-id")</code></pre>
182
+
183
+ <hr>
184
+
185
+ <h2>📤 Subir y Descargar Archivos</h2>
186
+
187
+ <pre><code># Subir cualquier archivo
188
+ with open("documento.pdf", "rb") as f:
189
+ url = client.upload_file(f.read(), FileType.FILE)
190
+
191
+ # Descargar a una carpeta
192
+ size, path = client.download_file_to_folder(url, "./descargas")
193
+ print(f"Descargado {size} bytes en {path}")</code></pre>
194
+
195
+ <hr>
196
+
197
+ <h2>⚠️ Excepciones</h2>
198
+
199
+ <table>
200
+ <thead>
201
+ <tr>
202
+ <th>Excepción</th>
203
+ <th>Cuándo ocurre</th>
204
+ </tr>
205
+ </thead>
206
+ <tbody>
207
+ <tr><td><code>AuthenticationError</code></td><td>Credenciales inválidas o falta autenticación</td></tr>
208
+ <tr><td><code>TokenExpiredError</code></td><td>El token JWT expiró (el cliente lo renueva automáticamente si usas ToDusClient2)</td></tr>
209
+ <tr><td><code>ConnectionLostError</code></td><td>Se perdió la conexión XMPP</td></tr>
210
+ <tr><td><code>UploadError</code></td><td>Error al subir/descargar archivo</td></tr>
211
+ <tr><td><code>GroupError</code></td><td>Error en operación de grupo</td></tr>
212
+ <tr><td><code>ParseError</code></td><td>Stanza malformada</td></tr>
213
+ </tbody>
214
+ </table>
215
+
216
+ <hr>
217
+
218
+ <h2>🗂️ Estructura del Proyecto</h2>
219
+
220
+ <pre><code>toDus-API/
221
+ ├── todus/ # Paquete principal
222
+ │ ├── __init__.py # Exports y versión
223
+ │ ├── client/ # Cliente XMPP/HTTP
224
+ │ │ ├── __init__.py # ToDusClient y ToDusClient2
225
+ │ │ ├── base.py # Conexión y transporte
226
+ │ │ ├── auth.py # Autenticación SMS/JWT
227
+ │ │ ├── message.py # Envío/recepción de mensajes
228
+ │ │ ├── file.py # Subida/descarga de archivos
229
+ │ │ └── profile.py # Perfil de usuario
230
+ │ ├── stanzas/ # Generadores de stanzas XML
231
+ │ │ ├── __init__.py
232
+ │ │ ├── private.py # Chat privado
233
+ │ │ ├── group.py # Chat grupal
234
+ │ │ ├── presence.py # Presencia XMPP
235
+ │ │ └── utils.py # Utilidades de protocolo
236
+ │ ├── group.py # Cliente de grupos MUC Light
237
+ │ ├── parser.py # Parser incremental de stanzas
238
+ │ ├── stanza.py # Re-exports unificados
239
+ │ ├── types.py # Enums (FileType, ChatState, etc.)
240
+ │ ├── util.py # Utilidades (JID, XML, JWT)
241
+ │ ├── constants.py # Hosts, puertos, versiones
242
+ │ └── errors.py # Excepciones personalizadas
243
+ ├── tests/ # Tests unitarios
244
+ │ ├── test_util.py
245
+ │ ├── test_types.py
246
+ │ └── test_stanzas.py
247
+ ├── examples/ # Ejemplos de uso
248
+ │ └── bot.py
249
+ ├── .github/workflows/ # CI/CD
250
+ │ ├── ci.yml # Tests en push/PR
251
+ │ └── pypi-publish.yml # Publicación a PyPI
252
+ ├── pyproject.toml # Configuración del paquete
253
+ ├── LICENSE # Licencia MIT
254
+ ├── CHANGELOG.md # Registro de cambios
255
+ ├── CONTRIBUTING.md # Guía para contribuir
256
+ ├── MANIFEST.in # Archivos incluidos en sdist
257
+ └── README.md # Este archivo</code></pre>
258
+
259
+ <hr>
260
+
261
+ <h2>🔗 Recursos</h2>
262
+
263
+ - **ToDus oficial:** [ToDus](https://web.todus.cu)
264
+ - **Apklis:** [Apklis](https://www.apklis.cu/application/cu.todus.android)
265
+ - **PyPI:** [todus-lib](https://pypi.org/project/todus-lib/)
266
+ - **Changelog:** [CHANGELOG.md](CHANGELOG.md)
267
+ - **Contribuir:** [CONTRIBUTING.md](CONTRIBUTING.md)