wappa 0.1.9__py3-none-any.whl → 0.1.10__py3-none-any.whl
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.
Potentially problematic release.
This version of wappa might be problematic. Click here for more details.
- wappa/__init__.py +4 -5
- wappa/api/controllers/webhook_controller.py +5 -2
- wappa/api/dependencies/__init__.py +0 -5
- wappa/api/middleware/error_handler.py +4 -4
- wappa/api/middleware/owner.py +11 -5
- wappa/api/routes/webhooks.py +2 -2
- wappa/cli/__init__.py +1 -1
- wappa/cli/examples/init/app/main.py +2 -1
- wappa/cli/examples/init/app/master_event.py +5 -3
- wappa/cli/examples/json_cache_example/app/__init__.py +1 -1
- wappa/cli/examples/json_cache_example/app/main.py +56 -44
- wappa/cli/examples/json_cache_example/app/master_event.py +181 -145
- wappa/cli/examples/json_cache_example/app/models/__init__.py +1 -1
- wappa/cli/examples/json_cache_example/app/models/json_demo_models.py +32 -51
- wappa/cli/examples/json_cache_example/app/scores/__init__.py +2 -2
- wappa/cli/examples/json_cache_example/app/scores/score_base.py +52 -46
- wappa/cli/examples/json_cache_example/app/scores/score_cache_statistics.py +70 -62
- wappa/cli/examples/json_cache_example/app/scores/score_message_history.py +41 -44
- wappa/cli/examples/json_cache_example/app/scores/score_state_commands.py +83 -71
- wappa/cli/examples/json_cache_example/app/scores/score_user_management.py +73 -57
- wappa/cli/examples/json_cache_example/app/utils/__init__.py +2 -2
- wappa/cli/examples/json_cache_example/app/utils/cache_utils.py +54 -56
- wappa/cli/examples/json_cache_example/app/utils/message_utils.py +85 -80
- wappa/cli/examples/openai_transcript/app/main.py +2 -1
- wappa/cli/examples/openai_transcript/app/master_event.py +31 -22
- wappa/cli/examples/openai_transcript/app/openai_utils/__init__.py +1 -1
- wappa/cli/examples/openai_transcript/app/openai_utils/audio_processing.py +37 -24
- wappa/cli/examples/redis_cache_example/app/__init__.py +1 -1
- wappa/cli/examples/redis_cache_example/app/main.py +56 -44
- wappa/cli/examples/redis_cache_example/app/master_event.py +181 -145
- wappa/cli/examples/redis_cache_example/app/models/redis_demo_models.py +31 -50
- wappa/cli/examples/redis_cache_example/app/scores/__init__.py +2 -2
- wappa/cli/examples/redis_cache_example/app/scores/score_base.py +52 -46
- wappa/cli/examples/redis_cache_example/app/scores/score_cache_statistics.py +70 -62
- wappa/cli/examples/redis_cache_example/app/scores/score_message_history.py +41 -44
- wappa/cli/examples/redis_cache_example/app/scores/score_state_commands.py +83 -71
- wappa/cli/examples/redis_cache_example/app/scores/score_user_management.py +73 -57
- wappa/cli/examples/redis_cache_example/app/utils/__init__.py +2 -2
- wappa/cli/examples/redis_cache_example/app/utils/cache_utils.py +54 -56
- wappa/cli/examples/redis_cache_example/app/utils/message_utils.py +85 -80
- wappa/cli/examples/simple_echo_example/app/__init__.py +1 -1
- wappa/cli/examples/simple_echo_example/app/main.py +41 -33
- wappa/cli/examples/simple_echo_example/app/master_event.py +78 -57
- wappa/cli/examples/wappa_full_example/app/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/handlers/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/handlers/command_handlers.py +134 -126
- wappa/cli/examples/wappa_full_example/app/handlers/message_handlers.py +237 -229
- wappa/cli/examples/wappa_full_example/app/handlers/state_handlers.py +170 -148
- wappa/cli/examples/wappa_full_example/app/main.py +51 -39
- wappa/cli/examples/wappa_full_example/app/master_event.py +179 -120
- wappa/cli/examples/wappa_full_example/app/models/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/models/state_models.py +113 -104
- wappa/cli/examples/wappa_full_example/app/models/user_models.py +92 -76
- wappa/cli/examples/wappa_full_example/app/models/webhook_metadata.py +109 -83
- wappa/cli/examples/wappa_full_example/app/utils/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/utils/cache_utils.py +132 -113
- wappa/cli/examples/wappa_full_example/app/utils/media_handler.py +175 -132
- wappa/cli/examples/wappa_full_example/app/utils/metadata_extractor.py +126 -87
- wappa/cli/main.py +9 -4
- wappa/core/__init__.py +18 -23
- wappa/core/config/settings.py +7 -5
- wappa/core/events/default_handlers.py +1 -1
- wappa/core/factory/wappa_builder.py +38 -25
- wappa/core/plugins/redis_plugin.py +1 -3
- wappa/core/plugins/wappa_core_plugin.py +7 -6
- wappa/core/types.py +12 -12
- wappa/core/wappa_app.py +10 -8
- wappa/database/__init__.py +3 -4
- wappa/domain/enums/messenger_platform.py +1 -2
- wappa/domain/factories/media_factory.py +5 -20
- wappa/domain/factories/message_factory.py +5 -20
- wappa/domain/factories/messenger_factory.py +2 -4
- wappa/domain/interfaces/cache_interface.py +7 -7
- wappa/domain/interfaces/media_interface.py +2 -5
- wappa/domain/models/media_result.py +1 -3
- wappa/domain/models/platforms/platform_config.py +1 -3
- wappa/messaging/__init__.py +9 -12
- wappa/messaging/whatsapp/handlers/whatsapp_media_handler.py +20 -22
- wappa/models/__init__.py +27 -35
- wappa/persistence/__init__.py +12 -15
- wappa/persistence/cache_factory.py +0 -1
- wappa/persistence/json/__init__.py +1 -1
- wappa/persistence/json/cache_adapters.py +37 -25
- wappa/persistence/json/handlers/state_handler.py +60 -52
- wappa/persistence/json/handlers/table_handler.py +51 -49
- wappa/persistence/json/handlers/user_handler.py +71 -55
- wappa/persistence/json/handlers/utils/file_manager.py +42 -39
- wappa/persistence/json/handlers/utils/key_factory.py +1 -1
- wappa/persistence/json/handlers/utils/serialization.py +13 -11
- wappa/persistence/json/json_cache_factory.py +4 -8
- wappa/persistence/json/storage_manager.py +66 -79
- wappa/persistence/memory/__init__.py +1 -1
- wappa/persistence/memory/cache_adapters.py +37 -25
- wappa/persistence/memory/handlers/state_handler.py +62 -52
- wappa/persistence/memory/handlers/table_handler.py +59 -53
- wappa/persistence/memory/handlers/user_handler.py +75 -55
- wappa/persistence/memory/handlers/utils/key_factory.py +1 -1
- wappa/persistence/memory/handlers/utils/memory_store.py +75 -71
- wappa/persistence/memory/handlers/utils/ttl_manager.py +59 -67
- wappa/persistence/memory/memory_cache_factory.py +3 -7
- wappa/persistence/memory/storage_manager.py +52 -62
- wappa/persistence/redis/cache_adapters.py +27 -21
- wappa/persistence/redis/ops.py +11 -11
- wappa/persistence/redis/redis_client.py +4 -6
- wappa/persistence/redis/redis_manager.py +12 -4
- wappa/processors/factory.py +5 -5
- wappa/schemas/factory.py +2 -5
- wappa/schemas/whatsapp/message_types/errors.py +3 -12
- wappa/schemas/whatsapp/validators.py +3 -3
- wappa/webhooks/__init__.py +17 -18
- wappa/webhooks/factory.py +3 -5
- wappa/webhooks/whatsapp/__init__.py +10 -13
- wappa/webhooks/whatsapp/message_types/audio.py +0 -4
- wappa/webhooks/whatsapp/message_types/document.py +1 -9
- wappa/webhooks/whatsapp/message_types/errors.py +3 -12
- wappa/webhooks/whatsapp/message_types/location.py +1 -21
- wappa/webhooks/whatsapp/message_types/sticker.py +1 -5
- wappa/webhooks/whatsapp/message_types/text.py +0 -6
- wappa/webhooks/whatsapp/message_types/video.py +1 -20
- wappa/webhooks/whatsapp/status_models.py +2 -2
- wappa/webhooks/whatsapp/validators.py +3 -3
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/METADATA +362 -8
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/RECORD +126 -126
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/WHEEL +0 -0
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/entry_points.txt +0 -0
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,61 +5,65 @@ This module provides functions for downloading and uploading media files,
|
|
|
5
5
|
handling different media types, and managing local media storage.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import asyncio
|
|
9
8
|
import os
|
|
10
9
|
import tempfile
|
|
11
10
|
from pathlib import Path
|
|
12
|
-
from typing import Dict, Optional, Tuple
|
|
13
11
|
|
|
14
|
-
import aiofiles
|
|
15
12
|
import aiohttp
|
|
13
|
+
|
|
16
14
|
from wappa.webhooks import IncomingMessageWebhook
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
class MediaHandler:
|
|
20
18
|
"""Utility class for handling media operations."""
|
|
21
|
-
|
|
22
|
-
def __init__(self, temp_dir:
|
|
19
|
+
|
|
20
|
+
def __init__(self, temp_dir: str | None = None):
|
|
23
21
|
"""
|
|
24
22
|
Initialize MediaHandler.
|
|
25
|
-
|
|
23
|
+
|
|
26
24
|
Args:
|
|
27
25
|
temp_dir: Optional temporary directory path for downloads
|
|
28
26
|
"""
|
|
29
27
|
self.temp_dir = temp_dir or tempfile.gettempdir()
|
|
30
|
-
self.session:
|
|
31
|
-
|
|
28
|
+
self.session: aiohttp.ClientSession | None = None
|
|
29
|
+
|
|
32
30
|
async def __aenter__(self):
|
|
33
31
|
"""Async context manager entry."""
|
|
34
32
|
self.session = aiohttp.ClientSession()
|
|
35
33
|
return self
|
|
36
|
-
|
|
34
|
+
|
|
37
35
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
38
36
|
"""Async context manager exit."""
|
|
39
37
|
if self.session:
|
|
40
38
|
await self.session.close()
|
|
41
|
-
|
|
42
|
-
async def get_media_info_from_webhook(
|
|
39
|
+
|
|
40
|
+
async def get_media_info_from_webhook(
|
|
41
|
+
self, webhook: IncomingMessageWebhook
|
|
42
|
+
) -> dict[str, str] | None:
|
|
43
43
|
"""
|
|
44
44
|
Extract media information from webhook.
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
Args:
|
|
47
47
|
webhook: IncomingMessageWebhook containing media
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
Returns:
|
|
50
50
|
Dictionary with media info or None if no media found
|
|
51
51
|
"""
|
|
52
52
|
message = webhook.message
|
|
53
53
|
message_type = webhook.get_message_type_name().lower()
|
|
54
|
-
|
|
55
|
-
if message_type not in [
|
|
54
|
+
|
|
55
|
+
if message_type not in [
|
|
56
|
+
"image",
|
|
57
|
+
"video",
|
|
58
|
+
"audio",
|
|
59
|
+
"voice",
|
|
60
|
+
"document",
|
|
61
|
+
"sticker",
|
|
62
|
+
]:
|
|
56
63
|
return None
|
|
57
|
-
|
|
58
|
-
media_info = {
|
|
59
|
-
|
|
60
|
-
"message_id": message.message_id
|
|
61
|
-
}
|
|
62
|
-
|
|
64
|
+
|
|
65
|
+
media_info = {"type": message_type, "message_id": message.message_id}
|
|
66
|
+
|
|
63
67
|
# Try to extract media ID (different field names possible)
|
|
64
68
|
media_id = None
|
|
65
69
|
for field_name in ["media_id", "id", f"{message_type}_id"]:
|
|
@@ -67,51 +71,53 @@ class MediaHandler:
|
|
|
67
71
|
media_id = getattr(message, field_name)
|
|
68
72
|
if media_id:
|
|
69
73
|
break
|
|
70
|
-
|
|
74
|
+
|
|
71
75
|
if not media_id:
|
|
72
76
|
return None
|
|
73
|
-
|
|
77
|
+
|
|
74
78
|
media_info["media_id"] = media_id
|
|
75
|
-
|
|
79
|
+
|
|
76
80
|
# Extract additional metadata
|
|
77
81
|
if hasattr(message, "mime_type"):
|
|
78
82
|
media_info["mime_type"] = message.mime_type
|
|
79
|
-
|
|
83
|
+
|
|
80
84
|
if hasattr(message, "file_size"):
|
|
81
85
|
media_info["file_size"] = message.file_size
|
|
82
|
-
|
|
86
|
+
|
|
83
87
|
if hasattr(message, "filename"):
|
|
84
88
|
media_info["filename"] = message.filename
|
|
85
|
-
|
|
89
|
+
|
|
86
90
|
if hasattr(message, "caption"):
|
|
87
91
|
media_info["caption"] = message.caption
|
|
88
|
-
|
|
92
|
+
|
|
89
93
|
# For images and videos, get dimensions
|
|
90
94
|
if message_type in ["image", "video"]:
|
|
91
95
|
if hasattr(message, "width"):
|
|
92
96
|
media_info["width"] = message.width
|
|
93
97
|
if hasattr(message, "height"):
|
|
94
98
|
media_info["height"] = message.height
|
|
95
|
-
|
|
99
|
+
|
|
96
100
|
# For audio and video, get duration
|
|
97
101
|
if message_type in ["audio", "video", "voice"]:
|
|
98
102
|
if hasattr(message, "duration"):
|
|
99
103
|
media_info["duration"] = message.duration
|
|
100
|
-
|
|
104
|
+
|
|
101
105
|
return media_info
|
|
102
|
-
|
|
103
|
-
async def download_media_by_id(
|
|
106
|
+
|
|
107
|
+
async def download_media_by_id(
|
|
108
|
+
self, media_id: str, messenger, media_type: str = None
|
|
109
|
+
) -> tuple[str, dict[str, str]] | None:
|
|
104
110
|
"""
|
|
105
111
|
Download media using media_id through WhatsApp API.
|
|
106
|
-
|
|
112
|
+
|
|
107
113
|
Note: This is a placeholder implementation. The actual implementation
|
|
108
114
|
would need to integrate with the WhatsApp Business API to download media.
|
|
109
|
-
|
|
115
|
+
|
|
110
116
|
Args:
|
|
111
117
|
media_id: Media ID from WhatsApp
|
|
112
118
|
messenger: IMessenger instance for API calls
|
|
113
119
|
media_type: Optional media type hint
|
|
114
|
-
|
|
120
|
+
|
|
115
121
|
Returns:
|
|
116
122
|
Tuple of (file_path, metadata) or None if failed
|
|
117
123
|
"""
|
|
@@ -122,25 +128,27 @@ class MediaHandler:
|
|
|
122
128
|
# 2. Download the media file
|
|
123
129
|
# 3. Save to temporary location
|
|
124
130
|
# 4. Return file path and metadata
|
|
125
|
-
|
|
131
|
+
|
|
126
132
|
# For now, return None to indicate media download not implemented
|
|
127
133
|
return None
|
|
128
|
-
|
|
134
|
+
|
|
129
135
|
except Exception as e:
|
|
130
136
|
print(f"Error downloading media {media_id}: {e}")
|
|
131
137
|
return None
|
|
132
|
-
|
|
133
|
-
async def upload_local_media(
|
|
138
|
+
|
|
139
|
+
async def upload_local_media(
|
|
140
|
+
self, file_path: str, media_type: str = None
|
|
141
|
+
) -> str | None:
|
|
134
142
|
"""
|
|
135
143
|
Upload local media file to get media_id for sending.
|
|
136
|
-
|
|
144
|
+
|
|
137
145
|
Note: This is a placeholder implementation. The actual implementation
|
|
138
146
|
would need to integrate with the WhatsApp Business API media upload endpoint.
|
|
139
|
-
|
|
147
|
+
|
|
140
148
|
Args:
|
|
141
149
|
file_path: Path to local media file
|
|
142
150
|
media_type: Type of media (image, video, audio, document)
|
|
143
|
-
|
|
151
|
+
|
|
144
152
|
Returns:
|
|
145
153
|
Media ID if successful, None if failed
|
|
146
154
|
"""
|
|
@@ -150,95 +158,110 @@ class MediaHandler:
|
|
|
150
158
|
# 1. Upload the file to WhatsApp Business API
|
|
151
159
|
# 2. Get the media_id from the response
|
|
152
160
|
# 3. Return the media_id
|
|
153
|
-
|
|
161
|
+
|
|
154
162
|
# For now, return the file path as a placeholder
|
|
155
163
|
if os.path.exists(file_path):
|
|
156
164
|
return f"local_{os.path.basename(file_path)}"
|
|
157
|
-
|
|
165
|
+
|
|
158
166
|
return None
|
|
159
|
-
|
|
167
|
+
|
|
160
168
|
except Exception as e:
|
|
161
169
|
print(f"Error uploading media {file_path}: {e}")
|
|
162
170
|
return None
|
|
163
|
-
|
|
171
|
+
|
|
164
172
|
def get_local_media_path(self, filename: str, media_subdir: str = None) -> str:
|
|
165
173
|
"""
|
|
166
174
|
Get path to local media file.
|
|
167
|
-
|
|
175
|
+
|
|
168
176
|
Args:
|
|
169
177
|
filename: Name of the media file
|
|
170
178
|
media_subdir: Optional subdirectory within media folder
|
|
171
|
-
|
|
179
|
+
|
|
172
180
|
Returns:
|
|
173
181
|
Full path to media file
|
|
174
182
|
"""
|
|
175
183
|
# Construct path relative to app directory
|
|
176
184
|
base_dir = Path(__file__).parent.parent # Go up to app directory
|
|
177
185
|
media_dir = base_dir / "media"
|
|
178
|
-
|
|
186
|
+
|
|
179
187
|
if media_subdir:
|
|
180
188
|
media_dir = media_dir / media_subdir
|
|
181
|
-
|
|
189
|
+
|
|
182
190
|
return str(media_dir / filename)
|
|
183
|
-
|
|
191
|
+
|
|
184
192
|
def media_file_exists(self, filename: str, media_subdir: str = None) -> bool:
|
|
185
193
|
"""
|
|
186
194
|
Check if local media file exists.
|
|
187
|
-
|
|
195
|
+
|
|
188
196
|
Args:
|
|
189
197
|
filename: Name of the media file
|
|
190
198
|
media_subdir: Optional subdirectory within media folder
|
|
191
|
-
|
|
199
|
+
|
|
192
200
|
Returns:
|
|
193
201
|
True if file exists, False otherwise
|
|
194
202
|
"""
|
|
195
203
|
file_path = self.get_local_media_path(filename, media_subdir)
|
|
196
204
|
return os.path.exists(file_path)
|
|
197
|
-
|
|
205
|
+
|
|
198
206
|
def get_media_type_from_extension(self, filename: str) -> str:
|
|
199
207
|
"""
|
|
200
208
|
Determine media type from file extension.
|
|
201
|
-
|
|
209
|
+
|
|
202
210
|
Args:
|
|
203
211
|
filename: Name of the file
|
|
204
|
-
|
|
212
|
+
|
|
205
213
|
Returns:
|
|
206
214
|
Media type string
|
|
207
215
|
"""
|
|
208
216
|
extension = Path(filename).suffix.lower()
|
|
209
|
-
|
|
217
|
+
|
|
210
218
|
# Image extensions
|
|
211
|
-
if extension in [
|
|
219
|
+
if extension in [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"]:
|
|
212
220
|
return "image"
|
|
213
|
-
|
|
214
|
-
# Video extensions
|
|
215
|
-
elif extension in [
|
|
221
|
+
|
|
222
|
+
# Video extensions
|
|
223
|
+
elif extension in [".mp4", ".avi", ".mov", ".wmv", ".flv", ".webm"]:
|
|
216
224
|
return "video"
|
|
217
|
-
|
|
225
|
+
|
|
218
226
|
# Audio extensions
|
|
219
|
-
elif extension in [
|
|
227
|
+
elif extension in [".mp3", ".wav", ".aac", ".ogg", ".m4a", ".flac"]:
|
|
220
228
|
return "audio"
|
|
221
|
-
|
|
229
|
+
|
|
222
230
|
# Document extensions
|
|
223
|
-
elif extension in [
|
|
231
|
+
elif extension in [
|
|
232
|
+
".pdf",
|
|
233
|
+
".doc",
|
|
234
|
+
".docx",
|
|
235
|
+
".xls",
|
|
236
|
+
".xlsx",
|
|
237
|
+
".ppt",
|
|
238
|
+
".pptx",
|
|
239
|
+
".txt",
|
|
240
|
+
]:
|
|
224
241
|
return "document"
|
|
225
|
-
|
|
242
|
+
|
|
226
243
|
# Default
|
|
227
244
|
else:
|
|
228
245
|
return "document"
|
|
229
|
-
|
|
230
|
-
async def send_media_by_file(
|
|
231
|
-
|
|
246
|
+
|
|
247
|
+
async def send_media_by_file(
|
|
248
|
+
self,
|
|
249
|
+
messenger,
|
|
250
|
+
recipient: str,
|
|
251
|
+
file_path: str,
|
|
252
|
+
caption: str = None,
|
|
253
|
+
reply_to_message_id: str = None,
|
|
254
|
+
) -> dict[str, any]:
|
|
232
255
|
"""
|
|
233
256
|
Send media file using appropriate messenger method.
|
|
234
|
-
|
|
257
|
+
|
|
235
258
|
Args:
|
|
236
259
|
messenger: IMessenger instance
|
|
237
260
|
recipient: Recipient phone number
|
|
238
261
|
file_path: Path to media file
|
|
239
262
|
caption: Optional caption
|
|
240
263
|
reply_to_message_id: Optional message to reply to
|
|
241
|
-
|
|
264
|
+
|
|
242
265
|
Returns:
|
|
243
266
|
Result dictionary with success status and details
|
|
244
267
|
"""
|
|
@@ -247,35 +270,35 @@ class MediaHandler:
|
|
|
247
270
|
return {
|
|
248
271
|
"success": False,
|
|
249
272
|
"error": f"File not found: {file_path}",
|
|
250
|
-
"method": "file_not_found"
|
|
273
|
+
"method": "file_not_found",
|
|
251
274
|
}
|
|
252
|
-
|
|
275
|
+
|
|
253
276
|
media_type = self.get_media_type_from_extension(file_path)
|
|
254
|
-
|
|
277
|
+
|
|
255
278
|
# Send using appropriate method based on media type
|
|
256
279
|
if media_type == "image":
|
|
257
280
|
result = await messenger.send_image(
|
|
258
281
|
image_source=file_path,
|
|
259
282
|
recipient=recipient,
|
|
260
283
|
caption=caption,
|
|
261
|
-
reply_to_message_id=reply_to_message_id
|
|
284
|
+
reply_to_message_id=reply_to_message_id,
|
|
262
285
|
)
|
|
263
|
-
|
|
286
|
+
|
|
264
287
|
elif media_type == "video":
|
|
265
288
|
result = await messenger.send_video(
|
|
266
289
|
video_source=file_path,
|
|
267
290
|
recipient=recipient,
|
|
268
291
|
caption=caption,
|
|
269
|
-
reply_to_message_id=reply_to_message_id
|
|
292
|
+
reply_to_message_id=reply_to_message_id,
|
|
270
293
|
)
|
|
271
|
-
|
|
294
|
+
|
|
272
295
|
elif media_type == "audio":
|
|
273
296
|
result = await messenger.send_audio(
|
|
274
297
|
audio_source=file_path,
|
|
275
298
|
recipient=recipient,
|
|
276
|
-
reply_to_message_id=reply_to_message_id
|
|
299
|
+
reply_to_message_id=reply_to_message_id,
|
|
277
300
|
)
|
|
278
|
-
|
|
301
|
+
|
|
279
302
|
elif media_type == "document":
|
|
280
303
|
filename = os.path.basename(file_path)
|
|
281
304
|
result = await messenger.send_document(
|
|
@@ -283,38 +306,47 @@ class MediaHandler:
|
|
|
283
306
|
recipient=recipient,
|
|
284
307
|
filename=filename,
|
|
285
308
|
caption=caption,
|
|
286
|
-
reply_to_message_id=reply_to_message_id
|
|
309
|
+
reply_to_message_id=reply_to_message_id,
|
|
287
310
|
)
|
|
288
|
-
|
|
311
|
+
|
|
289
312
|
else:
|
|
290
313
|
return {
|
|
291
314
|
"success": False,
|
|
292
315
|
"error": f"Unsupported media type: {media_type}",
|
|
293
|
-
"method": "unsupported_type"
|
|
316
|
+
"method": "unsupported_type",
|
|
294
317
|
}
|
|
295
|
-
|
|
318
|
+
|
|
296
319
|
return {
|
|
297
320
|
"success": result.success,
|
|
298
|
-
"message_id": result.message_id
|
|
299
|
-
|
|
321
|
+
"message_id": result.message_id
|
|
322
|
+
if hasattr(result, "message_id")
|
|
323
|
+
else None,
|
|
324
|
+
"error": result.error if hasattr(result, "error") else None,
|
|
300
325
|
"method": f"send_{media_type}",
|
|
301
326
|
"media_type": media_type,
|
|
302
|
-
"file_path": file_path
|
|
327
|
+
"file_path": file_path,
|
|
303
328
|
}
|
|
304
|
-
|
|
329
|
+
|
|
305
330
|
except Exception as e:
|
|
306
331
|
return {
|
|
307
332
|
"success": False,
|
|
308
333
|
"error": f"Error sending media: {str(e)}",
|
|
309
334
|
"method": "exception",
|
|
310
|
-
"file_path": file_path
|
|
335
|
+
"file_path": file_path,
|
|
311
336
|
}
|
|
312
|
-
|
|
313
|
-
async def send_media_by_id(
|
|
314
|
-
|
|
337
|
+
|
|
338
|
+
async def send_media_by_id(
|
|
339
|
+
self,
|
|
340
|
+
messenger,
|
|
341
|
+
recipient: str,
|
|
342
|
+
media_id: str,
|
|
343
|
+
media_type: str,
|
|
344
|
+
caption: str = None,
|
|
345
|
+
reply_to_message_id: str = None,
|
|
346
|
+
) -> dict[str, any]:
|
|
315
347
|
"""
|
|
316
348
|
Send media using media_id (relay existing media).
|
|
317
|
-
|
|
349
|
+
|
|
318
350
|
Args:
|
|
319
351
|
messenger: IMessenger instance
|
|
320
352
|
recipient: Recipient phone number
|
|
@@ -322,85 +354,87 @@ class MediaHandler:
|
|
|
322
354
|
media_type: Type of media
|
|
323
355
|
caption: Optional caption
|
|
324
356
|
reply_to_message_id: Optional message to reply to
|
|
325
|
-
|
|
357
|
+
|
|
326
358
|
Returns:
|
|
327
359
|
Result dictionary with success status and details
|
|
328
360
|
"""
|
|
329
361
|
try:
|
|
330
362
|
# For relaying media using media_id, we need to use the media_id as source
|
|
331
363
|
# This assumes the messenger can handle media_id as source parameter
|
|
332
|
-
|
|
364
|
+
|
|
333
365
|
if media_type == "image":
|
|
334
366
|
result = await messenger.send_image(
|
|
335
367
|
image_source=media_id, # Using media_id as source
|
|
336
368
|
recipient=recipient,
|
|
337
369
|
caption=caption,
|
|
338
|
-
reply_to_message_id=reply_to_message_id
|
|
370
|
+
reply_to_message_id=reply_to_message_id,
|
|
339
371
|
)
|
|
340
|
-
|
|
372
|
+
|
|
341
373
|
elif media_type == "video":
|
|
342
374
|
result = await messenger.send_video(
|
|
343
375
|
video_source=media_id,
|
|
344
376
|
recipient=recipient,
|
|
345
377
|
caption=caption,
|
|
346
|
-
reply_to_message_id=reply_to_message_id
|
|
378
|
+
reply_to_message_id=reply_to_message_id,
|
|
347
379
|
)
|
|
348
|
-
|
|
380
|
+
|
|
349
381
|
elif media_type in ["audio", "voice"]:
|
|
350
382
|
result = await messenger.send_audio(
|
|
351
383
|
audio_source=media_id,
|
|
352
384
|
recipient=recipient,
|
|
353
|
-
reply_to_message_id=reply_to_message_id
|
|
385
|
+
reply_to_message_id=reply_to_message_id,
|
|
354
386
|
)
|
|
355
|
-
|
|
387
|
+
|
|
356
388
|
elif media_type == "document":
|
|
357
389
|
result = await messenger.send_document(
|
|
358
390
|
document_source=media_id,
|
|
359
391
|
recipient=recipient,
|
|
360
392
|
caption=caption,
|
|
361
|
-
reply_to_message_id=reply_to_message_id
|
|
393
|
+
reply_to_message_id=reply_to_message_id,
|
|
362
394
|
)
|
|
363
|
-
|
|
395
|
+
|
|
364
396
|
elif media_type == "sticker":
|
|
365
397
|
result = await messenger.send_sticker(
|
|
366
398
|
sticker_source=media_id,
|
|
367
399
|
recipient=recipient,
|
|
368
|
-
reply_to_message_id=reply_to_message_id
|
|
400
|
+
reply_to_message_id=reply_to_message_id,
|
|
369
401
|
)
|
|
370
|
-
|
|
402
|
+
|
|
371
403
|
else:
|
|
372
404
|
return {
|
|
373
405
|
"success": False,
|
|
374
406
|
"error": f"Unsupported media type for relay: {media_type}",
|
|
375
|
-
"method": "unsupported_relay_type"
|
|
407
|
+
"method": "unsupported_relay_type",
|
|
376
408
|
}
|
|
377
|
-
|
|
409
|
+
|
|
378
410
|
return {
|
|
379
411
|
"success": result.success,
|
|
380
|
-
"message_id": result.message_id
|
|
381
|
-
|
|
412
|
+
"message_id": result.message_id
|
|
413
|
+
if hasattr(result, "message_id")
|
|
414
|
+
else None,
|
|
415
|
+
"error": result.error if hasattr(result, "error") else None,
|
|
382
416
|
"method": f"relay_{media_type}",
|
|
383
417
|
"media_type": media_type,
|
|
384
|
-
"media_id": media_id
|
|
418
|
+
"media_id": media_id,
|
|
385
419
|
}
|
|
386
|
-
|
|
420
|
+
|
|
387
421
|
except Exception as e:
|
|
388
422
|
return {
|
|
389
423
|
"success": False,
|
|
390
424
|
"error": f"Error relaying media: {str(e)}",
|
|
391
425
|
"method": "relay_exception",
|
|
392
|
-
"media_id": media_id
|
|
426
|
+
"media_id": media_id,
|
|
393
427
|
}
|
|
394
428
|
|
|
395
429
|
|
|
396
430
|
# Convenience functions for direct use
|
|
397
|
-
async def extract_media_info(webhook: IncomingMessageWebhook) ->
|
|
431
|
+
async def extract_media_info(webhook: IncomingMessageWebhook) -> dict[str, str] | None:
|
|
398
432
|
"""
|
|
399
433
|
Extract media information from webhook (convenience function).
|
|
400
|
-
|
|
434
|
+
|
|
401
435
|
Args:
|
|
402
436
|
webhook: IncomingMessageWebhook to process
|
|
403
|
-
|
|
437
|
+
|
|
404
438
|
Returns:
|
|
405
439
|
Media info dictionary or None
|
|
406
440
|
"""
|
|
@@ -408,12 +442,17 @@ async def extract_media_info(webhook: IncomingMessageWebhook) -> Optional[Dict[s
|
|
|
408
442
|
return await handler.get_media_info_from_webhook(webhook)
|
|
409
443
|
|
|
410
444
|
|
|
411
|
-
async def send_local_media_file(
|
|
412
|
-
|
|
413
|
-
|
|
445
|
+
async def send_local_media_file(
|
|
446
|
+
messenger,
|
|
447
|
+
recipient: str,
|
|
448
|
+
filename: str,
|
|
449
|
+
media_subdir: str = None,
|
|
450
|
+
caption: str = None,
|
|
451
|
+
reply_to_message_id: str = None,
|
|
452
|
+
) -> dict[str, any]:
|
|
414
453
|
"""
|
|
415
454
|
Send local media file (convenience function).
|
|
416
|
-
|
|
455
|
+
|
|
417
456
|
Args:
|
|
418
457
|
messenger: IMessenger instance
|
|
419
458
|
recipient: Recipient phone number
|
|
@@ -421,47 +460,51 @@ async def send_local_media_file(messenger, recipient: str, filename: str,
|
|
|
421
460
|
media_subdir: Optional subdirectory
|
|
422
461
|
caption: Optional caption
|
|
423
462
|
reply_to_message_id: Optional message to reply to
|
|
424
|
-
|
|
463
|
+
|
|
425
464
|
Returns:
|
|
426
465
|
Result dictionary
|
|
427
466
|
"""
|
|
428
467
|
handler = MediaHandler()
|
|
429
468
|
file_path = handler.get_local_media_path(filename, media_subdir)
|
|
430
|
-
|
|
469
|
+
|
|
431
470
|
return await handler.send_media_by_file(
|
|
432
471
|
messenger=messenger,
|
|
433
472
|
recipient=recipient,
|
|
434
473
|
file_path=file_path,
|
|
435
474
|
caption=caption,
|
|
436
|
-
reply_to_message_id=reply_to_message_id
|
|
475
|
+
reply_to_message_id=reply_to_message_id,
|
|
437
476
|
)
|
|
438
477
|
|
|
439
478
|
|
|
440
|
-
async def relay_webhook_media(
|
|
441
|
-
|
|
479
|
+
async def relay_webhook_media(
|
|
480
|
+
messenger,
|
|
481
|
+
webhook: IncomingMessageWebhook,
|
|
482
|
+
recipient: str,
|
|
483
|
+
reply_to_message_id: str = None,
|
|
484
|
+
) -> dict[str, any]:
|
|
442
485
|
"""
|
|
443
486
|
Relay media from webhook to recipient (convenience function).
|
|
444
|
-
|
|
487
|
+
|
|
445
488
|
Args:
|
|
446
489
|
messenger: IMessenger instance
|
|
447
490
|
webhook: Original webhook with media
|
|
448
|
-
recipient: Recipient phone number
|
|
491
|
+
recipient: Recipient phone number
|
|
449
492
|
reply_to_message_id: Optional message to reply to
|
|
450
|
-
|
|
493
|
+
|
|
451
494
|
Returns:
|
|
452
495
|
Result dictionary
|
|
453
496
|
"""
|
|
454
497
|
handler = MediaHandler()
|
|
455
|
-
|
|
498
|
+
|
|
456
499
|
# Extract media info from webhook
|
|
457
500
|
media_info = await handler.get_media_info_from_webhook(webhook)
|
|
458
501
|
if not media_info:
|
|
459
502
|
return {
|
|
460
503
|
"success": False,
|
|
461
504
|
"error": "No media found in webhook",
|
|
462
|
-
"method": "no_media"
|
|
505
|
+
"method": "no_media",
|
|
463
506
|
}
|
|
464
|
-
|
|
507
|
+
|
|
465
508
|
# Relay using media_id
|
|
466
509
|
return await handler.send_media_by_id(
|
|
467
510
|
messenger=messenger,
|
|
@@ -469,5 +512,5 @@ async def relay_webhook_media(messenger, webhook: IncomingMessageWebhook,
|
|
|
469
512
|
media_id=media_info["media_id"],
|
|
470
513
|
media_type=media_info["type"],
|
|
471
514
|
caption=media_info.get("caption"),
|
|
472
|
-
reply_to_message_id=reply_to_message_id
|
|
473
|
-
)
|
|
515
|
+
reply_to_message_id=reply_to_message_id,
|
|
516
|
+
)
|