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
|
@@ -6,10 +6,12 @@ that demonstrate interactive features and specialized messaging capabilities.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import time
|
|
9
|
-
from typing import Dict
|
|
10
9
|
|
|
10
|
+
from wappa.messaging.whatsapp.models.interactive_models import (
|
|
11
|
+
InteractiveHeader,
|
|
12
|
+
ReplyButton,
|
|
13
|
+
)
|
|
11
14
|
from wappa.webhooks import IncomingMessageWebhook
|
|
12
|
-
from wappa.messaging.whatsapp.models.interactive_models import ReplyButton, InteractiveHeader
|
|
13
15
|
|
|
14
16
|
from ..models.state_models import ButtonState, ListState, StateType
|
|
15
17
|
from ..models.user_models import UserProfile
|
|
@@ -18,11 +20,11 @@ from ..utils.cache_utils import CacheHelper
|
|
|
18
20
|
|
|
19
21
|
class CommandHandlers:
|
|
20
22
|
"""Collection of handlers for special commands."""
|
|
21
|
-
|
|
23
|
+
|
|
22
24
|
def __init__(self, messenger, cache_factory, logger):
|
|
23
25
|
"""
|
|
24
26
|
Initialize command handlers.
|
|
25
|
-
|
|
27
|
+
|
|
26
28
|
Args:
|
|
27
29
|
messenger: IMessenger instance for sending messages
|
|
28
30
|
cache_factory: Cache factory for data persistence
|
|
@@ -31,56 +33,59 @@ class CommandHandlers:
|
|
|
31
33
|
self.messenger = messenger
|
|
32
34
|
self.cache_helper = CacheHelper(cache_factory)
|
|
33
35
|
self.logger = logger
|
|
34
|
-
|
|
35
|
-
async def handle_button_command(
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
async def handle_button_command(
|
|
38
|
+
self, webhook: IncomingMessageWebhook, user_profile: UserProfile
|
|
39
|
+
) -> dict[str, any]:
|
|
37
40
|
"""
|
|
38
41
|
Handle /button command - creates interactive button message.
|
|
39
|
-
|
|
42
|
+
|
|
40
43
|
Args:
|
|
41
44
|
webhook: IncomingMessageWebhook with command
|
|
42
45
|
user_profile: User profile for tracking
|
|
43
|
-
|
|
46
|
+
|
|
44
47
|
Returns:
|
|
45
48
|
Result dictionary with operation status
|
|
46
49
|
"""
|
|
47
50
|
try:
|
|
48
51
|
start_time = time.time()
|
|
49
|
-
|
|
52
|
+
|
|
50
53
|
user_id = webhook.user.user_id
|
|
51
54
|
message_id = webhook.message.message_id
|
|
52
|
-
|
|
55
|
+
|
|
53
56
|
self.logger.info(f"🔘 Processing /button command from {user_id}")
|
|
54
|
-
|
|
57
|
+
|
|
55
58
|
# Clean up any existing button state
|
|
56
|
-
existing_state = await self.cache_helper.get_user_state(
|
|
59
|
+
existing_state = await self.cache_helper.get_user_state(
|
|
60
|
+
user_id, StateType.BUTTON
|
|
61
|
+
)
|
|
57
62
|
if existing_state:
|
|
58
63
|
await self.cache_helper.remove_user_state(user_id, StateType.BUTTON)
|
|
59
|
-
|
|
64
|
+
|
|
60
65
|
# Create button data for state storage (as dictionaries)
|
|
61
66
|
button_data = [
|
|
62
67
|
{"id": "kitty", "title": "🐱 Kitty"},
|
|
63
|
-
{"id": "puppy", "title": "🐶 Puppy"}
|
|
68
|
+
{"id": "puppy", "title": "🐶 Puppy"},
|
|
64
69
|
]
|
|
65
|
-
|
|
70
|
+
|
|
66
71
|
# Create button objects for WhatsApp messenger
|
|
67
72
|
buttons = [
|
|
68
73
|
ReplyButton(id="kitty", title="🐱 Kitty"),
|
|
69
|
-
ReplyButton(id="puppy", title="🐶 Puppy")
|
|
74
|
+
ReplyButton(id="puppy", title="🐶 Puppy"),
|
|
70
75
|
]
|
|
71
|
-
|
|
76
|
+
|
|
72
77
|
# Create button state with 10 minute TTL (using dictionaries)
|
|
73
78
|
button_state = ButtonState.create_button_state(
|
|
74
79
|
user_id=user_id,
|
|
75
80
|
buttons=button_data,
|
|
76
81
|
message_text="Choose your favorite animal! You have 10 minutes to decide.",
|
|
77
82
|
ttl_seconds=600, # 10 minutes
|
|
78
|
-
original_message_id=message_id
|
|
83
|
+
original_message_id=message_id,
|
|
79
84
|
)
|
|
80
|
-
|
|
85
|
+
|
|
81
86
|
# Save the state
|
|
82
87
|
await self.cache_helper.save_user_state(button_state)
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
# Send button message
|
|
85
90
|
button_result = await self.messenger.send_button_message(
|
|
86
91
|
buttons=buttons,
|
|
@@ -88,18 +93,20 @@ class CommandHandlers:
|
|
|
88
93
|
body="🎯 *Button Demo Activated!*\n\nChoose your favorite animal below. You have 10 minutes to make your selection, or the state will expire automatically.",
|
|
89
94
|
header=InteractiveHeader(type="text", text="Interactive Button Demo"),
|
|
90
95
|
footer="⏰ Expires in 10 minutes",
|
|
91
|
-
reply_to_message_id=message_id
|
|
96
|
+
reply_to_message_id=message_id,
|
|
92
97
|
)
|
|
93
|
-
|
|
98
|
+
|
|
94
99
|
if not button_result.success:
|
|
95
|
-
self.logger.error(
|
|
100
|
+
self.logger.error(
|
|
101
|
+
f"Failed to send button message: {button_result.error}"
|
|
102
|
+
)
|
|
96
103
|
await self.cache_helper.remove_user_state(user_id, StateType.BUTTON)
|
|
97
104
|
return {"success": False, "error": "Failed to send button message"}
|
|
98
|
-
|
|
105
|
+
|
|
99
106
|
# Update button state with message ID
|
|
100
107
|
button_state.interactive_message_id = button_result.message_id
|
|
101
108
|
await self.cache_helper.save_user_state(button_state)
|
|
102
|
-
|
|
109
|
+
|
|
103
110
|
# Send instruction message
|
|
104
111
|
instruction_text = (
|
|
105
112
|
"📋 *How to use this demo:*\n\n"
|
|
@@ -110,56 +117,56 @@ class CommandHandlers:
|
|
|
110
117
|
"5. ⏰ State expires in 10 minutes if no selection is made\n\n"
|
|
111
118
|
"💡 *Pro tip*: This demonstrates state management with TTL!"
|
|
112
119
|
)
|
|
113
|
-
|
|
114
|
-
await self.messenger.send_text(
|
|
115
|
-
|
|
116
|
-
text=instruction_text
|
|
117
|
-
)
|
|
118
|
-
|
|
120
|
+
|
|
121
|
+
await self.messenger.send_text(recipient=user_id, text=instruction_text)
|
|
122
|
+
|
|
119
123
|
# Update user activity
|
|
120
124
|
await self.cache_helper.update_user_activity(user_id, "text", "/button")
|
|
121
|
-
|
|
125
|
+
|
|
122
126
|
processing_time = int((time.time() - start_time) * 1000)
|
|
123
127
|
self.logger.info(f"✅ Button command processed in {processing_time}ms")
|
|
124
|
-
|
|
128
|
+
|
|
125
129
|
return {
|
|
126
130
|
"success": True,
|
|
127
131
|
"command": "/button",
|
|
128
132
|
"state_created": True,
|
|
129
133
|
"state_ttl_seconds": 600,
|
|
130
134
|
"buttons_sent": True,
|
|
131
|
-
"processing_time_ms": processing_time
|
|
135
|
+
"processing_time_ms": processing_time,
|
|
132
136
|
}
|
|
133
|
-
|
|
137
|
+
|
|
134
138
|
except Exception as e:
|
|
135
139
|
self.logger.error(f"❌ Error handling /button command: {e}", exc_info=True)
|
|
136
140
|
return {"success": False, "error": str(e)}
|
|
137
|
-
|
|
138
|
-
async def handle_list_command(
|
|
139
|
-
|
|
141
|
+
|
|
142
|
+
async def handle_list_command(
|
|
143
|
+
self, webhook: IncomingMessageWebhook, user_profile: UserProfile
|
|
144
|
+
) -> dict[str, any]:
|
|
140
145
|
"""
|
|
141
146
|
Handle /list command - creates interactive list message.
|
|
142
|
-
|
|
147
|
+
|
|
143
148
|
Args:
|
|
144
149
|
webhook: IncomingMessageWebhook with command
|
|
145
150
|
user_profile: User profile for tracking
|
|
146
|
-
|
|
151
|
+
|
|
147
152
|
Returns:
|
|
148
153
|
Result dictionary with operation status
|
|
149
154
|
"""
|
|
150
155
|
try:
|
|
151
156
|
start_time = time.time()
|
|
152
|
-
|
|
157
|
+
|
|
153
158
|
user_id = webhook.user.user_id
|
|
154
159
|
message_id = webhook.message.message_id
|
|
155
|
-
|
|
160
|
+
|
|
156
161
|
self.logger.info(f"📋 Processing /list command from {user_id}")
|
|
157
|
-
|
|
162
|
+
|
|
158
163
|
# Clean up any existing list state
|
|
159
|
-
existing_state = await self.cache_helper.get_user_state(
|
|
164
|
+
existing_state = await self.cache_helper.get_user_state(
|
|
165
|
+
user_id, StateType.LIST
|
|
166
|
+
)
|
|
160
167
|
if existing_state:
|
|
161
168
|
await self.cache_helper.remove_user_state(user_id, StateType.LIST)
|
|
162
|
-
|
|
169
|
+
|
|
163
170
|
# Create list sections with media options
|
|
164
171
|
sections = [
|
|
165
172
|
{
|
|
@@ -168,27 +175,27 @@ class CommandHandlers:
|
|
|
168
175
|
{
|
|
169
176
|
"id": "image_file",
|
|
170
177
|
"title": "🖼️ Image",
|
|
171
|
-
"description": "Get a sample image file"
|
|
178
|
+
"description": "Get a sample image file",
|
|
172
179
|
},
|
|
173
180
|
{
|
|
174
181
|
"id": "video_file",
|
|
175
182
|
"title": "🎬 Video",
|
|
176
|
-
"description": "Get a sample video file"
|
|
183
|
+
"description": "Get a sample video file",
|
|
177
184
|
},
|
|
178
185
|
{
|
|
179
186
|
"id": "audio_file",
|
|
180
187
|
"title": "🎵 Audio",
|
|
181
|
-
"description": "Get a sample audio file"
|
|
188
|
+
"description": "Get a sample audio file",
|
|
182
189
|
},
|
|
183
190
|
{
|
|
184
191
|
"id": "document_file",
|
|
185
192
|
"title": "📄 Document",
|
|
186
|
-
"description": "Get a sample document file"
|
|
187
|
-
}
|
|
188
|
-
]
|
|
193
|
+
"description": "Get a sample document file",
|
|
194
|
+
},
|
|
195
|
+
],
|
|
189
196
|
}
|
|
190
197
|
]
|
|
191
|
-
|
|
198
|
+
|
|
192
199
|
# Create list state with 10 minute TTL
|
|
193
200
|
list_state = ListState.create_list_state(
|
|
194
201
|
user_id=user_id,
|
|
@@ -196,12 +203,12 @@ class CommandHandlers:
|
|
|
196
203
|
message_text="Choose the type of media file you want to receive!",
|
|
197
204
|
button_text="Choose Media",
|
|
198
205
|
ttl_seconds=600, # 10 minutes
|
|
199
|
-
original_message_id=message_id
|
|
206
|
+
original_message_id=message_id,
|
|
200
207
|
)
|
|
201
|
-
|
|
208
|
+
|
|
202
209
|
# Save the state
|
|
203
210
|
await self.cache_helper.save_user_state(list_state)
|
|
204
|
-
|
|
211
|
+
|
|
205
212
|
# Send list message
|
|
206
213
|
list_result = await self.messenger.send_list_message(
|
|
207
214
|
sections=sections,
|
|
@@ -210,18 +217,18 @@ class CommandHandlers:
|
|
|
210
217
|
button_text="Choose Media",
|
|
211
218
|
header="Interactive List Demo",
|
|
212
219
|
footer="⏰ Expires in 10 minutes",
|
|
213
|
-
reply_to_message_id=message_id
|
|
220
|
+
reply_to_message_id=message_id,
|
|
214
221
|
)
|
|
215
|
-
|
|
222
|
+
|
|
216
223
|
if not list_result.success:
|
|
217
224
|
self.logger.error(f"Failed to send list message: {list_result.error}")
|
|
218
225
|
await self.cache_helper.remove_user_state(user_id, StateType.LIST)
|
|
219
226
|
return {"success": False, "error": "Failed to send list message"}
|
|
220
|
-
|
|
227
|
+
|
|
221
228
|
# Update list state with message ID
|
|
222
229
|
list_state.interactive_message_id = list_result.message_id
|
|
223
230
|
await self.cache_helper.save_user_state(list_state)
|
|
224
|
-
|
|
231
|
+
|
|
225
232
|
# Send instruction message
|
|
226
233
|
instruction_text = (
|
|
227
234
|
"📋 *How to use this demo:*\n\n"
|
|
@@ -233,51 +240,49 @@ class CommandHandlers:
|
|
|
233
240
|
"6. ⏰ State expires in 10 minutes if no selection is made\n\n"
|
|
234
241
|
"💡 *Pro tip*: This demonstrates list interactions with media responses!"
|
|
235
242
|
)
|
|
236
|
-
|
|
237
|
-
await self.messenger.send_text(
|
|
238
|
-
|
|
239
|
-
text=instruction_text
|
|
240
|
-
)
|
|
241
|
-
|
|
243
|
+
|
|
244
|
+
await self.messenger.send_text(recipient=user_id, text=instruction_text)
|
|
245
|
+
|
|
242
246
|
# Update user activity
|
|
243
247
|
await self.cache_helper.update_user_activity(user_id, "text", "/list")
|
|
244
|
-
|
|
248
|
+
|
|
245
249
|
processing_time = int((time.time() - start_time) * 1000)
|
|
246
250
|
self.logger.info(f"✅ List command processed in {processing_time}ms")
|
|
247
|
-
|
|
251
|
+
|
|
248
252
|
return {
|
|
249
253
|
"success": True,
|
|
250
254
|
"command": "/list",
|
|
251
255
|
"state_created": True,
|
|
252
256
|
"state_ttl_seconds": 600,
|
|
253
257
|
"list_sent": True,
|
|
254
|
-
"processing_time_ms": processing_time
|
|
258
|
+
"processing_time_ms": processing_time,
|
|
255
259
|
}
|
|
256
|
-
|
|
260
|
+
|
|
257
261
|
except Exception as e:
|
|
258
262
|
self.logger.error(f"❌ Error handling /list command: {e}", exc_info=True)
|
|
259
263
|
return {"success": False, "error": str(e)}
|
|
260
|
-
|
|
261
|
-
async def handle_cta_command(
|
|
262
|
-
|
|
264
|
+
|
|
265
|
+
async def handle_cta_command(
|
|
266
|
+
self, webhook: IncomingMessageWebhook, user_profile: UserProfile
|
|
267
|
+
) -> dict[str, any]:
|
|
263
268
|
"""
|
|
264
269
|
Handle /cta command - sends call-to-action message.
|
|
265
|
-
|
|
270
|
+
|
|
266
271
|
Args:
|
|
267
272
|
webhook: IncomingMessageWebhook with command
|
|
268
273
|
user_profile: User profile for tracking
|
|
269
|
-
|
|
274
|
+
|
|
270
275
|
Returns:
|
|
271
276
|
Result dictionary with operation status
|
|
272
277
|
"""
|
|
273
278
|
try:
|
|
274
279
|
start_time = time.time()
|
|
275
|
-
|
|
280
|
+
|
|
276
281
|
user_id = webhook.user.user_id
|
|
277
282
|
message_id = webhook.message.message_id
|
|
278
|
-
|
|
283
|
+
|
|
279
284
|
self.logger.info(f"🔗 Processing /cta command from {user_id}")
|
|
280
|
-
|
|
285
|
+
|
|
281
286
|
# Send CTA message with link to Wappa documentation
|
|
282
287
|
cta_result = await self.messenger.send_cta_message(
|
|
283
288
|
button_text="📚 View Documentation",
|
|
@@ -286,13 +291,13 @@ class CommandHandlers:
|
|
|
286
291
|
body="🎯 *Call-to-Action Demo*\n\nThis is a demonstration of CTA (Call-to-Action) buttons that link to external websites. Click the button below to visit the Wappa framework documentation!",
|
|
287
292
|
header="CTA Button Demo",
|
|
288
293
|
footer="External link - opens in browser",
|
|
289
|
-
reply_to_message_id=message_id
|
|
294
|
+
reply_to_message_id=message_id,
|
|
290
295
|
)
|
|
291
|
-
|
|
296
|
+
|
|
292
297
|
if not cta_result.success:
|
|
293
298
|
self.logger.error(f"Failed to send CTA message: {cta_result.error}")
|
|
294
299
|
return {"success": False, "error": "Failed to send CTA message"}
|
|
295
|
-
|
|
300
|
+
|
|
296
301
|
# Send follow-up explanation
|
|
297
302
|
explanation_text = (
|
|
298
303
|
"📋 *About CTA Buttons:*\n\n"
|
|
@@ -307,56 +312,54 @@ class CommandHandlers:
|
|
|
307
312
|
"• Provide easy access to support or contact forms\n\n"
|
|
308
313
|
"💡 *Pro tip*: CTA buttons are great for bridging WhatsApp conversations with web experiences!"
|
|
309
314
|
)
|
|
310
|
-
|
|
311
|
-
await self.messenger.send_text(
|
|
312
|
-
|
|
313
|
-
text=explanation_text
|
|
314
|
-
)
|
|
315
|
-
|
|
315
|
+
|
|
316
|
+
await self.messenger.send_text(recipient=user_id, text=explanation_text)
|
|
317
|
+
|
|
316
318
|
# Update user activity
|
|
317
319
|
await self.cache_helper.update_user_activity(user_id, "text", "/cta")
|
|
318
|
-
|
|
320
|
+
|
|
319
321
|
processing_time = int((time.time() - start_time) * 1000)
|
|
320
322
|
self.logger.info(f"✅ CTA command processed in {processing_time}ms")
|
|
321
|
-
|
|
323
|
+
|
|
322
324
|
return {
|
|
323
325
|
"success": True,
|
|
324
326
|
"command": "/cta",
|
|
325
327
|
"cta_sent": True,
|
|
326
328
|
"url": "https://wappa.mimeia.com/docs",
|
|
327
|
-
"processing_time_ms": processing_time
|
|
329
|
+
"processing_time_ms": processing_time,
|
|
328
330
|
}
|
|
329
|
-
|
|
331
|
+
|
|
330
332
|
except Exception as e:
|
|
331
333
|
self.logger.error(f"❌ Error handling /cta command: {e}", exc_info=True)
|
|
332
334
|
return {"success": False, "error": str(e)}
|
|
333
|
-
|
|
334
|
-
async def handle_location_command(
|
|
335
|
-
|
|
335
|
+
|
|
336
|
+
async def handle_location_command(
|
|
337
|
+
self, webhook: IncomingMessageWebhook, user_profile: UserProfile
|
|
338
|
+
) -> dict[str, any]:
|
|
336
339
|
"""
|
|
337
340
|
Handle /location command - sends predefined location.
|
|
338
|
-
|
|
341
|
+
|
|
339
342
|
Args:
|
|
340
343
|
webhook: IncomingMessageWebhook with command
|
|
341
344
|
user_profile: User profile for tracking
|
|
342
|
-
|
|
345
|
+
|
|
343
346
|
Returns:
|
|
344
347
|
Result dictionary with operation status
|
|
345
348
|
"""
|
|
346
349
|
try:
|
|
347
350
|
start_time = time.time()
|
|
348
|
-
|
|
351
|
+
|
|
349
352
|
user_id = webhook.user.user_id
|
|
350
353
|
message_id = webhook.message.message_id
|
|
351
|
-
|
|
354
|
+
|
|
352
355
|
self.logger.info(f"📍 Processing /location command from {user_id}")
|
|
353
|
-
|
|
356
|
+
|
|
354
357
|
# Predefined coordinates (Bogotá, Colombia)
|
|
355
358
|
latitude = 4.616738
|
|
356
359
|
longitude = -74.089853
|
|
357
360
|
location_name = "Bogotá, Colombia"
|
|
358
361
|
location_address = "Bogotá D.C., Colombia"
|
|
359
|
-
|
|
362
|
+
|
|
360
363
|
# Send location message
|
|
361
364
|
location_result = await self.messenger.send_location(
|
|
362
365
|
latitude=latitude,
|
|
@@ -364,13 +367,13 @@ class CommandHandlers:
|
|
|
364
367
|
recipient=user_id,
|
|
365
368
|
name=location_name,
|
|
366
369
|
address=location_address,
|
|
367
|
-
reply_to_message_id=message_id
|
|
370
|
+
reply_to_message_id=message_id,
|
|
368
371
|
)
|
|
369
|
-
|
|
372
|
+
|
|
370
373
|
if not location_result.success:
|
|
371
374
|
self.logger.error(f"Failed to send location: {location_result.error}")
|
|
372
375
|
return {"success": False, "error": "Failed to send location"}
|
|
373
|
-
|
|
376
|
+
|
|
374
377
|
# Send follow-up explanation
|
|
375
378
|
explanation_text = (
|
|
376
379
|
f"📍 *Location Demo*\n\n"
|
|
@@ -386,47 +389,52 @@ class CommandHandlers:
|
|
|
386
389
|
f"• Useful for sharing business locations, meeting points, etc.\n\n"
|
|
387
390
|
f"💡 *Pro tip*: Location messages are perfect for businesses to share their address with customers!"
|
|
388
391
|
)
|
|
389
|
-
|
|
390
|
-
await self.messenger.send_text(
|
|
391
|
-
|
|
392
|
-
text=explanation_text
|
|
393
|
-
)
|
|
394
|
-
|
|
392
|
+
|
|
393
|
+
await self.messenger.send_text(recipient=user_id, text=explanation_text)
|
|
394
|
+
|
|
395
395
|
# Update user activity
|
|
396
396
|
await self.cache_helper.update_user_activity(user_id, "text", "/location")
|
|
397
|
-
|
|
397
|
+
|
|
398
398
|
processing_time = int((time.time() - start_time) * 1000)
|
|
399
399
|
self.logger.info(f"✅ Location command processed in {processing_time}ms")
|
|
400
|
-
|
|
400
|
+
|
|
401
401
|
return {
|
|
402
402
|
"success": True,
|
|
403
403
|
"command": "/location",
|
|
404
404
|
"location_sent": True,
|
|
405
405
|
"coordinates": {"latitude": latitude, "longitude": longitude},
|
|
406
406
|
"location_name": location_name,
|
|
407
|
-
"processing_time_ms": processing_time
|
|
407
|
+
"processing_time_ms": processing_time,
|
|
408
408
|
}
|
|
409
|
-
|
|
409
|
+
|
|
410
410
|
except Exception as e:
|
|
411
|
-
self.logger.error(
|
|
411
|
+
self.logger.error(
|
|
412
|
+
f"❌ Error handling /location command: {e}", exc_info=True
|
|
413
|
+
)
|
|
412
414
|
return {"success": False, "error": str(e)}
|
|
413
415
|
|
|
414
416
|
|
|
415
417
|
# Command mapping for easy lookup
|
|
416
418
|
COMMAND_HANDLERS = {
|
|
417
419
|
"/button": "handle_button_command",
|
|
418
|
-
"/list": "handle_list_command",
|
|
420
|
+
"/list": "handle_list_command",
|
|
419
421
|
"/cta": "handle_cta_command",
|
|
420
|
-
"/location": "handle_location_command"
|
|
422
|
+
"/location": "handle_location_command",
|
|
421
423
|
}
|
|
422
424
|
|
|
423
425
|
|
|
424
426
|
# Convenience functions for direct use
|
|
425
|
-
async def handle_command(
|
|
426
|
-
|
|
427
|
+
async def handle_command(
|
|
428
|
+
command: str,
|
|
429
|
+
webhook: IncomingMessageWebhook,
|
|
430
|
+
user_profile: UserProfile,
|
|
431
|
+
messenger,
|
|
432
|
+
cache_factory,
|
|
433
|
+
logger,
|
|
434
|
+
) -> dict[str, any]:
|
|
427
435
|
"""
|
|
428
436
|
Handle command based on command string (convenience function).
|
|
429
|
-
|
|
437
|
+
|
|
430
438
|
Args:
|
|
431
439
|
command: Command string (e.g., "/button")
|
|
432
440
|
webhook: IncomingMessageWebhook with command
|
|
@@ -434,13 +442,13 @@ async def handle_command(command: str, webhook: IncomingMessageWebhook,
|
|
|
434
442
|
messenger: IMessenger instance
|
|
435
443
|
cache_factory: Cache factory
|
|
436
444
|
logger: Logger instance
|
|
437
|
-
|
|
445
|
+
|
|
438
446
|
Returns:
|
|
439
447
|
Result dictionary
|
|
440
448
|
"""
|
|
441
449
|
handlers = CommandHandlers(messenger, cache_factory, logger)
|
|
442
450
|
command_lower = command.lower()
|
|
443
|
-
|
|
451
|
+
|
|
444
452
|
if command_lower == "/button":
|
|
445
453
|
return await handlers.handle_button_command(webhook, user_profile)
|
|
446
454
|
elif command_lower == "/list":
|
|
@@ -457,10 +465,10 @@ async def handle_command(command: str, webhook: IncomingMessageWebhook,
|
|
|
457
465
|
def is_special_command(text: str) -> bool:
|
|
458
466
|
"""
|
|
459
467
|
Check if text is a special command.
|
|
460
|
-
|
|
468
|
+
|
|
461
469
|
Args:
|
|
462
470
|
text: Message text to check
|
|
463
|
-
|
|
471
|
+
|
|
464
472
|
Returns:
|
|
465
473
|
True if it's a special command, False otherwise
|
|
466
474
|
"""
|
|
@@ -471,14 +479,14 @@ def is_special_command(text: str) -> bool:
|
|
|
471
479
|
def get_command_from_text(text: str) -> str:
|
|
472
480
|
"""
|
|
473
481
|
Extract command from message text.
|
|
474
|
-
|
|
482
|
+
|
|
475
483
|
Args:
|
|
476
484
|
text: Message text
|
|
477
|
-
|
|
485
|
+
|
|
478
486
|
Returns:
|
|
479
487
|
Command string or empty string if not a command
|
|
480
488
|
"""
|
|
481
489
|
text_clean = text.strip().lower()
|
|
482
490
|
if is_special_command(text_clean):
|
|
483
491
|
return text_clean
|
|
484
|
-
return ""
|
|
492
|
+
return ""
|