webagents 0.2.0__py3-none-any.whl → 0.2.2__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.
- webagents/agents/skills/ecosystem/crewai/__init__.py +3 -1
- webagents/agents/skills/ecosystem/crewai/skill.py +158 -0
- webagents/agents/skills/ecosystem/database/__init__.py +3 -1
- webagents/agents/skills/ecosystem/database/skill.py +522 -0
- webagents/agents/skills/ecosystem/mongodb/__init__.py +3 -0
- webagents/agents/skills/ecosystem/mongodb/skill.py +428 -0
- webagents/agents/skills/ecosystem/n8n/README.md +287 -0
- webagents/agents/skills/ecosystem/n8n/__init__.py +3 -0
- webagents/agents/skills/ecosystem/n8n/skill.py +341 -0
- webagents/agents/skills/ecosystem/x_com/README.md +401 -0
- webagents/agents/skills/ecosystem/x_com/__init__.py +3 -0
- webagents/agents/skills/ecosystem/x_com/skill.py +1048 -0
- webagents/agents/skills/ecosystem/zapier/README.md +363 -0
- webagents/agents/skills/ecosystem/zapier/__init__.py +3 -0
- webagents/agents/skills/ecosystem/zapier/skill.py +337 -0
- {webagents-0.2.0.dist-info → webagents-0.2.2.dist-info}/METADATA +33 -9
- {webagents-0.2.0.dist-info → webagents-0.2.2.dist-info}/RECORD +20 -9
- {webagents-0.2.0.dist-info → webagents-0.2.2.dist-info}/WHEEL +0 -0
- {webagents-0.2.0.dist-info → webagents-0.2.2.dist-info}/entry_points.txt +0 -0
- {webagents-0.2.0.dist-info → webagents-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,363 @@
|
|
1
|
+
# Zapier Skill for WebAgents
|
2
|
+
|
3
|
+
Minimalistic Zapier integration for workflow automation. This skill allows you to securely manage Zapier API credentials and trigger Zaps from your WebAgent.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- 🔐 **Secure credential storage** via auth and KV skills
|
8
|
+
- ⚡ **Trigger Zaps** with custom input data
|
9
|
+
- 📋 **List Zaps** from your Zapier account
|
10
|
+
- 📊 **Monitor task status** in real-time
|
11
|
+
- 🔒 **Per-user isolation** with authentication
|
12
|
+
- 🛡️ **API key validation** during setup
|
13
|
+
|
14
|
+
## Prerequisites
|
15
|
+
|
16
|
+
1. **Zapier account** (free or paid)
|
17
|
+
2. **Zapier API key** (generated from Zapier account settings)
|
18
|
+
3. **WebAgents framework** with auth and kv skills enabled
|
19
|
+
|
20
|
+
## Quick Start
|
21
|
+
|
22
|
+
### 1. Set up Zapier API credentials
|
23
|
+
|
24
|
+
```python
|
25
|
+
# First, configure your Zapier credentials
|
26
|
+
await agent.call_tool("zapier_setup",
|
27
|
+
api_key="your_zapier_api_key_here"
|
28
|
+
)
|
29
|
+
```
|
30
|
+
|
31
|
+
### 2. List available Zaps
|
32
|
+
|
33
|
+
```python
|
34
|
+
# See all Zaps in your Zapier account
|
35
|
+
await agent.call_tool("zapier_list_zaps")
|
36
|
+
```
|
37
|
+
|
38
|
+
### 3. Trigger a Zap
|
39
|
+
|
40
|
+
```python
|
41
|
+
# Trigger a Zap with optional input data
|
42
|
+
await agent.call_tool("zapier_trigger",
|
43
|
+
zap_id="zap_id_here",
|
44
|
+
data={"name": "John", "email": "john@example.com"} # Optional
|
45
|
+
)
|
46
|
+
```
|
47
|
+
|
48
|
+
### 4. Check task status
|
49
|
+
|
50
|
+
```python
|
51
|
+
# Monitor Zap execution
|
52
|
+
await agent.call_tool("zapier_status",
|
53
|
+
task_id="task_id_from_trigger_response"
|
54
|
+
)
|
55
|
+
```
|
56
|
+
|
57
|
+
## Tools Reference
|
58
|
+
|
59
|
+
### `zapier_setup(api_key)`
|
60
|
+
Set up Zapier API credentials securely.
|
61
|
+
|
62
|
+
**Parameters:**
|
63
|
+
- `api_key` (str): Your Zapier API key from account settings
|
64
|
+
|
65
|
+
**Returns:**
|
66
|
+
- Success message with account information
|
67
|
+
- Error message if API key is invalid
|
68
|
+
|
69
|
+
**Example:**
|
70
|
+
```python
|
71
|
+
result = await agent.call_tool("zapier_setup",
|
72
|
+
api_key="AK_1a2b3c4d5e6f7g8h9i0j"
|
73
|
+
)
|
74
|
+
# ✅ Zapier credentials saved successfully!
|
75
|
+
# 🔑 API key configured
|
76
|
+
# 📊 Found 5 Zaps in your account
|
77
|
+
```
|
78
|
+
|
79
|
+
### `zapier_trigger(zap_id, data=None)`
|
80
|
+
Trigger a Zapier Zap with optional input data.
|
81
|
+
|
82
|
+
**Parameters:**
|
83
|
+
- `zap_id` (str): The ID of the Zap to trigger
|
84
|
+
- `data` (dict, optional): Input data to pass to the Zap
|
85
|
+
|
86
|
+
**Returns:**
|
87
|
+
- Success message with task ID and status
|
88
|
+
- Error message if Zap not found or trigger fails
|
89
|
+
|
90
|
+
**Example:**
|
91
|
+
```python
|
92
|
+
result = await agent.call_tool("zapier_trigger",
|
93
|
+
zap_id="12345",
|
94
|
+
data={"customer_name": "Jane Doe", "order_total": 99.99}
|
95
|
+
)
|
96
|
+
# ✅ Zap triggered successfully!
|
97
|
+
# 📋 Task ID: task_67890
|
98
|
+
# 📊 Status: triggered
|
99
|
+
```
|
100
|
+
|
101
|
+
### `zapier_list_zaps()`
|
102
|
+
List all available Zaps in your Zapier account.
|
103
|
+
|
104
|
+
**Parameters:** None
|
105
|
+
|
106
|
+
**Returns:**
|
107
|
+
- List of Zaps with names, IDs, status, and trigger apps
|
108
|
+
- Empty message if no Zaps found
|
109
|
+
|
110
|
+
**Example:**
|
111
|
+
```python
|
112
|
+
result = await agent.call_tool("zapier_list_zaps")
|
113
|
+
# 📋 Available Zapier Zaps:
|
114
|
+
#
|
115
|
+
# 🟢 **Email to Slack** (ID: 12345)
|
116
|
+
# 📊 Status: on
|
117
|
+
# 🔗 Trigger: Gmail
|
118
|
+
#
|
119
|
+
# 🔴 **Form to Spreadsheet** (ID: 67890)
|
120
|
+
# 📊 Status: off
|
121
|
+
# 🔗 Trigger: Typeform
|
122
|
+
#
|
123
|
+
# 💡 Use zapier_trigger(zap_id, data) to trigger a Zap
|
124
|
+
```
|
125
|
+
|
126
|
+
### `zapier_status(task_id)`
|
127
|
+
Check the status of a Zap execution.
|
128
|
+
|
129
|
+
**Parameters:**
|
130
|
+
- `task_id` (str): The task ID returned from zapier_trigger
|
131
|
+
|
132
|
+
**Returns:**
|
133
|
+
- Detailed task status report
|
134
|
+
- Error message if task not found
|
135
|
+
|
136
|
+
**Example:**
|
137
|
+
```python
|
138
|
+
result = await agent.call_tool("zapier_status", task_id="task_67890")
|
139
|
+
# 📊 Zap Execution Status Report
|
140
|
+
# 🆔 Task ID: task_67890
|
141
|
+
# 🔧 Zap ID: 12345
|
142
|
+
# ✅ Status: success
|
143
|
+
# 🕐 Created: 2024-01-15T10:30:00Z
|
144
|
+
# 🕑 Updated: 2024-01-15T10:32:15Z
|
145
|
+
```
|
146
|
+
|
147
|
+
## Setup Guide
|
148
|
+
|
149
|
+
### Getting your Zapier API Key
|
150
|
+
|
151
|
+
1. Log into your Zapier account
|
152
|
+
2. Go to **Account Settings** → **Developer**
|
153
|
+
3. Click **Manage API Keys**
|
154
|
+
4. Click **Create API Key**
|
155
|
+
5. Give it a name (e.g., "WebAgent Integration")
|
156
|
+
6. Copy the generated API key
|
157
|
+
|
158
|
+
### Zapier API Limits
|
159
|
+
|
160
|
+
- **Free accounts**: 100 tasks/month
|
161
|
+
- **Paid accounts**: Based on your plan
|
162
|
+
- **Rate limits**: 1 request per second per API key
|
163
|
+
|
164
|
+
## Usage Examples
|
165
|
+
|
166
|
+
### Simple Zap Trigger
|
167
|
+
```python
|
168
|
+
# Setup (one-time)
|
169
|
+
await agent.call_tool("zapier_setup", api_key="your_api_key")
|
170
|
+
|
171
|
+
# Trigger a simple Zap
|
172
|
+
result = await agent.call_tool("zapier_trigger", zap_id="welcome_email_zap")
|
173
|
+
print(result) # ✅ Zap triggered successfully!
|
174
|
+
```
|
175
|
+
|
176
|
+
### Zap with Input Data
|
177
|
+
```python
|
178
|
+
# Trigger Zap with custom data
|
179
|
+
lead_data = {
|
180
|
+
"name": "John Smith",
|
181
|
+
"email": "john@company.com",
|
182
|
+
"company": "Acme Corp",
|
183
|
+
"source": "website_form"
|
184
|
+
}
|
185
|
+
|
186
|
+
result = await agent.call_tool("zapier_trigger",
|
187
|
+
zap_id="lead_processing_zap",
|
188
|
+
data=lead_data
|
189
|
+
)
|
190
|
+
|
191
|
+
# Monitor execution
|
192
|
+
task_id = "task_123" # Extract from result
|
193
|
+
status = await agent.call_tool("zapier_status", task_id=task_id)
|
194
|
+
print(status)
|
195
|
+
```
|
196
|
+
|
197
|
+
### Discover and Trigger Zaps
|
198
|
+
```python
|
199
|
+
# First, see what Zaps are available
|
200
|
+
zaps = await agent.call_tool("zapier_list_zaps")
|
201
|
+
print(zaps)
|
202
|
+
|
203
|
+
# Trigger a specific Zap
|
204
|
+
result = await agent.call_tool("zapier_trigger",
|
205
|
+
zap_id="data_sync_zap",
|
206
|
+
data={"sync_type": "full", "timestamp": "2024-01-15T10:00:00Z"}
|
207
|
+
)
|
208
|
+
```
|
209
|
+
|
210
|
+
### Error Handling Example
|
211
|
+
```python
|
212
|
+
try:
|
213
|
+
result = await agent.call_tool("zapier_trigger", zap_id="test_zap")
|
214
|
+
if "✅" in result:
|
215
|
+
print("Zap triggered successfully!")
|
216
|
+
elif "❌" in result:
|
217
|
+
print(f"Error: {result}")
|
218
|
+
except Exception as e:
|
219
|
+
print(f"Unexpected error: {e}")
|
220
|
+
```
|
221
|
+
|
222
|
+
## Common Use Cases
|
223
|
+
|
224
|
+
### 1. Lead Processing
|
225
|
+
```python
|
226
|
+
# New lead from website form
|
227
|
+
lead_info = {
|
228
|
+
"name": request.form["name"],
|
229
|
+
"email": request.form["email"],
|
230
|
+
"message": request.form["message"]
|
231
|
+
}
|
232
|
+
|
233
|
+
# Trigger Zap to add to CRM and send welcome email
|
234
|
+
await agent.call_tool("zapier_trigger",
|
235
|
+
zap_id="lead_processing",
|
236
|
+
data=lead_info
|
237
|
+
)
|
238
|
+
```
|
239
|
+
|
240
|
+
### 2. Order Fulfillment
|
241
|
+
```python
|
242
|
+
# New order processed
|
243
|
+
order_data = {
|
244
|
+
"order_id": "ORD-123",
|
245
|
+
"customer_email": "customer@example.com",
|
246
|
+
"items": [{"name": "Product A", "qty": 2}],
|
247
|
+
"total": 49.98
|
248
|
+
}
|
249
|
+
|
250
|
+
# Trigger fulfillment workflow
|
251
|
+
await agent.call_tool("zapier_trigger",
|
252
|
+
zap_id="order_fulfillment",
|
253
|
+
data=order_data
|
254
|
+
)
|
255
|
+
```
|
256
|
+
|
257
|
+
### 3. Data Synchronization
|
258
|
+
```python
|
259
|
+
# Sync data between systems
|
260
|
+
sync_data = {
|
261
|
+
"source": "database",
|
262
|
+
"destination": "crm",
|
263
|
+
"record_count": 150,
|
264
|
+
"sync_timestamp": datetime.now().isoformat()
|
265
|
+
}
|
266
|
+
|
267
|
+
await agent.call_tool("zapier_trigger",
|
268
|
+
zap_id="data_sync",
|
269
|
+
data=sync_data
|
270
|
+
)
|
271
|
+
```
|
272
|
+
|
273
|
+
## Error Handling
|
274
|
+
|
275
|
+
The skill provides clear error messages for common scenarios:
|
276
|
+
|
277
|
+
- **❌ Authentication required** - User not authenticated
|
278
|
+
- **❌ API key is required** - Empty or missing API key
|
279
|
+
- **❌ Invalid API key** - API key rejected by Zapier
|
280
|
+
- **❌ API key doesn't have required permissions** - Limited API key
|
281
|
+
- **❌ Zap not found** - Invalid Zap ID
|
282
|
+
- **❌ Permission denied** - Zap disabled or no access
|
283
|
+
- **❌ Task not found** - Invalid task ID
|
284
|
+
|
285
|
+
## Security
|
286
|
+
|
287
|
+
- **API keys** are stored securely using the KV skill with per-user namespacing
|
288
|
+
- **User isolation** ensures each user can only access their own credentials
|
289
|
+
- **Authentication required** for all operations
|
290
|
+
- **Automatic validation** of API keys during setup
|
291
|
+
- **Memory fallback** available if KV skill unavailable
|
292
|
+
|
293
|
+
## Architecture
|
294
|
+
|
295
|
+
```
|
296
|
+
WebAgent
|
297
|
+
├── Auth Skill (user context)
|
298
|
+
├── KV Skill (secure storage)
|
299
|
+
└── Zapier Skill
|
300
|
+
├── Credential Management
|
301
|
+
├── API Communication
|
302
|
+
└── Zap Operations
|
303
|
+
```
|
304
|
+
|
305
|
+
## Dependencies
|
306
|
+
|
307
|
+
- `auth` skill - For user authentication and context
|
308
|
+
- `kv` skill - For secure credential storage
|
309
|
+
- `httpx` - For HTTP API communication
|
310
|
+
|
311
|
+
## Troubleshooting
|
312
|
+
|
313
|
+
### "Authentication required"
|
314
|
+
Ensure your agent has proper authentication configured.
|
315
|
+
|
316
|
+
### "Invalid API key"
|
317
|
+
- Verify the API key is correct
|
318
|
+
- Check if the key has been revoked
|
319
|
+
- Ensure you copied the full key
|
320
|
+
|
321
|
+
### "API key doesn't have required permissions"
|
322
|
+
- Check if your Zapier plan supports API access
|
323
|
+
- Verify the API key has necessary permissions
|
324
|
+
- Contact Zapier support if issues persist
|
325
|
+
|
326
|
+
### "Zap not found"
|
327
|
+
- Use `zapier_list_zaps()` to see available Zaps
|
328
|
+
- Check if the Zap ID is correct
|
329
|
+
- Verify the Zap exists and is accessible
|
330
|
+
|
331
|
+
### "Permission denied"
|
332
|
+
- Check if the Zap is enabled/turned on
|
333
|
+
- Verify you have access to the Zap
|
334
|
+
- Ensure the Zap isn't paused or has errors
|
335
|
+
|
336
|
+
## Limitations
|
337
|
+
|
338
|
+
- Supports Zapier REST API v1 endpoints
|
339
|
+
- Limited to triggering existing Zaps (no Zap creation/modification)
|
340
|
+
- Rate limited by Zapier (1 request/second)
|
341
|
+
- Task status checking depends on Zapier's API availability
|
342
|
+
- Some Zap types may not support external triggering
|
343
|
+
|
344
|
+
## Zapier Integration Tips
|
345
|
+
|
346
|
+
1. **Test your Zaps** in Zapier before triggering via API
|
347
|
+
2. **Use descriptive Zap names** for easier identification
|
348
|
+
3. **Enable Zaps** before trying to trigger them
|
349
|
+
4. **Check Zap history** in Zapier for debugging
|
350
|
+
5. **Monitor task limits** to avoid hitting quotas
|
351
|
+
|
352
|
+
## Contributing
|
353
|
+
|
354
|
+
To extend this skill:
|
355
|
+
|
356
|
+
1. Add new tools following the existing pattern
|
357
|
+
2. Update tests in `tests/test_zapier_skill.py`
|
358
|
+
3. Update this documentation
|
359
|
+
4. Ensure proper error handling and user feedback
|
360
|
+
|
361
|
+
## License
|
362
|
+
|
363
|
+
Part of the WebAgents framework. See main project license.
|
@@ -0,0 +1,337 @@
|
|
1
|
+
"""
|
2
|
+
Minimalistic Zapier Skill for WebAgents
|
3
|
+
|
4
|
+
This skill allows users to:
|
5
|
+
- Set up Zapier API key securely (via auth/kv skills)
|
6
|
+
- Trigger Zapier workflows (Zaps)
|
7
|
+
- List available Zaps
|
8
|
+
- Get Zap execution status
|
9
|
+
|
10
|
+
Uses auth skill for user context and kv skill for secure API key storage.
|
11
|
+
"""
|
12
|
+
|
13
|
+
import os
|
14
|
+
import httpx
|
15
|
+
import json
|
16
|
+
from typing import Dict, Any, Optional, List
|
17
|
+
from datetime import datetime
|
18
|
+
|
19
|
+
from webagents.agents.skills.base import Skill
|
20
|
+
from webagents.agents.tools.decorators import tool, prompt
|
21
|
+
from webagents.server.context.context_vars import get_context
|
22
|
+
|
23
|
+
|
24
|
+
class ZapierSkill(Skill):
|
25
|
+
"""Minimalistic Zapier skill for workflow automation"""
|
26
|
+
|
27
|
+
def __init__(self):
|
28
|
+
super().__init__()
|
29
|
+
self.api_base = "https://api.zapier.com/v1"
|
30
|
+
|
31
|
+
def get_dependencies(self) -> List[str]:
|
32
|
+
"""Skill dependencies"""
|
33
|
+
return ['auth', 'kv']
|
34
|
+
|
35
|
+
@prompt(priority=40, scope=["owner", "all"])
|
36
|
+
def zapier_prompt(self) -> str:
|
37
|
+
"""Prompt describing Zapier capabilities"""
|
38
|
+
return """
|
39
|
+
Minimalistic Zapier integration for workflow automation. Available tools:
|
40
|
+
|
41
|
+
• zapier_setup(api_key) - Set up Zapier API credentials securely
|
42
|
+
• zapier_trigger(zap_id, data) - Trigger a specific Zap with optional input data
|
43
|
+
• zapier_list_zaps() - List all available Zaps in your Zapier account
|
44
|
+
• zapier_status(task_id) - Check the status of a Zap execution
|
45
|
+
|
46
|
+
Features:
|
47
|
+
- Secure API key storage via KV skill
|
48
|
+
- Per-user credential isolation via Auth skill
|
49
|
+
- Trigger Zaps with custom input data
|
50
|
+
- Monitor Zap execution status
|
51
|
+
- List and discover available Zaps
|
52
|
+
|
53
|
+
Setup: First run zapier_setup() with your Zapier API key from your account settings.
|
54
|
+
"""
|
55
|
+
|
56
|
+
# Helper methods for auth and kv skills
|
57
|
+
async def _get_auth_skill(self):
|
58
|
+
"""Get auth skill for user context"""
|
59
|
+
return self.agent.skills.get('auth')
|
60
|
+
|
61
|
+
async def _get_kv_skill(self):
|
62
|
+
"""Get KV skill for secure storage"""
|
63
|
+
return self.agent.skills.get('kv')
|
64
|
+
|
65
|
+
async def _get_authenticated_user_id(self) -> Optional[str]:
|
66
|
+
"""Get authenticated user ID from context"""
|
67
|
+
try:
|
68
|
+
context = get_context()
|
69
|
+
if context and context.auth and context.auth.authenticated:
|
70
|
+
return context.auth.user_id
|
71
|
+
return None
|
72
|
+
except Exception as e:
|
73
|
+
self.logger.error(f"Failed to get user context: {e}")
|
74
|
+
return None
|
75
|
+
|
76
|
+
async def _save_zapier_credentials(self, user_id: str, api_key: str) -> bool:
|
77
|
+
"""Save Zapier credentials securely using KV skill"""
|
78
|
+
try:
|
79
|
+
kv_skill = await self._get_kv_skill()
|
80
|
+
if kv_skill:
|
81
|
+
credentials = {
|
82
|
+
'api_key': api_key,
|
83
|
+
'created_at': datetime.now().isoformat()
|
84
|
+
}
|
85
|
+
await kv_skill.kv_set(
|
86
|
+
key='credentials',
|
87
|
+
value=json.dumps(credentials),
|
88
|
+
namespace=f'zapier:{user_id}'
|
89
|
+
)
|
90
|
+
return True
|
91
|
+
else:
|
92
|
+
# Fallback to in-memory storage
|
93
|
+
if not hasattr(self.agent, '_zapier_credentials'):
|
94
|
+
self.agent._zapier_credentials = {}
|
95
|
+
self.agent._zapier_credentials[user_id] = {
|
96
|
+
'api_key': api_key
|
97
|
+
}
|
98
|
+
return True
|
99
|
+
except Exception as e:
|
100
|
+
self.logger.error(f"Failed to save Zapier credentials: {e}")
|
101
|
+
return False
|
102
|
+
|
103
|
+
async def _load_zapier_credentials(self, user_id: str) -> Optional[Dict[str, str]]:
|
104
|
+
"""Load Zapier credentials from KV skill"""
|
105
|
+
try:
|
106
|
+
kv_skill = await self._get_kv_skill()
|
107
|
+
if kv_skill:
|
108
|
+
credentials_json = await kv_skill.kv_get(
|
109
|
+
key='credentials',
|
110
|
+
namespace=f'zapier:{user_id}'
|
111
|
+
)
|
112
|
+
if credentials_json:
|
113
|
+
return json.loads(credentials_json)
|
114
|
+
else:
|
115
|
+
# Fallback to in-memory storage
|
116
|
+
if hasattr(self.agent, '_zapier_credentials'):
|
117
|
+
return self.agent._zapier_credentials.get(user_id)
|
118
|
+
return None
|
119
|
+
except Exception as e:
|
120
|
+
self.logger.error(f"Failed to load Zapier credentials: {e}")
|
121
|
+
return None
|
122
|
+
|
123
|
+
async def _make_zapier_request(self, method: str, endpoint: str, data: Optional[Dict] = None, user_id: str = None) -> Dict[str, Any]:
|
124
|
+
"""Make authenticated request to Zapier API"""
|
125
|
+
if not user_id:
|
126
|
+
user_id = await self._get_authenticated_user_id()
|
127
|
+
if not user_id:
|
128
|
+
raise Exception("Authentication required")
|
129
|
+
|
130
|
+
credentials = await self._load_zapier_credentials(user_id)
|
131
|
+
if not credentials:
|
132
|
+
raise Exception("Zapier credentials not found. Please run zapier_setup() first.")
|
133
|
+
|
134
|
+
api_key = credentials['api_key']
|
135
|
+
|
136
|
+
headers = {
|
137
|
+
'X-API-Key': api_key,
|
138
|
+
'Content-Type': 'application/json'
|
139
|
+
}
|
140
|
+
|
141
|
+
url = f"{self.api_base}{endpoint}"
|
142
|
+
|
143
|
+
async with httpx.AsyncClient() as client:
|
144
|
+
if method.upper() == 'GET':
|
145
|
+
response = await client.get(url, headers=headers)
|
146
|
+
elif method.upper() == 'POST':
|
147
|
+
response = await client.post(url, headers=headers, json=data)
|
148
|
+
elif method.upper() == 'PUT':
|
149
|
+
response = await client.put(url, headers=headers, json=data)
|
150
|
+
elif method.upper() == 'DELETE':
|
151
|
+
response = await client.delete(url, headers=headers)
|
152
|
+
else:
|
153
|
+
raise Exception(f"Unsupported HTTP method: {method}")
|
154
|
+
|
155
|
+
response.raise_for_status()
|
156
|
+
return response.json() if response.content else {}
|
157
|
+
|
158
|
+
# Public tools
|
159
|
+
@tool(description="Set up Zapier API credentials securely. Get your API key from Zapier account settings.", scope="owner")
|
160
|
+
async def zapier_setup(self, api_key: str) -> str:
|
161
|
+
"""Set up Zapier API credentials for secure access"""
|
162
|
+
user_id = await self._get_authenticated_user_id()
|
163
|
+
if not user_id:
|
164
|
+
return "❌ Authentication required"
|
165
|
+
|
166
|
+
if not api_key or not api_key.strip():
|
167
|
+
return "❌ API key is required. Get one from your Zapier account settings."
|
168
|
+
|
169
|
+
try:
|
170
|
+
# Test the API key by making a simple request
|
171
|
+
test_response = await self._make_zapier_request('GET', '/zaps', user_id=user_id)
|
172
|
+
|
173
|
+
# If test succeeds, save credentials
|
174
|
+
success = await self._save_zapier_credentials(user_id, api_key.strip())
|
175
|
+
|
176
|
+
if success:
|
177
|
+
zap_count = len(test_response.get('data', []))
|
178
|
+
return f"✅ Zapier credentials saved successfully!\n🔑 API key configured\n📊 Found {zap_count} Zaps in your account"
|
179
|
+
else:
|
180
|
+
return "❌ Failed to save credentials"
|
181
|
+
|
182
|
+
except httpx.HTTPStatusError as e:
|
183
|
+
if e.response.status_code == 401:
|
184
|
+
return "❌ Invalid API key. Please check your Zapier API key."
|
185
|
+
elif e.response.status_code == 403:
|
186
|
+
return "❌ API key doesn't have required permissions."
|
187
|
+
else:
|
188
|
+
return f"❌ API test failed: HTTP {e.response.status_code}"
|
189
|
+
except Exception as e:
|
190
|
+
return f"❌ Setup failed: {str(e)}"
|
191
|
+
|
192
|
+
@tool(description="Trigger a Zapier Zap with optional input data")
|
193
|
+
async def zapier_trigger(self, zap_id: str, data: Dict[str, Any] = None) -> str:
|
194
|
+
"""Trigger a Zapier Zap with optional input data"""
|
195
|
+
user_id = await self._get_authenticated_user_id()
|
196
|
+
if not user_id:
|
197
|
+
return "❌ Authentication required"
|
198
|
+
|
199
|
+
if not zap_id or not zap_id.strip():
|
200
|
+
return "❌ Zap ID is required"
|
201
|
+
|
202
|
+
try:
|
203
|
+
# Prepare trigger data
|
204
|
+
trigger_data = data or {}
|
205
|
+
|
206
|
+
# Trigger the Zap
|
207
|
+
response = await self._make_zapier_request(
|
208
|
+
'POST',
|
209
|
+
f'/zaps/{zap_id.strip()}/trigger',
|
210
|
+
trigger_data,
|
211
|
+
user_id
|
212
|
+
)
|
213
|
+
|
214
|
+
task_id = response.get('id', 'unknown')
|
215
|
+
status = response.get('status', 'triggered')
|
216
|
+
|
217
|
+
return f"✅ Zap triggered successfully!\n📋 Task ID: {task_id}\n📊 Status: {status}"
|
218
|
+
|
219
|
+
except httpx.HTTPStatusError as e:
|
220
|
+
if e.response.status_code == 401:
|
221
|
+
return "❌ Authentication failed. Please run zapier_setup() again."
|
222
|
+
elif e.response.status_code == 404:
|
223
|
+
return f"❌ Zap '{zap_id}' not found"
|
224
|
+
elif e.response.status_code == 403:
|
225
|
+
return "❌ Permission denied. Check if Zap is enabled and you have access."
|
226
|
+
else:
|
227
|
+
return f"❌ Trigger failed: HTTP {e.response.status_code}"
|
228
|
+
except Exception as e:
|
229
|
+
return f"❌ Error triggering Zap: {str(e)}"
|
230
|
+
|
231
|
+
@tool(description="List all available Zaps in your Zapier account")
|
232
|
+
async def zapier_list_zaps(self) -> str:
|
233
|
+
"""List all available Zaps"""
|
234
|
+
user_id = await self._get_authenticated_user_id()
|
235
|
+
if not user_id:
|
236
|
+
return "❌ Authentication required"
|
237
|
+
|
238
|
+
try:
|
239
|
+
response = await self._make_zapier_request('GET', '/zaps', user_id=user_id)
|
240
|
+
|
241
|
+
zaps = response.get('data', [])
|
242
|
+
|
243
|
+
if not zaps:
|
244
|
+
return "📭 No Zaps found in your Zapier account"
|
245
|
+
|
246
|
+
result = ["📋 Available Zapier Zaps:\n"]
|
247
|
+
|
248
|
+
for zap in zaps:
|
249
|
+
zap_id = zap.get('id', 'unknown')
|
250
|
+
title = zap.get('title', 'Untitled Zap')
|
251
|
+
status = zap.get('status', 'unknown')
|
252
|
+
|
253
|
+
# Status icons
|
254
|
+
status_icon = "🟢" if status == 'on' else "🔴" if status == 'off' else "⚠️"
|
255
|
+
|
256
|
+
result.append(f"{status_icon} **{title}** (ID: {zap_id})")
|
257
|
+
result.append(f" 📊 Status: {status}")
|
258
|
+
|
259
|
+
# Add trigger and action info if available
|
260
|
+
steps = zap.get('steps', [])
|
261
|
+
if steps:
|
262
|
+
trigger_app = steps[0].get('app', {}).get('title', 'Unknown') if steps else 'Unknown'
|
263
|
+
result.append(f" 🔗 Trigger: {trigger_app}")
|
264
|
+
|
265
|
+
result.append("") # Empty line for spacing
|
266
|
+
|
267
|
+
result.append("💡 Use zapier_trigger(zap_id, data) to trigger a Zap")
|
268
|
+
|
269
|
+
return "\n".join(result)
|
270
|
+
|
271
|
+
except httpx.HTTPStatusError as e:
|
272
|
+
if e.response.status_code == 401:
|
273
|
+
return "❌ Authentication failed. Please run zapier_setup() again."
|
274
|
+
else:
|
275
|
+
return f"❌ Failed to list Zaps: HTTP {e.response.status_code}"
|
276
|
+
except Exception as e:
|
277
|
+
return f"❌ Error listing Zaps: {str(e)}"
|
278
|
+
|
279
|
+
@tool(description="Check the status of a Zap execution")
|
280
|
+
async def zapier_status(self, task_id: str) -> str:
|
281
|
+
"""Check the status of a Zap execution"""
|
282
|
+
user_id = await self._get_authenticated_user_id()
|
283
|
+
if not user_id:
|
284
|
+
return "❌ Authentication required"
|
285
|
+
|
286
|
+
if not task_id or not task_id.strip():
|
287
|
+
return "❌ Task ID is required"
|
288
|
+
|
289
|
+
try:
|
290
|
+
response = await self._make_zapier_request(
|
291
|
+
'GET',
|
292
|
+
f'/tasks/{task_id.strip()}',
|
293
|
+
user_id=user_id
|
294
|
+
)
|
295
|
+
|
296
|
+
status = response.get('status', 'unknown')
|
297
|
+
zap_id = response.get('zap_id', 'unknown')
|
298
|
+
created_at = response.get('created_at', 'unknown')
|
299
|
+
updated_at = response.get('updated_at', 'running')
|
300
|
+
|
301
|
+
# Status icons
|
302
|
+
status_icons = {
|
303
|
+
'success': '✅',
|
304
|
+
'error': '❌',
|
305
|
+
'running': '🔄',
|
306
|
+
'waiting': '⏳',
|
307
|
+
'cancelled': '🚫',
|
308
|
+
'throttled': '🐌'
|
309
|
+
}
|
310
|
+
|
311
|
+
status_icon = status_icons.get(status.lower(), '❓')
|
312
|
+
|
313
|
+
result = [
|
314
|
+
f"📊 Zap Execution Status Report",
|
315
|
+
f"🆔 Task ID: {task_id}",
|
316
|
+
f"🔧 Zap ID: {zap_id}",
|
317
|
+
f"{status_icon} Status: {status}",
|
318
|
+
f"🕐 Created: {created_at}",
|
319
|
+
f"🕑 Updated: {updated_at}"
|
320
|
+
]
|
321
|
+
|
322
|
+
# Add error details if execution failed
|
323
|
+
if status.lower() == 'error' and 'error' in response:
|
324
|
+
error_msg = response.get('error', 'Unknown error')
|
325
|
+
result.append(f"❌ Error: {error_msg}")
|
326
|
+
|
327
|
+
return "\n".join(result)
|
328
|
+
|
329
|
+
except httpx.HTTPStatusError as e:
|
330
|
+
if e.response.status_code == 401:
|
331
|
+
return "❌ Authentication failed. Please run zapier_setup() again."
|
332
|
+
elif e.response.status_code == 404:
|
333
|
+
return f"❌ Task '{task_id}' not found"
|
334
|
+
else:
|
335
|
+
return f"❌ Status check failed: HTTP {e.response.status_code}"
|
336
|
+
except Exception as e:
|
337
|
+
return f"❌ Error checking status: {str(e)}"
|