toDus-API 1.3.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.
- todus_api-1.3.3/CHANGELOG.md +61 -0
- todus_api-1.3.3/LICENSE +21 -0
- todus_api-1.3.3/MANIFEST.in +4 -0
- todus_api-1.3.3/PKG-INFO +299 -0
- todus_api-1.3.3/README.md +268 -0
- todus_api-1.3.3/pyproject.toml +56 -0
- todus_api-1.3.3/setup.cfg +4 -0
- todus_api-1.3.3/tests/test_proxy.py +65 -0
- todus_api-1.3.3/tests/test_stanzas.py +170 -0
- todus_api-1.3.3/tests/test_types.py +73 -0
- todus_api-1.3.3/tests/test_util.py +175 -0
- todus_api-1.3.3/toDus_API.egg-info/PKG-INFO +299 -0
- todus_api-1.3.3/toDus_API.egg-info/SOURCES.txt +33 -0
- todus_api-1.3.3/toDus_API.egg-info/dependency_links.txt +1 -0
- todus_api-1.3.3/toDus_API.egg-info/requires.txt +7 -0
- todus_api-1.3.3/toDus_API.egg-info/top_level.txt +1 -0
- todus_api-1.3.3/todus/__init__.py +52 -0
- todus_api-1.3.3/todus/client/__init__.py +277 -0
- todus_api-1.3.3/todus/client/auth.py +89 -0
- todus_api-1.3.3/todus/client/base.py +183 -0
- todus_api-1.3.3/todus/client/file.py +190 -0
- todus_api-1.3.3/todus/client/message.py +202 -0
- todus_api-1.3.3/todus/client/profile.py +56 -0
- todus_api-1.3.3/todus/constants.py +10 -0
- todus_api-1.3.3/todus/errors.py +41 -0
- todus_api-1.3.3/todus/group.py +370 -0
- todus_api-1.3.3/todus/parser.py +415 -0
- todus_api-1.3.3/todus/stanza.py +54 -0
- todus_api-1.3.3/todus/stanzas/__init__.py +1 -0
- todus_api-1.3.3/todus/stanzas/group.py +175 -0
- todus_api-1.3.3/todus/stanzas/presence.py +37 -0
- todus_api-1.3.3/todus/stanzas/private.py +203 -0
- todus_api-1.3.3/todus/stanzas/utils.py +122 -0
- todus_api-1.3.3/todus/types.py +54 -0
- todus_api-1.3.3/todus/util.py +177 -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
|
todus_api-1.3.3/LICENSE
ADDED
|
@@ -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.
|
todus_api-1.3.3/PKG-INFO
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: toDus-API
|
|
3
|
+
Version: 1.3.3
|
|
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-API</h1>
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<a href="https://pypi.org/project/toDus-API/"><img src="https://img.shields.io/pypi/v/toDus-API" alt="PyPI"></a>
|
|
36
|
+
<a href="https://pypi.org/project/toDus-API/"><img src="https://img.shields.io/pypi/pyversions/toDus-API" alt="Python"></a>
|
|
37
|
+
<a href="https://github.com/ElJoker63/toDus-API/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/ElJoker63/toDus-API/ci.yml?branch=main&label=tests" alt="Tests"></a>
|
|
38
|
+
<a href="https://github.com/ElJoker63/toDus-API/actions/workflows/pypi-publish.yml"><img src="https://img.shields.io/github/actions/workflow/status/ElJoker63/toDus-API/pypi-publish.yml?branch=main&label=publish" alt="Publish"></a>
|
|
39
|
+
<a href="https://github.com/ElJoker63/toDus-API/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ElJoker63/toDus-API" alt="License"></a>
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<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>
|
|
43
|
+
|
|
44
|
+
<ul>
|
|
45
|
+
<li><strong>Versión:</strong> 1.3.3</li>
|
|
46
|
+
<li><strong>Python:</strong> >= 3.11</li>
|
|
47
|
+
<li><strong>Autor:</strong> ElJoker63</li>
|
|
48
|
+
<li><strong>Licencia:</strong> MIT</li>
|
|
49
|
+
</ul>
|
|
50
|
+
|
|
51
|
+
<hr>
|
|
52
|
+
|
|
53
|
+
<h2>📦 Instalación</h2>
|
|
54
|
+
|
|
55
|
+
<pre><code>pip install toDus-API</code></pre>
|
|
56
|
+
|
|
57
|
+
<p>O directamente desde el repositorio:</p>
|
|
58
|
+
|
|
59
|
+
<pre><code>pip install git+https://github.com/ElJoker63/toDus-API.git</code></pre>
|
|
60
|
+
|
|
61
|
+
<p>Para desarrollo:</p>
|
|
62
|
+
|
|
63
|
+
<pre><code>git clone https://github.com/ElJoker63/toDus-API.git
|
|
64
|
+
cd toDus-API
|
|
65
|
+
pip install -e ".[dev]"</code></pre>
|
|
66
|
+
|
|
67
|
+
<hr>
|
|
68
|
+
|
|
69
|
+
<h2>🔐 Autenticación (¡Importante!)</h2>
|
|
70
|
+
|
|
71
|
+
<p>ToDus no usa contraseñas elegidas por el usuario. El proceso de autenticación tiene dos pasos:</p>
|
|
72
|
+
|
|
73
|
+
<ol>
|
|
74
|
+
<li>Obtener un token largo (96 caracteres) mediante validación por SMS.</li>
|
|
75
|
+
<li>Usar ese token para hacer login y obtener un JWT de sesión, que se usa para todas las comunicaciones.</li>
|
|
76
|
+
</ol>
|
|
77
|
+
|
|
78
|
+
<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>
|
|
79
|
+
|
|
80
|
+
<h3>🔹 Flujo para primera vez (SMS)</h3>
|
|
81
|
+
|
|
82
|
+
<pre><code>from todus import ToDusClient2
|
|
83
|
+
|
|
84
|
+
client = ToDusClient2(phone_number="535xxxxxxx") # sin password aún
|
|
85
|
+
|
|
86
|
+
# 1. Pedir código SMS
|
|
87
|
+
client.request_code() # te llega un PIN de 6 dígitos
|
|
88
|
+
|
|
89
|
+
# 2. Validar el código
|
|
90
|
+
client.validate_code("123456")
|
|
91
|
+
# Ahora client.password contiene el token largo (96 caracteres)
|
|
92
|
+
# ¡Guárdalo en un lugar seguro para futuras sesiones!
|
|
93
|
+
|
|
94
|
+
# 3. Obtener el JWT de sesión
|
|
95
|
+
client.login() # ahora client.logged == True</code></pre>
|
|
96
|
+
|
|
97
|
+
<h3>🔹 Si ya tienes el token largo (de una sesión anterior)</h3>
|
|
98
|
+
|
|
99
|
+
<pre><code>client = ToDusClient2(phone_number="535xxxxxxx", password="ese_token_largo_que_guardaste")
|
|
100
|
+
client.login() # obtiene el JWT internamente</code></pre>
|
|
101
|
+
|
|
102
|
+
<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>
|
|
103
|
+
|
|
104
|
+
<hr>
|
|
105
|
+
|
|
106
|
+
<h2>🚀 Uso Rápido</h2>
|
|
107
|
+
|
|
108
|
+
<h3>Enviar mensajes (privados y grupos automáticamente)</h3>
|
|
109
|
+
|
|
110
|
+
<pre><code># Asumiendo que ya hiciste login
|
|
111
|
+
client.send_message("535yyyyyyy", "¡Hola mundo!") # privado
|
|
112
|
+
client.send_message("mi-grupo-id", "Hola grupo!") # grupo (auto-detectado)</code></pre>
|
|
113
|
+
|
|
114
|
+
<h3>Enviar imagen (con subida previa)</h3>
|
|
115
|
+
|
|
116
|
+
<pre><code>from todus import FileType
|
|
117
|
+
|
|
118
|
+
# 1. Subir la imagen
|
|
119
|
+
with open("foto.jpg", "rb") as f:
|
|
120
|
+
image_data = f.read()
|
|
121
|
+
url = client.upload_file(image_data, FileType.PICTURE)
|
|
122
|
+
|
|
123
|
+
# 2. Enviar el mensaje con la imagen
|
|
124
|
+
client.send_image_message(
|
|
125
|
+
"535yyyyyyy",
|
|
126
|
+
url=url,
|
|
127
|
+
file_name="foto.jpg",
|
|
128
|
+
file_size=len(image_data),
|
|
129
|
+
caption="Mi foto"
|
|
130
|
+
)</code></pre>
|
|
131
|
+
|
|
132
|
+
<h3>Escuchar mensajes entrantes</h3>
|
|
133
|
+
|
|
134
|
+
<pre><code>def on_message(msg):
|
|
135
|
+
if msg.get("body"):
|
|
136
|
+
print(f"{msg['from']}: {msg['body']}")
|
|
137
|
+
|
|
138
|
+
client.listen_messages(on_message) # bucle infinito</code></pre>
|
|
139
|
+
|
|
140
|
+
<hr>
|
|
141
|
+
|
|
142
|
+
<h2>🤖 Bot de Ejemplo</h2>
|
|
143
|
+
|
|
144
|
+
<p>En la carpeta <code>examples/</code> encontrarás un bot funcional con comandos:</p>
|
|
145
|
+
|
|
146
|
+
<table>
|
|
147
|
+
<thead>
|
|
148
|
+
<tr>
|
|
149
|
+
<th>Comando</th>
|
|
150
|
+
<th>Respuesta</th>
|
|
151
|
+
</tr>
|
|
152
|
+
</thead>
|
|
153
|
+
<tbody>
|
|
154
|
+
<tr><td><code>/start</code></td><td>Mensaje de bienvenida con lista de comandos</td></tr>
|
|
155
|
+
<tr><td><code>/info</code></td><td>Información sobre la librería</td></tr>
|
|
156
|
+
<tr><td><code>/ping</code></td><td><code>pong</code></td></tr>
|
|
157
|
+
</tbody>
|
|
158
|
+
</table>
|
|
159
|
+
|
|
160
|
+
<p>Ejecútalo con:</p>
|
|
161
|
+
|
|
162
|
+
<pre><code>export TODUS_PHONE=535xxxxxxx
|
|
163
|
+
export TODUS_AUTH_TOKEN=token_largo_de_96_caracteres
|
|
164
|
+
python examples/bot.py</code></pre>
|
|
165
|
+
|
|
166
|
+
<hr>
|
|
167
|
+
|
|
168
|
+
<h2>📡 Tipos de Mensaje Soportados</h2>
|
|
169
|
+
|
|
170
|
+
<p>La librería soporta los siguientes tipos de mensaje:</p>
|
|
171
|
+
|
|
172
|
+
<table>
|
|
173
|
+
<thead>
|
|
174
|
+
<tr>
|
|
175
|
+
<th>Tipo</th>
|
|
176
|
+
<th>Método (ToDusClient2)</th>
|
|
177
|
+
</tr>
|
|
178
|
+
</thead>
|
|
179
|
+
<tbody>
|
|
180
|
+
<tr><td>Texto</td><td><code>send_message(to, body)</code></td></tr>
|
|
181
|
+
<tr><td>Imagen</td><td><code>send_image_message(to, url, file_name, file_size, ...)</code></td></tr>
|
|
182
|
+
<tr><td>Video</td><td><code>send_video_message(to, url, video_id, file_name, ...)</code></td></tr>
|
|
183
|
+
<tr><td>Archivo</td><td><code>send_file_message(to, url, file_type, ...)</code></td></tr>
|
|
184
|
+
<tr><td>Sticker</td><td><code>send_sticker_message(to, sticker_id, ...)</code></td></tr>
|
|
185
|
+
<tr><td>Contacto</td><td><code>send_contact_message(to, contact_id, ...)</code></td></tr>
|
|
186
|
+
<tr><td>Botones</td><td><code>send_button_message(to, text, buttons)</code></td></tr>
|
|
187
|
+
<tr><td>Ubicación</td><td><code>send_location_message(to, lat, lon, ...)</code></td></tr>
|
|
188
|
+
<tr><td>Evento</td><td><code>send_event_message(to, title, start, end, ...)</code></td></tr>
|
|
189
|
+
<tr><td>Editar</td><td><code>edit_message(to, new_body, original_msg_id)</code></td></tr>
|
|
190
|
+
<tr><td>Eliminar</td><td><code>delete_message(to, message_id)</code></td></tr>
|
|
191
|
+
</tbody>
|
|
192
|
+
</table>
|
|
193
|
+
|
|
194
|
+
<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>
|
|
195
|
+
|
|
196
|
+
<hr>
|
|
197
|
+
|
|
198
|
+
<h2>👥 Grupos MUC Light</h2>
|
|
199
|
+
|
|
200
|
+
<pre><code># Unirse a un grupo
|
|
201
|
+
client.groups.join("mi-grupo-id")
|
|
202
|
+
|
|
203
|
+
# Enviar mensaje al grupo (auto-detectado)
|
|
204
|
+
client.send_message("mi-grupo-id", "Hola grupo!")
|
|
205
|
+
|
|
206
|
+
# Callback específico para mensajes de ese grupo
|
|
207
|
+
def on_group_msg(msg):
|
|
208
|
+
print(f"{msg['sender_phone']}: {msg['body']}")
|
|
209
|
+
|
|
210
|
+
client.groups.on_group_message("mi-grupo-id", on_group_msg)
|
|
211
|
+
|
|
212
|
+
# Salir del grupo
|
|
213
|
+
client.groups.leave("mi-grupo-id")</code></pre>
|
|
214
|
+
|
|
215
|
+
<hr>
|
|
216
|
+
|
|
217
|
+
<h2>📤 Subir y Descargar Archivos</h2>
|
|
218
|
+
|
|
219
|
+
<pre><code># Subir cualquier archivo
|
|
220
|
+
with open("documento.pdf", "rb") as f:
|
|
221
|
+
url = client.upload_file(f.read(), FileType.FILE)
|
|
222
|
+
|
|
223
|
+
# Descargar a una carpeta
|
|
224
|
+
size, path = client.download_file_to_folder(url, "./descargas")
|
|
225
|
+
print(f"Descargado {size} bytes en {path}")</code></pre>
|
|
226
|
+
|
|
227
|
+
<hr>
|
|
228
|
+
|
|
229
|
+
<h2>⚠️ Excepciones</h2>
|
|
230
|
+
|
|
231
|
+
<table>
|
|
232
|
+
<thead>
|
|
233
|
+
<tr>
|
|
234
|
+
<th>Excepción</th>
|
|
235
|
+
<th>Cuándo ocurre</th>
|
|
236
|
+
</tr>
|
|
237
|
+
</thead>
|
|
238
|
+
<tbody>
|
|
239
|
+
<tr><td><code>AuthenticationError</code></td><td>Credenciales inválidas o falta autenticación</td></tr>
|
|
240
|
+
<tr><td><code>TokenExpiredError</code></td><td>El token JWT expiró (el cliente lo renueva automáticamente si usas ToDusClient2)</td></tr>
|
|
241
|
+
<tr><td><code>ConnectionLostError</code></td><td>Se perdió la conexión XMPP</td></tr>
|
|
242
|
+
<tr><td><code>UploadError</code></td><td>Error al subir/descargar archivo</td></tr>
|
|
243
|
+
<tr><td><code>GroupError</code></td><td>Error en operación de grupo</td></tr>
|
|
244
|
+
<tr><td><code>ParseError</code></td><td>Stanza malformada</td></tr>
|
|
245
|
+
</tbody>
|
|
246
|
+
</table>
|
|
247
|
+
|
|
248
|
+
<hr>
|
|
249
|
+
|
|
250
|
+
<h2>🗂️ Estructura del Proyecto</h2>
|
|
251
|
+
|
|
252
|
+
<pre><code>toDus-API/
|
|
253
|
+
├── todus/ # Paquete principal
|
|
254
|
+
│ ├── __init__.py # Exports y versión
|
|
255
|
+
│ ├── client/ # Cliente XMPP/HTTP
|
|
256
|
+
│ │ ├── __init__.py # ToDusClient y ToDusClient2
|
|
257
|
+
│ │ ├── base.py # Conexión y transporte
|
|
258
|
+
│ │ ├── auth.py # Autenticación SMS/JWT
|
|
259
|
+
│ │ ├── message.py # Envío/recepción de mensajes
|
|
260
|
+
│ │ ├── file.py # Subida/descarga de archivos
|
|
261
|
+
│ │ └── profile.py # Perfil de usuario
|
|
262
|
+
│ ├── stanzas/ # Generadores de stanzas XML
|
|
263
|
+
│ │ ├── __init__.py
|
|
264
|
+
│ │ ├── private.py # Chat privado
|
|
265
|
+
│ │ ├── group.py # Chat grupal
|
|
266
|
+
│ │ ├── presence.py # Presencia XMPP
|
|
267
|
+
│ │ └── utils.py # Utilidades de protocolo
|
|
268
|
+
│ ├── group.py # Cliente de grupos MUC Light
|
|
269
|
+
│ ├── parser.py # Parser incremental de stanzas
|
|
270
|
+
│ ├── stanza.py # Re-exports unificados
|
|
271
|
+
│ ├── types.py # Enums (FileType, ChatState, etc.)
|
|
272
|
+
│ ├── util.py # Utilidades (JID, XML, JWT)
|
|
273
|
+
│ ├── constants.py # Hosts, puertos, versiones
|
|
274
|
+
│ └── errors.py # Excepciones personalizadas
|
|
275
|
+
├── tests/ # Tests unitarios
|
|
276
|
+
│ ├── test_util.py
|
|
277
|
+
│ ├── test_types.py
|
|
278
|
+
│ └── test_stanzas.py
|
|
279
|
+
├── examples/ # Ejemplos de uso
|
|
280
|
+
│ └── bot.py
|
|
281
|
+
├── .github/workflows/ # CI/CD
|
|
282
|
+
│ ├── ci.yml # Tests en push/PR
|
|
283
|
+
│ └── pypi-publish.yml # Publicación a PyPI
|
|
284
|
+
├── pyproject.toml # Configuración del paquete
|
|
285
|
+
├── LICENSE # Licencia MIT
|
|
286
|
+
├── CHANGELOG.md # Registro de cambios
|
|
287
|
+
├── CONTRIBUTING.md # Guía para contribuir
|
|
288
|
+
├── MANIFEST.in # Archivos incluidos en sdist
|
|
289
|
+
└── README.md # Este archivo</code></pre>
|
|
290
|
+
|
|
291
|
+
<hr>
|
|
292
|
+
|
|
293
|
+
<h2>🔗 Recursos</h2>
|
|
294
|
+
|
|
295
|
+
- **ToDus oficial:** [ToDus](https://web.todus.cu)
|
|
296
|
+
- **Apklis:** [Apklis](https://www.apklis.cu/application/cu.todus.android)
|
|
297
|
+
- **PyPI:** [toDus-API](https://pypi.org/project/toDus-API/)
|
|
298
|
+
- **Changelog:** [CHANGELOG.md](CHANGELOG.md)
|
|
299
|
+
- **Contribuir:** [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
<h1>📱 toDus-API</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://pypi.org/project/toDus-API/"><img src="https://img.shields.io/pypi/v/toDus-API" alt="PyPI"></a>
|
|
5
|
+
<a href="https://pypi.org/project/toDus-API/"><img src="https://img.shields.io/pypi/pyversions/toDus-API" alt="Python"></a>
|
|
6
|
+
<a href="https://github.com/ElJoker63/toDus-API/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/ElJoker63/toDus-API/ci.yml?branch=main&label=tests" alt="Tests"></a>
|
|
7
|
+
<a href="https://github.com/ElJoker63/toDus-API/actions/workflows/pypi-publish.yml"><img src="https://img.shields.io/github/actions/workflow/status/ElJoker63/toDus-API/pypi-publish.yml?branch=main&label=publish" alt="Publish"></a>
|
|
8
|
+
<a href="https://github.com/ElJoker63/toDus-API/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ElJoker63/toDus-API" alt="License"></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<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>
|
|
12
|
+
|
|
13
|
+
<ul>
|
|
14
|
+
<li><strong>Versión:</strong> 1.3.3</li>
|
|
15
|
+
<li><strong>Python:</strong> >= 3.11</li>
|
|
16
|
+
<li><strong>Autor:</strong> ElJoker63</li>
|
|
17
|
+
<li><strong>Licencia:</strong> MIT</li>
|
|
18
|
+
</ul>
|
|
19
|
+
|
|
20
|
+
<hr>
|
|
21
|
+
|
|
22
|
+
<h2>📦 Instalación</h2>
|
|
23
|
+
|
|
24
|
+
<pre><code>pip install toDus-API</code></pre>
|
|
25
|
+
|
|
26
|
+
<p>O directamente desde el repositorio:</p>
|
|
27
|
+
|
|
28
|
+
<pre><code>pip install git+https://github.com/ElJoker63/toDus-API.git</code></pre>
|
|
29
|
+
|
|
30
|
+
<p>Para desarrollo:</p>
|
|
31
|
+
|
|
32
|
+
<pre><code>git clone https://github.com/ElJoker63/toDus-API.git
|
|
33
|
+
cd toDus-API
|
|
34
|
+
pip install -e ".[dev]"</code></pre>
|
|
35
|
+
|
|
36
|
+
<hr>
|
|
37
|
+
|
|
38
|
+
<h2>🔐 Autenticación (¡Importante!)</h2>
|
|
39
|
+
|
|
40
|
+
<p>ToDus no usa contraseñas elegidas por el usuario. El proceso de autenticación tiene dos pasos:</p>
|
|
41
|
+
|
|
42
|
+
<ol>
|
|
43
|
+
<li>Obtener un token largo (96 caracteres) mediante validación por SMS.</li>
|
|
44
|
+
<li>Usar ese token para hacer login y obtener un JWT de sesión, que se usa para todas las comunicaciones.</li>
|
|
45
|
+
</ol>
|
|
46
|
+
|
|
47
|
+
<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>
|
|
48
|
+
|
|
49
|
+
<h3>🔹 Flujo para primera vez (SMS)</h3>
|
|
50
|
+
|
|
51
|
+
<pre><code>from todus import ToDusClient2
|
|
52
|
+
|
|
53
|
+
client = ToDusClient2(phone_number="535xxxxxxx") # sin password aún
|
|
54
|
+
|
|
55
|
+
# 1. Pedir código SMS
|
|
56
|
+
client.request_code() # te llega un PIN de 6 dígitos
|
|
57
|
+
|
|
58
|
+
# 2. Validar el código
|
|
59
|
+
client.validate_code("123456")
|
|
60
|
+
# Ahora client.password contiene el token largo (96 caracteres)
|
|
61
|
+
# ¡Guárdalo en un lugar seguro para futuras sesiones!
|
|
62
|
+
|
|
63
|
+
# 3. Obtener el JWT de sesión
|
|
64
|
+
client.login() # ahora client.logged == True</code></pre>
|
|
65
|
+
|
|
66
|
+
<h3>🔹 Si ya tienes el token largo (de una sesión anterior)</h3>
|
|
67
|
+
|
|
68
|
+
<pre><code>client = ToDusClient2(phone_number="535xxxxxxx", password="ese_token_largo_que_guardaste")
|
|
69
|
+
client.login() # obtiene el JWT internamente</code></pre>
|
|
70
|
+
|
|
71
|
+
<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>
|
|
72
|
+
|
|
73
|
+
<hr>
|
|
74
|
+
|
|
75
|
+
<h2>🚀 Uso Rápido</h2>
|
|
76
|
+
|
|
77
|
+
<h3>Enviar mensajes (privados y grupos automáticamente)</h3>
|
|
78
|
+
|
|
79
|
+
<pre><code># Asumiendo que ya hiciste login
|
|
80
|
+
client.send_message("535yyyyyyy", "¡Hola mundo!") # privado
|
|
81
|
+
client.send_message("mi-grupo-id", "Hola grupo!") # grupo (auto-detectado)</code></pre>
|
|
82
|
+
|
|
83
|
+
<h3>Enviar imagen (con subida previa)</h3>
|
|
84
|
+
|
|
85
|
+
<pre><code>from todus import FileType
|
|
86
|
+
|
|
87
|
+
# 1. Subir la imagen
|
|
88
|
+
with open("foto.jpg", "rb") as f:
|
|
89
|
+
image_data = f.read()
|
|
90
|
+
url = client.upload_file(image_data, FileType.PICTURE)
|
|
91
|
+
|
|
92
|
+
# 2. Enviar el mensaje con la imagen
|
|
93
|
+
client.send_image_message(
|
|
94
|
+
"535yyyyyyy",
|
|
95
|
+
url=url,
|
|
96
|
+
file_name="foto.jpg",
|
|
97
|
+
file_size=len(image_data),
|
|
98
|
+
caption="Mi foto"
|
|
99
|
+
)</code></pre>
|
|
100
|
+
|
|
101
|
+
<h3>Escuchar mensajes entrantes</h3>
|
|
102
|
+
|
|
103
|
+
<pre><code>def on_message(msg):
|
|
104
|
+
if msg.get("body"):
|
|
105
|
+
print(f"{msg['from']}: {msg['body']}")
|
|
106
|
+
|
|
107
|
+
client.listen_messages(on_message) # bucle infinito</code></pre>
|
|
108
|
+
|
|
109
|
+
<hr>
|
|
110
|
+
|
|
111
|
+
<h2>🤖 Bot de Ejemplo</h2>
|
|
112
|
+
|
|
113
|
+
<p>En la carpeta <code>examples/</code> encontrarás un bot funcional con comandos:</p>
|
|
114
|
+
|
|
115
|
+
<table>
|
|
116
|
+
<thead>
|
|
117
|
+
<tr>
|
|
118
|
+
<th>Comando</th>
|
|
119
|
+
<th>Respuesta</th>
|
|
120
|
+
</tr>
|
|
121
|
+
</thead>
|
|
122
|
+
<tbody>
|
|
123
|
+
<tr><td><code>/start</code></td><td>Mensaje de bienvenida con lista de comandos</td></tr>
|
|
124
|
+
<tr><td><code>/info</code></td><td>Información sobre la librería</td></tr>
|
|
125
|
+
<tr><td><code>/ping</code></td><td><code>pong</code></td></tr>
|
|
126
|
+
</tbody>
|
|
127
|
+
</table>
|
|
128
|
+
|
|
129
|
+
<p>Ejecútalo con:</p>
|
|
130
|
+
|
|
131
|
+
<pre><code>export TODUS_PHONE=535xxxxxxx
|
|
132
|
+
export TODUS_AUTH_TOKEN=token_largo_de_96_caracteres
|
|
133
|
+
python examples/bot.py</code></pre>
|
|
134
|
+
|
|
135
|
+
<hr>
|
|
136
|
+
|
|
137
|
+
<h2>📡 Tipos de Mensaje Soportados</h2>
|
|
138
|
+
|
|
139
|
+
<p>La librería soporta los siguientes tipos de mensaje:</p>
|
|
140
|
+
|
|
141
|
+
<table>
|
|
142
|
+
<thead>
|
|
143
|
+
<tr>
|
|
144
|
+
<th>Tipo</th>
|
|
145
|
+
<th>Método (ToDusClient2)</th>
|
|
146
|
+
</tr>
|
|
147
|
+
</thead>
|
|
148
|
+
<tbody>
|
|
149
|
+
<tr><td>Texto</td><td><code>send_message(to, body)</code></td></tr>
|
|
150
|
+
<tr><td>Imagen</td><td><code>send_image_message(to, url, file_name, file_size, ...)</code></td></tr>
|
|
151
|
+
<tr><td>Video</td><td><code>send_video_message(to, url, video_id, file_name, ...)</code></td></tr>
|
|
152
|
+
<tr><td>Archivo</td><td><code>send_file_message(to, url, file_type, ...)</code></td></tr>
|
|
153
|
+
<tr><td>Sticker</td><td><code>send_sticker_message(to, sticker_id, ...)</code></td></tr>
|
|
154
|
+
<tr><td>Contacto</td><td><code>send_contact_message(to, contact_id, ...)</code></td></tr>
|
|
155
|
+
<tr><td>Botones</td><td><code>send_button_message(to, text, buttons)</code></td></tr>
|
|
156
|
+
<tr><td>Ubicación</td><td><code>send_location_message(to, lat, lon, ...)</code></td></tr>
|
|
157
|
+
<tr><td>Evento</td><td><code>send_event_message(to, title, start, end, ...)</code></td></tr>
|
|
158
|
+
<tr><td>Editar</td><td><code>edit_message(to, new_body, original_msg_id)</code></td></tr>
|
|
159
|
+
<tr><td>Eliminar</td><td><code>delete_message(to, message_id)</code></td></tr>
|
|
160
|
+
</tbody>
|
|
161
|
+
</table>
|
|
162
|
+
|
|
163
|
+
<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>
|
|
164
|
+
|
|
165
|
+
<hr>
|
|
166
|
+
|
|
167
|
+
<h2>👥 Grupos MUC Light</h2>
|
|
168
|
+
|
|
169
|
+
<pre><code># Unirse a un grupo
|
|
170
|
+
client.groups.join("mi-grupo-id")
|
|
171
|
+
|
|
172
|
+
# Enviar mensaje al grupo (auto-detectado)
|
|
173
|
+
client.send_message("mi-grupo-id", "Hola grupo!")
|
|
174
|
+
|
|
175
|
+
# Callback específico para mensajes de ese grupo
|
|
176
|
+
def on_group_msg(msg):
|
|
177
|
+
print(f"{msg['sender_phone']}: {msg['body']}")
|
|
178
|
+
|
|
179
|
+
client.groups.on_group_message("mi-grupo-id", on_group_msg)
|
|
180
|
+
|
|
181
|
+
# Salir del grupo
|
|
182
|
+
client.groups.leave("mi-grupo-id")</code></pre>
|
|
183
|
+
|
|
184
|
+
<hr>
|
|
185
|
+
|
|
186
|
+
<h2>📤 Subir y Descargar Archivos</h2>
|
|
187
|
+
|
|
188
|
+
<pre><code># Subir cualquier archivo
|
|
189
|
+
with open("documento.pdf", "rb") as f:
|
|
190
|
+
url = client.upload_file(f.read(), FileType.FILE)
|
|
191
|
+
|
|
192
|
+
# Descargar a una carpeta
|
|
193
|
+
size, path = client.download_file_to_folder(url, "./descargas")
|
|
194
|
+
print(f"Descargado {size} bytes en {path}")</code></pre>
|
|
195
|
+
|
|
196
|
+
<hr>
|
|
197
|
+
|
|
198
|
+
<h2>⚠️ Excepciones</h2>
|
|
199
|
+
|
|
200
|
+
<table>
|
|
201
|
+
<thead>
|
|
202
|
+
<tr>
|
|
203
|
+
<th>Excepción</th>
|
|
204
|
+
<th>Cuándo ocurre</th>
|
|
205
|
+
</tr>
|
|
206
|
+
</thead>
|
|
207
|
+
<tbody>
|
|
208
|
+
<tr><td><code>AuthenticationError</code></td><td>Credenciales inválidas o falta autenticación</td></tr>
|
|
209
|
+
<tr><td><code>TokenExpiredError</code></td><td>El token JWT expiró (el cliente lo renueva automáticamente si usas ToDusClient2)</td></tr>
|
|
210
|
+
<tr><td><code>ConnectionLostError</code></td><td>Se perdió la conexión XMPP</td></tr>
|
|
211
|
+
<tr><td><code>UploadError</code></td><td>Error al subir/descargar archivo</td></tr>
|
|
212
|
+
<tr><td><code>GroupError</code></td><td>Error en operación de grupo</td></tr>
|
|
213
|
+
<tr><td><code>ParseError</code></td><td>Stanza malformada</td></tr>
|
|
214
|
+
</tbody>
|
|
215
|
+
</table>
|
|
216
|
+
|
|
217
|
+
<hr>
|
|
218
|
+
|
|
219
|
+
<h2>🗂️ Estructura del Proyecto</h2>
|
|
220
|
+
|
|
221
|
+
<pre><code>toDus-API/
|
|
222
|
+
├── todus/ # Paquete principal
|
|
223
|
+
│ ├── __init__.py # Exports y versión
|
|
224
|
+
│ ├── client/ # Cliente XMPP/HTTP
|
|
225
|
+
│ │ ├── __init__.py # ToDusClient y ToDusClient2
|
|
226
|
+
│ │ ├── base.py # Conexión y transporte
|
|
227
|
+
│ │ ├── auth.py # Autenticación SMS/JWT
|
|
228
|
+
│ │ ├── message.py # Envío/recepción de mensajes
|
|
229
|
+
│ │ ├── file.py # Subida/descarga de archivos
|
|
230
|
+
│ │ └── profile.py # Perfil de usuario
|
|
231
|
+
│ ├── stanzas/ # Generadores de stanzas XML
|
|
232
|
+
│ │ ├── __init__.py
|
|
233
|
+
│ │ ├── private.py # Chat privado
|
|
234
|
+
│ │ ├── group.py # Chat grupal
|
|
235
|
+
│ │ ├── presence.py # Presencia XMPP
|
|
236
|
+
│ │ └── utils.py # Utilidades de protocolo
|
|
237
|
+
│ ├── group.py # Cliente de grupos MUC Light
|
|
238
|
+
│ ├── parser.py # Parser incremental de stanzas
|
|
239
|
+
│ ├── stanza.py # Re-exports unificados
|
|
240
|
+
│ ├── types.py # Enums (FileType, ChatState, etc.)
|
|
241
|
+
│ ├── util.py # Utilidades (JID, XML, JWT)
|
|
242
|
+
│ ├── constants.py # Hosts, puertos, versiones
|
|
243
|
+
│ └── errors.py # Excepciones personalizadas
|
|
244
|
+
├── tests/ # Tests unitarios
|
|
245
|
+
│ ├── test_util.py
|
|
246
|
+
│ ├── test_types.py
|
|
247
|
+
│ └── test_stanzas.py
|
|
248
|
+
├── examples/ # Ejemplos de uso
|
|
249
|
+
│ └── bot.py
|
|
250
|
+
├── .github/workflows/ # CI/CD
|
|
251
|
+
│ ├── ci.yml # Tests en push/PR
|
|
252
|
+
│ └── pypi-publish.yml # Publicación a PyPI
|
|
253
|
+
├── pyproject.toml # Configuración del paquete
|
|
254
|
+
├── LICENSE # Licencia MIT
|
|
255
|
+
├── CHANGELOG.md # Registro de cambios
|
|
256
|
+
├── CONTRIBUTING.md # Guía para contribuir
|
|
257
|
+
├── MANIFEST.in # Archivos incluidos en sdist
|
|
258
|
+
└── README.md # Este archivo</code></pre>
|
|
259
|
+
|
|
260
|
+
<hr>
|
|
261
|
+
|
|
262
|
+
<h2>🔗 Recursos</h2>
|
|
263
|
+
|
|
264
|
+
- **ToDus oficial:** [ToDus](https://web.todus.cu)
|
|
265
|
+
- **Apklis:** [Apklis](https://www.apklis.cu/application/cu.todus.android)
|
|
266
|
+
- **PyPI:** [toDus-API](https://pypi.org/project/toDus-API/)
|
|
267
|
+
- **Changelog:** [CHANGELOG.md](CHANGELOG.md)
|
|
268
|
+
- **Contribuir:** [CONTRIBUTING.md](CONTRIBUTING.md)
|