whatsapp-toolkit 1.0.6__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.
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.3
2
+ Name: whatsapp-toolkit
3
+ Version: 1.0.6
4
+ Summary: Una biblioteca para interactuar con la API de WhatsApp desde Python usando Evolution API
5
+ Author: Fernando Leon Franco
6
+ Author-email: Fernando Leon Franco <fernanlee2131@gmail.com>
7
+ License: MIT
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Dist: colorstreak>=2.1.0
16
+ Requires-Dist: dotenv>=0.9.9
17
+ Requires-Dist: requests>=2.32.5
18
+ Requires-Python: >=3.10
19
+ Project-URL: Homepage, https://github.com/epok200/whatsapp_toolkit
20
+ Project-URL: Issues, https://github.com/epok200/whatsapp_toolkit/issues
21
+ Project-URL: Repository, https://github.com/epok200/whatsapp_toolkit
22
+ Description-Content-Type: text/markdown
23
+
24
+
25
+ # Whatsapp Toolkit
26
+
27
+ Este módulo permite el envío de mensajes, archivos y multimedia por WhatsApp, con manejo avanzado de conexión, errores y administración de instancias.
28
+
29
+
30
+ ## Componentes principales
31
+
32
+ - **whatsapp.py**: Cliente principal para WhatsApp, incluye administración de instancias, conexión QR, envío de mensajes, archivos, multimedia, ubicación, audio y stickers.
33
+ - **whatsapp_instanced.py**: Utilidades avanzadas para instancias y flujos personalizados, incluyendo envío asíncrono de mensajes y archivos usando Celery.
34
+
35
+ ---
36
+
37
+ ## Ejemplos y recomendaciones de uso
38
+
39
+ ### 1. Inicialización y conexión
40
+ Conecta tu cliente y asegura la conexión antes de enviar mensajes.
41
+ ```python
42
+ from epok_toolkit.messaging.whatsapp import WhatsappClient
43
+
44
+ client = WhatsappClient(api_key="tu_api_key", server_url="https://api.whatsapp.com", instance_name="EPOK")
45
+ client.ensure_connected() # Muestra QR y enlaza la instancia si es necesario
46
+ ```
47
+ **Tip:** El método `ensure_connected` reintenta y muestra QR hasta enlazar la instancia.
48
+
49
+ ### 2. Enviar mensajes de texto
50
+ ```python
51
+ client.send_text(number="521234567890", text="Hola desde EPOK Toolkit!")
52
+ ```
53
+
54
+ ### 3. Enviar archivos y multimedia
55
+ Envía documentos, imágenes, stickers, audio y ubicación:
56
+ ```python
57
+ # Enviar PDF
58
+ with open("ticket.pdf", "rb") as f:
59
+ import base64
60
+ pdf_b64 = base64.b64encode(f.read()).decode()
61
+ client.send_media(number="521234567890", media_b64=pdf_b64, filename="ticket.pdf", caption="Tu ticket", mediatype="document", mimetype="application/pdf")
62
+
63
+ # Enviar sticker
64
+ client.send_sticker(number="521234567890", sticker_b64=sticker_b64)
65
+
66
+ # Enviar ubicación
67
+ client.send_location(number="521234567890", name="EPOK", address="Calle 123", latitude=21.123, longitude=-101.456)
68
+
69
+ # Enviar audio
70
+ client.send_audio(number="521234567890", audio_b64=audio_b64)
71
+ ```
72
+
73
+ ### 4. Administración de instancias y grupos
74
+ ```python
75
+ client.create_instance() # Crea una nueva instancia en el servidor
76
+ client.delete_instance() # Elimina la instancia
77
+ client.fetch_groups() # Obtiene todos los grupos y participantes
78
+ ```
79
+
80
+
81
+ ### 6. Envío asíncrono con whatsapp_instanced.py
82
+ Envía mensajes y archivos en segundo plano usando Celery:
83
+ ```python
84
+ from epok_toolkit.messaging.whatsapp_instanced import send_text, send_media
85
+
86
+ # Enviar mensaje de texto de forma asíncrona
87
+ send_text(number="521234567890", message="Hola desde EPOK Toolkit!")
88
+
89
+ # Enviar archivo PDF de forma asíncrona
90
+ send_media(number="521234567890", media_b64=pdf_b64, filename="ticket.pdf", caption="Tu ticket")
91
+ ```
92
+ **Tip:** Configura Celery y los settings de API_KEY, INSTANCE y SERVER_URL para habilitar el envío asíncrono.
93
+
94
+ ---
95
+
96
+ ## Más información
97
+ Consulta la documentación de cada archivo para detalles avanzados, recomendaciones y ejemplos específicos.
98
+
99
+ ## Instalación
100
+
101
+ Con UV Package Manager:
102
+ ```bash
103
+ uv add whatsapp-toolkit
104
+ ```
105
+
106
+ Con pip:
107
+ ```bash
108
+ pip install whatsapp-toolkit
109
+ ```
110
+ ## Requisitos
111
+ - Python 3.10 o superior
112
+ - requests >=2.32.5
113
+
@@ -0,0 +1,90 @@
1
+
2
+ # Whatsapp Toolkit
3
+
4
+ Este módulo permite el envío de mensajes, archivos y multimedia por WhatsApp, con manejo avanzado de conexión, errores y administración de instancias.
5
+
6
+
7
+ ## Componentes principales
8
+
9
+ - **whatsapp.py**: Cliente principal para WhatsApp, incluye administración de instancias, conexión QR, envío de mensajes, archivos, multimedia, ubicación, audio y stickers.
10
+ - **whatsapp_instanced.py**: Utilidades avanzadas para instancias y flujos personalizados, incluyendo envío asíncrono de mensajes y archivos usando Celery.
11
+
12
+ ---
13
+
14
+ ## Ejemplos y recomendaciones de uso
15
+
16
+ ### 1. Inicialización y conexión
17
+ Conecta tu cliente y asegura la conexión antes de enviar mensajes.
18
+ ```python
19
+ from epok_toolkit.messaging.whatsapp import WhatsappClient
20
+
21
+ client = WhatsappClient(api_key="tu_api_key", server_url="https://api.whatsapp.com", instance_name="EPOK")
22
+ client.ensure_connected() # Muestra QR y enlaza la instancia si es necesario
23
+ ```
24
+ **Tip:** El método `ensure_connected` reintenta y muestra QR hasta enlazar la instancia.
25
+
26
+ ### 2. Enviar mensajes de texto
27
+ ```python
28
+ client.send_text(number="521234567890", text="Hola desde EPOK Toolkit!")
29
+ ```
30
+
31
+ ### 3. Enviar archivos y multimedia
32
+ Envía documentos, imágenes, stickers, audio y ubicación:
33
+ ```python
34
+ # Enviar PDF
35
+ with open("ticket.pdf", "rb") as f:
36
+ import base64
37
+ pdf_b64 = base64.b64encode(f.read()).decode()
38
+ client.send_media(number="521234567890", media_b64=pdf_b64, filename="ticket.pdf", caption="Tu ticket", mediatype="document", mimetype="application/pdf")
39
+
40
+ # Enviar sticker
41
+ client.send_sticker(number="521234567890", sticker_b64=sticker_b64)
42
+
43
+ # Enviar ubicación
44
+ client.send_location(number="521234567890", name="EPOK", address="Calle 123", latitude=21.123, longitude=-101.456)
45
+
46
+ # Enviar audio
47
+ client.send_audio(number="521234567890", audio_b64=audio_b64)
48
+ ```
49
+
50
+ ### 4. Administración de instancias y grupos
51
+ ```python
52
+ client.create_instance() # Crea una nueva instancia en el servidor
53
+ client.delete_instance() # Elimina la instancia
54
+ client.fetch_groups() # Obtiene todos los grupos y participantes
55
+ ```
56
+
57
+
58
+ ### 6. Envío asíncrono con whatsapp_instanced.py
59
+ Envía mensajes y archivos en segundo plano usando Celery:
60
+ ```python
61
+ from epok_toolkit.messaging.whatsapp_instanced import send_text, send_media
62
+
63
+ # Enviar mensaje de texto de forma asíncrona
64
+ send_text(number="521234567890", message="Hola desde EPOK Toolkit!")
65
+
66
+ # Enviar archivo PDF de forma asíncrona
67
+ send_media(number="521234567890", media_b64=pdf_b64, filename="ticket.pdf", caption="Tu ticket")
68
+ ```
69
+ **Tip:** Configura Celery y los settings de API_KEY, INSTANCE y SERVER_URL para habilitar el envío asíncrono.
70
+
71
+ ---
72
+
73
+ ## Más información
74
+ Consulta la documentación de cada archivo para detalles avanzados, recomendaciones y ejemplos específicos.
75
+
76
+ ## Instalación
77
+
78
+ Con UV Package Manager:
79
+ ```bash
80
+ uv add whatsapp-toolkit
81
+ ```
82
+
83
+ Con pip:
84
+ ```bash
85
+ pip install whatsapp-toolkit
86
+ ```
87
+ ## Requisitos
88
+ - Python 3.10 o superior
89
+ - requests >=2.32.5
90
+
@@ -0,0 +1,34 @@
1
+ [project]
2
+ name = "whatsapp-toolkit"
3
+ version = "1.0.6"
4
+ description = "Una biblioteca para interactuar con la API de WhatsApp desde Python usando Evolution API"
5
+ authors = [
6
+ { name="Fernando Leon Franco", email="fernanlee2131@gmail.com"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.10"
10
+ license = { text = "MIT" }
11
+ dependencies = [
12
+ "colorstreak>=2.1.0",
13
+ "dotenv>=0.9.9",
14
+ "requests>=2.32.5",
15
+ ]
16
+
17
+ classifiers = [
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
24
+ "Operating System :: OS Independent",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/epok200/whatsapp_toolkit"
29
+ Repository = "https://github.com/epok200/whatsapp_toolkit"
30
+ Issues = "https://github.com/epok200/whatsapp_toolkit/issues"
31
+
32
+ [build-system]
33
+ requires = ["uv_build>=0.9.17,<0.10.0"]
34
+ build-backend = "uv_build"
@@ -0,0 +1,2 @@
1
+ from .whatsapp import WhatsappClient
2
+ from .whatsapp_instanced import send_media, send_message
@@ -0,0 +1,383 @@
1
+ import requests
2
+ from typing import Optional
3
+ from functools import wraps
4
+ from dataclasses import dataclass
5
+
6
+
7
+ def timeout_response(func):
8
+ @wraps(func)
9
+ def wrapper(*args, **kwargs):
10
+ try:
11
+ return func(*args, **kwargs)
12
+ except requests.Timeout:
13
+ print("La solicitud ha excedido el tiempo de espera.")
14
+ return HttpResponse(status_code=408, text="Timeout", json_data=None)
15
+ except requests.RequestException as e:
16
+ print(f"Error en la solicitud: {e}")
17
+ return HttpResponse(
18
+ status_code=500, text="Error", json_data={"error": str(e)}
19
+ )
20
+
21
+ return wrapper
22
+
23
+
24
+ def require_connection(method):
25
+ """
26
+ Decorador para métodos de WhatsappClient que necesitan una conexión activa.
27
+ Llama a `self.ensure_connected()` y solo ejecuta el método original si la
28
+ conexión se confirma; de lo contrario devuelve False.
29
+ """
30
+ from functools import wraps
31
+
32
+ @wraps(method)
33
+ def _wrapper(self, *args, **kwargs):
34
+ if not self.ensure_connected():
35
+ print("❌ No fue posible establecer conexión.")
36
+ return False
37
+ return method(self, *args, **kwargs)
38
+
39
+ return _wrapper
40
+
41
+
42
+ @dataclass
43
+ class HttpResponse:
44
+ status_code: int
45
+ text: str
46
+ json_data: Optional[dict] = None
47
+
48
+
49
+ class WhatsAppInstance:
50
+ def __init__(self, api_key: str, instance: str, server_url: str):
51
+ self.api_key = api_key
52
+ self.name_instance = instance
53
+ self.status = "disconnected"
54
+ self.server_url = server_url.rstrip("/")
55
+ self.headers = {"apikey": self.api_key, "Content-Type": "application/json"}
56
+
57
+ def create_instance(self) -> HttpResponse:
58
+ """Crea una nueva instancia de WhatsApp usando la API de Envole."""
59
+ url = f"{self.server_url}/instance/create"
60
+ payload = {
61
+ "instanceName": self.name_instance,
62
+ "integration": "WHATSAPP-BAILEYS",
63
+ "syncFullHistory": False,
64
+ }
65
+ response = requests.post(url, json=payload, headers=self.headers)
66
+ return HttpResponse(response.status_code, response.text, response.json())
67
+
68
+ def delete_instance(self) -> HttpResponse:
69
+ """Elimina una instancia de WhatsApp usando la API de Envole."""
70
+ url = f"{self.server_url}/instance/delete/{self.name_instance}"
71
+ response = requests.delete(url, headers=self.headers)
72
+ return HttpResponse(response.status_code, response.text)
73
+
74
+ def show_qr(self, qr_text: str) -> None:
75
+ """Genera un código QR a partir de `qr_text` y lo muestra con el visor por defecto."""
76
+ import qrcode
77
+
78
+ qr = qrcode.QRCode(border=2)
79
+ qr.add_data(qr_text)
80
+ qr.make(fit=True)
81
+ img = qr.make_image()
82
+ img.show()
83
+
84
+ def connect_instance_qr(self) -> None:
85
+ """Conecta una instancia de WhatsApp y muestra una imagen"""
86
+ url = f"{self.server_url}/instance/connect/{self.name_instance}"
87
+ response = requests.get(url, headers=self.headers)
88
+ codigo = response.json().get("code")
89
+ self.show_qr(codigo)
90
+
91
+ def mode_connecting(self):
92
+ """
93
+ Se intentará por 30 min el mantenter intentos de conexión a la instancia
94
+ generando un qr cada 10 segundos, si es exitoso se podra enviar un mensaje,
95
+ si después de eso no se conecta, se devolvera un error
96
+ """
97
+ pass
98
+
99
+
100
+ class WhatsAppSender:
101
+ def __init__(self, instance: WhatsAppInstance):
102
+ self.instance = instance.name_instance
103
+ self.server_url = instance.server_url
104
+ self.headers = instance.headers
105
+ self._instance_obj = instance
106
+ self.connected = True # estado de conexión conocido
107
+
108
+ def test_connection_status(self) -> bool:
109
+ cel_epok = "5214778966517"
110
+ print(f"Probando conexión enviando mensaje a {cel_epok}...")
111
+ ok = bool(self.send_text(cel_epok, "ping"))
112
+ self.connected = ok
113
+ return ok
114
+
115
+ @timeout_response
116
+ def get(self, endpoint: str, params: Optional[dict] = None) -> requests.Response:
117
+ url = f"{self.server_url}{endpoint}"
118
+ return requests.get(url, headers=self.headers, params=params)
119
+
120
+ def put(self, endpoint: str) -> requests.Response:
121
+ url = f"{self.server_url}{endpoint}"
122
+ return requests.put(url, headers=self.headers)
123
+
124
+ def post(self, endpoint: str, payload: dict):
125
+ url = f"{self.server_url}{endpoint}"
126
+ request = requests.post(url, json=payload, headers=self.headers, timeout=10)
127
+ # if timeout:
128
+ try:
129
+ return request
130
+ except requests.Timeout:
131
+ print("Request timed out")
132
+ return HttpResponse(status_code=408, text="Timeout", json_data=None)
133
+
134
+ def send_text(self, number: str, text: str, link_preview: bool = True, delay_ms: int = 0) -> str:
135
+ payload = {
136
+ "number": number,
137
+ "text": text,
138
+ "delay": delay_ms,
139
+ "linkPreview": link_preview,
140
+ }
141
+ print(f"Enviando mensaje a {number}: {text}")
142
+ resp = self.post(f"/message/sendText/{self.instance}", payload)
143
+
144
+ # Si la solicitud se convirtió en HttpResponse por timeout
145
+ status = resp.status_code if hasattr(resp, "status_code") else 0
146
+
147
+ if 200 <= status < 300:
148
+ self.connected = True
149
+ return resp.text
150
+
151
+ # Fallo: marcar desconexión y reportar
152
+ print(f"Error al enviar mensaje a {number}: {status} - {resp.text}")
153
+ self.connected = False
154
+ return False
155
+
156
+ def send_media(self, number: str, media_b64: str, filename: str, caption: str, mediatype: str = "document", mimetype: str = "application/pdf") -> str:
157
+ payload = {
158
+ "number": number,
159
+ "mediatype": mediatype,
160
+ "mimetype": mimetype,
161
+ "caption": caption,
162
+ "media": media_b64,
163
+ "fileName": filename,
164
+ "delay": 0,
165
+ "linkPreview": False,
166
+ "mentionsEveryOne": False,
167
+ }
168
+ resp = self.post(f"/message/sendMedia/{self.instance}", payload)
169
+ return resp.text
170
+
171
+ def send_sticker(self, number: str, sticker_b64: str, delay: int = 0, link_preview: bool = True, mentions_everyone: bool = True) -> str:
172
+ """Envía un sticker a un contacto específico."""
173
+ payload = {
174
+ "number": number,
175
+ "sticker": sticker_b64,
176
+ "delay": delay,
177
+ "linkPreview": link_preview,
178
+ "mentionsEveryOne": mentions_everyone,
179
+ }
180
+ resp = self.post(f"/message/sendSticker/{self.instance}", payload)
181
+ return resp.text
182
+
183
+ def send_location(self, number: str, name: str, address: str, latitude: float, longitude: float, delay: int = 0) -> str:
184
+ """Envía una ubicación a un contacto."""
185
+ payload = {
186
+ "number": number,
187
+ "name": name,
188
+ "address": address,
189
+ "latitude": latitude,
190
+ "longitude": longitude,
191
+ "delay": delay,
192
+ }
193
+ resp = self.post(f"/message/sendLocation/{self.instance}", payload)
194
+ return resp.text
195
+
196
+ def send_audio(self, number: str, audio_b64: str, delay: int = 0) -> str:
197
+ """Envía un audio en formato base64 a un contacto."""
198
+ payload = {
199
+ "audio": audio_b64,
200
+ "number": number,
201
+ "delay": delay,
202
+ }
203
+ resp = self.post(f"/message/sendWhatsAppAudio/{self.instance}", payload)
204
+ return resp.text
205
+
206
+ def connect(self, number: str) -> str:
207
+ querystring = {"number": number}
208
+ resp = self.get(f"/instance/connect/{self.instance}", params=querystring)
209
+ return resp.text
210
+
211
+ def set_webhook(self, webhook_url: str, enabled: bool = True, webhook_by_events: bool = True, webhook_base64: bool = True, events: Optional[list] = None) -> str:
212
+ """Configura el webhook para la instancia."""
213
+ if events is None:
214
+ events = ["SEND_MESSAGE"]
215
+ payload = {
216
+ "url": webhook_url,
217
+ "enabled": enabled,
218
+ "webhookByEvents": webhook_by_events,
219
+ "webhookBase64": webhook_base64,
220
+ "events": events,
221
+ }
222
+ resp = self.post(f"/webhook/set/{self.instance}", payload)
223
+ return resp.text
224
+
225
+ def fetch_groups(self, get_participants: bool = True) -> list:
226
+ """Obtiene todos los grupos y sus participantes."""
227
+ params = {"getParticipants": str(get_participants).lower()}
228
+ resp = self.get(f"/group/fetchAllGroups/{self.instance}", params=params)
229
+ if resp.status_code == 200:
230
+ return resp.json()
231
+ else:
232
+ raise Exception(
233
+ f"Error al obtener grupos: {resp.status_code} - {resp.text}"
234
+ )
235
+
236
+ @staticmethod
237
+ def fetch_instances(api_key: str, server_url: str) -> list:
238
+ """Obtiene todas las instancias disponibles en el servidor."""
239
+ url = f"{server_url}/instance/fetchInstances"
240
+ headers = {"apikey": api_key}
241
+ response = requests.get(url, headers=headers, verify=False)
242
+ # Puede ser una lista o dict, depende del backend
243
+ try:
244
+ return response.json()
245
+ except Exception:
246
+ return []
247
+
248
+ @staticmethod
249
+ def get_instance_info(api_key: str, instance_name: str, server_url: str):
250
+ """Busca la info de una instancia específica por nombre, robusto a diferentes formatos de respuesta."""
251
+ instances = WhatsAppSender.fetch_instances(api_key, server_url)
252
+
253
+ # Normalizar a lista para iterar
254
+ if isinstance(instances, dict):
255
+ instances = [instances]
256
+ # print(f"Buscando instancia: {instance_name} en {len(instances)} instancias disponibles.")
257
+ for item in instances:
258
+ data = (
259
+ item.get("instance")
260
+ if isinstance(item, dict) and "instance" in item
261
+ else item
262
+ )
263
+ # print(data)
264
+ if not isinstance(data, dict):
265
+ continue # Formato inesperado para us
266
+
267
+ if data.get("name") == instance_name:
268
+ return data
269
+ return {}
270
+
271
+
272
+
273
+ class WhatsappClient:
274
+ """
275
+ Cliente para interactuar con la API de WhatsApp.
276
+ """
277
+ def __init__(self, api_key: str, server_url: str, instance_name: str = "EPOK"):
278
+ self.instance = WhatsAppInstance(api_key, instance_name, server_url)
279
+ self.sender: Optional[WhatsAppSender] = None
280
+ self._auto_initialize_sender()
281
+
282
+ def _auto_initialize_sender(self):
283
+ """Solo asigna sender si la instancia está enlazada a WhatsApp."""
284
+ info = WhatsAppSender.get_instance_info(
285
+ self.instance.api_key, self.instance.name_instance, self.instance.server_url
286
+ )
287
+ if info.get("ownerJid"): # <- si tiene owner, significa que ya está enlazada
288
+ self.sender = WhatsAppSender(self.instance)
289
+
290
+ def ensure_connected(self, retries: int = 3, delay: int = 30) -> bool:
291
+ """
292
+ Garantiza que la instancia esté conectada.
293
+ Si aún no existe `self.sender`, intentará crearlo.
294
+ Si la prueba de conexión falla, muestra un QR y reintenta.
295
+ """
296
+ import time
297
+
298
+ # Si ya tenemos sender y está marcado como conectado, salimos rápido
299
+ if self.sender and getattr(self.sender, "connected", False):
300
+ return True
301
+
302
+ def _init_sender():
303
+ if self.sender is None:
304
+ # Intentar inicializar si la instancia ya está enlazada
305
+ info = WhatsAppSender.get_instance_info(
306
+ self.instance.api_key,
307
+ self.instance.name_instance,
308
+ self.instance.server_url,
309
+ )
310
+ if info.get("ownerJid"):
311
+ self.sender = WhatsAppSender(self.instance)
312
+
313
+ # Primer intento de inicializar el sender
314
+ _init_sender()
315
+
316
+ for attempt in range(1, retries + 1):
317
+ if self.sender and self.sender.test_connection_status():
318
+ return True
319
+
320
+ print(
321
+ f"[{attempt}/{retries}] Conexión no disponible, mostrando nuevo QR (espera {delay}s)…"
322
+ )
323
+ self.instance.connect_instance_qr() # muestra nuevo QR
324
+ time.sleep(delay)
325
+
326
+ # Reintentar inicializar sender después de mostrar QR
327
+ _init_sender()
328
+
329
+ print("❌ No fue posible establecer conexión después de varios intentos.")
330
+ return False
331
+
332
+ @require_connection
333
+ def send_text(self, number: str, text: str, link_preview: bool = True, delay_ms: int = 1000):
334
+ """
335
+ Envía un mensaje de texto de WhatsApp a un número específico.
336
+ Args:
337
+ number (str): Número de teléfono de destino en formato internacional (por ejemplo, "+34123456789").
338
+ text (str): Contenido del mensaje de texto a enviar.
339
+ link_preview (bool, optional): Indica si se debe generar vista previa de enlaces incluidos en el mensaje.
340
+ Por defecto es True.
341
+ delay_ms (int, optional): Retraso en milisegundos antes de enviar el mensaje. Por defecto es 1000.
342
+ Returns:
343
+ Any: El resultado devuelto por `self.sender.send_text`, típicamente la respuesta del envío
344
+ proporcionada por la implementación concreta del `sender`.
345
+ Raises:
346
+ AttributeError: Si `self.sender` es None o no implementa el método `send_text`.
347
+ Exception: Cualquier error que pueda producirse durante el envío del mensaje.
348
+ """
349
+
350
+ return self.sender.send_text(number, text, link_preview, delay_ms=delay_ms)
351
+
352
+ @require_connection
353
+ def send_media(self, number: str, media_b64: str, filename: str, caption: str, mediatype: str = "document", mimetype: str = "application/pdf"):
354
+ return self.sender.send_media(number, media_b64, filename, caption, mediatype, mimetype)
355
+
356
+ @require_connection
357
+ def send_sticker(self, number: str, sticker_b64: str, delay: int = 0, link_preview: bool = True, mentions_everyone: bool = True):
358
+ return self.sender.send_sticker(number, sticker_b64, delay, link_preview, mentions_everyone)
359
+
360
+ @require_connection
361
+ def send_location(self, number: str, name: str, address: str, latitude: float, longitude: float, delay: int = 0):
362
+ return self.sender.send_location(number, name, address, latitude, longitude, delay)
363
+
364
+ @require_connection
365
+ def send_audio(self, number: str, audio_b64: str, delay: int = 0):
366
+ return self.sender.send_audio(number, audio_b64, delay)
367
+
368
+ @require_connection
369
+ def connect_number(self, number: str):
370
+ return self.sender.connect(number)
371
+
372
+ @require_connection
373
+ def fetch_groups(self, get_participants: bool = True):
374
+ return self.sender.fetch_groups(get_participants)
375
+
376
+ def create_instance(self):
377
+ return self.instance.create_instance()
378
+
379
+ def delete_instance(self):
380
+ return self.instance.delete_instance()
381
+
382
+ def connect_instance_qr(self):
383
+ return self.instance.connect_instance_qr()
@@ -0,0 +1,25 @@
1
+ from .whatsapp import WhatsappClient
2
+ from dotenv import load_dotenv
3
+ import os
4
+
5
+ load_dotenv()
6
+
7
+
8
+ API_KEY = os.getenv("WHATSAPP_API_KEY")
9
+ # TODO: La instancia deberia ser creada y seteada (no desde variable de entorno)
10
+ INSTANCE = os.getenv("WHATSAPP_INSTANCE")
11
+ SERVER_URL = os.getenv("WHATSAPP_SERVER_URL")
12
+
13
+
14
+ def send_message(number: str, message: str, delay :int):
15
+ client = WhatsappClient(api_key=API_KEY, server_url=SERVER_URL, instance_name=INSTANCE)
16
+ return client.send_text(number, message, delay_ms=delay)
17
+
18
+
19
+
20
+ def send_media(number: str, media_b64: str, filename: str, caption: str, mediatype: str = "document", mimetype: str = "application/pdf"):
21
+ client = WhatsappClient(api_key=API_KEY, server_url=SERVER_URL, instance_name=INSTANCE)
22
+ return client.send_media(number, media_b64, filename, caption, mediatype, mimetype)
23
+
24
+
25
+