deadsimple-email 0.1.0__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.
- deadsimple/__init__.py +45 -0
- deadsimple/__main__.py +5 -0
- deadsimple/cli.py +287 -0
- deadsimple/client.py +952 -0
- deadsimple/exceptions.py +37 -0
- deadsimple/integrations/__init__.py +1 -0
- deadsimple/integrations/autogen.py +141 -0
- deadsimple/integrations/crewai.py +121 -0
- deadsimple/integrations/langchain.py +177 -0
- deadsimple/integrations/llamaindex.py +166 -0
- deadsimple/integrations/openai_agents.py +254 -0
- deadsimple/mcp.py +283 -0
- deadsimple/py.typed +0 -0
- deadsimple/types.py +427 -0
- deadsimple/webhooks.py +84 -0
- deadsimple_email-0.1.0.dist-info/METADATA +246 -0
- deadsimple_email-0.1.0.dist-info/RECORD +20 -0
- deadsimple_email-0.1.0.dist-info/WHEEL +5 -0
- deadsimple_email-0.1.0.dist-info/entry_points.txt +3 -0
- deadsimple_email-0.1.0.dist-info/top_level.txt +1 -0
deadsimple/__init__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Dead Simple Email — Python SDK for AI agents.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
from deadsimple import DeadSimple
|
|
6
|
+
|
|
7
|
+
client = DeadSimple("dse_your_api_key")
|
|
8
|
+
inbox = client.inboxes.create(display_name="My Agent")
|
|
9
|
+
client.messages.send(
|
|
10
|
+
inbox_id=inbox.inbox_id,
|
|
11
|
+
to="user@example.com",
|
|
12
|
+
subject="Hello",
|
|
13
|
+
text_body="Sent by an AI agent.",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
Async usage::
|
|
17
|
+
|
|
18
|
+
from deadsimple import AsyncDeadSimple
|
|
19
|
+
|
|
20
|
+
async with AsyncDeadSimple("dse_your_api_key") as client:
|
|
21
|
+
inbox = await client.inboxes.create(display_name="My Agent")
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from deadsimple.client import AsyncDeadSimple, DeadSimple, WebSocketStream
|
|
25
|
+
from deadsimple.exceptions import (
|
|
26
|
+
AuthenticationError,
|
|
27
|
+
DeadSimpleError,
|
|
28
|
+
NotFoundError,
|
|
29
|
+
RateLimitError,
|
|
30
|
+
ValidationError,
|
|
31
|
+
)
|
|
32
|
+
from deadsimple.types import WebhookEvent
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
__all__ = [
|
|
36
|
+
"DeadSimple",
|
|
37
|
+
"AsyncDeadSimple",
|
|
38
|
+
"WebSocketStream",
|
|
39
|
+
"DeadSimpleError",
|
|
40
|
+
"AuthenticationError",
|
|
41
|
+
"NotFoundError",
|
|
42
|
+
"RateLimitError",
|
|
43
|
+
"ValidationError",
|
|
44
|
+
"WebhookEvent",
|
|
45
|
+
]
|
deadsimple/__main__.py
ADDED
deadsimple/cli.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Dead Simple Email CLI.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
dse inboxes list
|
|
5
|
+
dse inboxes create --name "My Bot"
|
|
6
|
+
dse inboxes delete <inbox_id>
|
|
7
|
+
dse send --inbox <inbox_id> --to user@example.com --subject "Hello" --body "Hi there"
|
|
8
|
+
dse messages list <inbox_id>
|
|
9
|
+
dse messages get <inbox_id> <message_id>
|
|
10
|
+
dse webhooks list
|
|
11
|
+
dse webhooks create --url https://example.com/hook --events message.received
|
|
12
|
+
dse usage
|
|
13
|
+
dse templates list
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
from typing import NoReturn
|
|
21
|
+
|
|
22
|
+
from deadsimple.client import DeadSimple
|
|
23
|
+
from deadsimple.exceptions import DeadSimpleError
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_key() -> str:
|
|
27
|
+
key = os.environ.get("DSE_API_KEY", "")
|
|
28
|
+
if not key:
|
|
29
|
+
print("Error: DSE_API_KEY environment variable is required.", file=sys.stderr)
|
|
30
|
+
print(" export DSE_API_KEY=dse_your_api_key", file=sys.stderr)
|
|
31
|
+
sys.exit(1)
|
|
32
|
+
return key
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _client() -> DeadSimple:
|
|
36
|
+
return DeadSimple(_get_key())
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _print_json(data: object) -> None:
|
|
40
|
+
"""Pretty-print as JSON."""
|
|
41
|
+
if hasattr(data, "__dataclass_fields__"):
|
|
42
|
+
import dataclasses
|
|
43
|
+
data = dataclasses.asdict(data) # type: ignore[arg-type]
|
|
44
|
+
print(json.dumps(data, indent=2, default=str))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _error(msg: str) -> NoReturn:
|
|
48
|
+
print(f"Error: {msg}", file=sys.stderr)
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ── Commands ──────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def cmd_inboxes(args: list[str]) -> None:
|
|
56
|
+
if not args:
|
|
57
|
+
_error("Usage: dse inboxes [list|create|get|delete]")
|
|
58
|
+
|
|
59
|
+
sub = args[0]
|
|
60
|
+
client = _client()
|
|
61
|
+
|
|
62
|
+
if sub == "list":
|
|
63
|
+
result = client.inboxes.list()
|
|
64
|
+
for inbox in result.inboxes:
|
|
65
|
+
status = "●" if inbox.status == "active" else "○"
|
|
66
|
+
print(f" {status} {inbox.email:<40} {inbox.display_name or '(unnamed)':<20} {inbox.message_count} msgs")
|
|
67
|
+
print(f"\n{result.count} inbox(es)")
|
|
68
|
+
|
|
69
|
+
elif sub == "create":
|
|
70
|
+
name = _flag(args, "--name", "")
|
|
71
|
+
inbox = client.inboxes.create(display_name=name)
|
|
72
|
+
print(f"Created: {inbox.email}")
|
|
73
|
+
print(f" ID: {inbox.inbox_id}")
|
|
74
|
+
|
|
75
|
+
elif sub == "get":
|
|
76
|
+
if len(args) < 2:
|
|
77
|
+
_error("Usage: dse inboxes get <inbox_id>")
|
|
78
|
+
inbox = client.inboxes.get(args[1])
|
|
79
|
+
_print_json(inbox)
|
|
80
|
+
|
|
81
|
+
elif sub == "delete":
|
|
82
|
+
if len(args) < 2:
|
|
83
|
+
_error("Usage: dse inboxes delete <inbox_id>")
|
|
84
|
+
client.inboxes.delete(args[1])
|
|
85
|
+
print(f"Deleted: {args[1]}")
|
|
86
|
+
|
|
87
|
+
else:
|
|
88
|
+
_error(f"Unknown subcommand: {sub}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def cmd_send(args: list[str]) -> None:
|
|
92
|
+
inbox_id = _flag(args, "--inbox", "")
|
|
93
|
+
to = _flag(args, "--to", "")
|
|
94
|
+
subject = _flag(args, "--subject", "")
|
|
95
|
+
body = _flag(args, "--body", "")
|
|
96
|
+
|
|
97
|
+
if not inbox_id or not to or not subject:
|
|
98
|
+
_error("Usage: dse send --inbox <id> --to <email> --subject <subject> --body <body>")
|
|
99
|
+
|
|
100
|
+
client = _client()
|
|
101
|
+
result = client.messages.send(inbox_id=inbox_id, to=to, subject=subject, text_body=body or "(no body)")
|
|
102
|
+
print(f"Sent: {result.message_id} (status: {result.status})")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def cmd_messages(args: list[str]) -> None:
|
|
106
|
+
if not args:
|
|
107
|
+
_error("Usage: dse messages [list|get]")
|
|
108
|
+
|
|
109
|
+
sub = args[0]
|
|
110
|
+
client = _client()
|
|
111
|
+
|
|
112
|
+
if sub == "list":
|
|
113
|
+
if len(args) < 2:
|
|
114
|
+
_error("Usage: dse messages list <inbox_id>")
|
|
115
|
+
result = client.messages.list(args[1], limit=20)
|
|
116
|
+
for msg in result.messages:
|
|
117
|
+
direction = "←" if msg.direction == "inbound" else "→"
|
|
118
|
+
print(f" {direction} {msg.from_email:<35} {msg.subject:<40} {msg.message_id[:12]}...")
|
|
119
|
+
print(f"\n{result.count} message(s)")
|
|
120
|
+
|
|
121
|
+
elif sub == "get":
|
|
122
|
+
if len(args) < 3:
|
|
123
|
+
_error("Usage: dse messages get <inbox_id> <message_id>")
|
|
124
|
+
msg = client.messages.get(args[1], args[2])
|
|
125
|
+
print(f"From: {msg.from_name or msg.from_email}")
|
|
126
|
+
print(f"To: {', '.join(msg.to)}")
|
|
127
|
+
print(f"Subject: {msg.subject}")
|
|
128
|
+
print(f"Date: {msg.created_at or msg.received_at}")
|
|
129
|
+
print("---")
|
|
130
|
+
print(msg.latest_reply or msg.text_body or "(no text body)")
|
|
131
|
+
|
|
132
|
+
else:
|
|
133
|
+
_error(f"Unknown subcommand: {sub}")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def cmd_webhooks(args: list[str]) -> None:
|
|
137
|
+
if not args:
|
|
138
|
+
_error("Usage: dse webhooks [list|create|delete]")
|
|
139
|
+
|
|
140
|
+
sub = args[0]
|
|
141
|
+
client = _client()
|
|
142
|
+
|
|
143
|
+
if sub == "list":
|
|
144
|
+
result = client.webhooks.list()
|
|
145
|
+
for wh in result.webhooks:
|
|
146
|
+
status = "●" if wh.is_active else "○"
|
|
147
|
+
events = ", ".join(wh.events)
|
|
148
|
+
print(f" {status} {wh.url:<50} [{events}] {wh.webhook_id[:12]}...")
|
|
149
|
+
print(f"\n{result.count} webhook(s)")
|
|
150
|
+
|
|
151
|
+
elif sub == "create":
|
|
152
|
+
url = _flag(args, "--url", "")
|
|
153
|
+
events_str = _flag(args, "--events", "message.received")
|
|
154
|
+
if not url:
|
|
155
|
+
_error("Usage: dse webhooks create --url <url> [--events event1,event2]")
|
|
156
|
+
events = [e.strip() for e in events_str.split(",")]
|
|
157
|
+
wh = client.webhooks.create(url=url, events=events)
|
|
158
|
+
print(f"Created webhook: {wh.webhook_id}")
|
|
159
|
+
if wh.signing_secret:
|
|
160
|
+
print(f" Signing secret: {wh.signing_secret}")
|
|
161
|
+
print(" (Save this — it won't be shown again)")
|
|
162
|
+
|
|
163
|
+
elif sub == "delete":
|
|
164
|
+
if len(args) < 2:
|
|
165
|
+
_error("Usage: dse webhooks delete <webhook_id>")
|
|
166
|
+
client.webhooks.delete(args[1])
|
|
167
|
+
print(f"Deleted: {args[1]}")
|
|
168
|
+
|
|
169
|
+
else:
|
|
170
|
+
_error(f"Unknown subcommand: {sub}")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def cmd_usage(args: list[str]) -> None:
|
|
174
|
+
client = _client()
|
|
175
|
+
usage = client.usage.get()
|
|
176
|
+
print(f"Plan: {usage.plan_name}")
|
|
177
|
+
print(f"Inboxes: {usage.inboxes.get('used', 0)} / {usage.inboxes.get('limit', 0)}")
|
|
178
|
+
print(f"Emails: {usage.emails.get('sent_this_month', 0) + usage.emails.get('received_this_month', 0)} / {usage.emails.get('monthly_limit', 0)} this month")
|
|
179
|
+
print(f" Sent today: {usage.emails.get('sent_today', 0)}")
|
|
180
|
+
print(f" Received today: {usage.emails.get('received_today', 0)}")
|
|
181
|
+
print(f"Webhooks: {usage.webhooks}")
|
|
182
|
+
print(f"Domains: {usage.custom_domains.get('used', 0)} / {usage.custom_domains.get('limit', 0)}")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def cmd_templates(args: list[str]) -> None:
|
|
186
|
+
if not args:
|
|
187
|
+
_error("Usage: dse templates [list|create|get|delete]")
|
|
188
|
+
|
|
189
|
+
sub = args[0]
|
|
190
|
+
client = _client()
|
|
191
|
+
|
|
192
|
+
if sub == "list":
|
|
193
|
+
result = client.templates.list()
|
|
194
|
+
for t in result.get("templates", []):
|
|
195
|
+
vars_str = ", ".join(t.get("variables", []))
|
|
196
|
+
print(f" {t['name']:<30} vars: {vars_str or '(none)':<30} {t['template_id'][:12]}...")
|
|
197
|
+
print(f"\n{result.get('count', 0)} template(s)")
|
|
198
|
+
|
|
199
|
+
elif sub == "create":
|
|
200
|
+
name = _flag(args, "--name", "")
|
|
201
|
+
subject = _flag(args, "--subject", "")
|
|
202
|
+
body = _flag(args, "--body", "")
|
|
203
|
+
if not name:
|
|
204
|
+
_error("Usage: dse templates create --name <name> --subject <subject> --body <body>")
|
|
205
|
+
result = client.templates.create(name, subject=subject, text_body=body)
|
|
206
|
+
print(f"Created template: {result.get('template_id')}")
|
|
207
|
+
if result.get("variables"):
|
|
208
|
+
print(f" Variables: {', '.join(result['variables'])}")
|
|
209
|
+
|
|
210
|
+
elif sub == "delete":
|
|
211
|
+
if len(args) < 2:
|
|
212
|
+
_error("Usage: dse templates delete <template_id>")
|
|
213
|
+
client.templates.delete(args[1])
|
|
214
|
+
print(f"Deleted: {args[1]}")
|
|
215
|
+
|
|
216
|
+
else:
|
|
217
|
+
_error(f"Unknown subcommand: {sub}")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _flag(args: list[str], name: str, default: str) -> str:
|
|
224
|
+
"""Extract --flag value from args list."""
|
|
225
|
+
for i, arg in enumerate(args):
|
|
226
|
+
if arg == name and i + 1 < len(args):
|
|
227
|
+
return args[i + 1]
|
|
228
|
+
return default
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
COMMANDS = {
|
|
232
|
+
"inboxes": cmd_inboxes,
|
|
233
|
+
"send": cmd_send,
|
|
234
|
+
"messages": cmd_messages,
|
|
235
|
+
"webhooks": cmd_webhooks,
|
|
236
|
+
"usage": cmd_usage,
|
|
237
|
+
"templates": cmd_templates,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def main() -> None:
|
|
242
|
+
args = sys.argv[1:]
|
|
243
|
+
|
|
244
|
+
if not args or args[0] in ("-h", "--help", "help"):
|
|
245
|
+
print("Dead Simple Email CLI")
|
|
246
|
+
print()
|
|
247
|
+
print("Usage: dse <command> [args]")
|
|
248
|
+
print()
|
|
249
|
+
print("Commands:")
|
|
250
|
+
print(" inboxes Manage inboxes (list, create, get, delete)")
|
|
251
|
+
print(" send Send an email")
|
|
252
|
+
print(" messages Read messages (list, get)")
|
|
253
|
+
print(" webhooks Manage webhooks (list, create, delete)")
|
|
254
|
+
print(" templates Manage email templates (list, create, delete)")
|
|
255
|
+
print(" usage Show account usage metrics")
|
|
256
|
+
print()
|
|
257
|
+
print("Environment:")
|
|
258
|
+
print(" DSE_API_KEY Your API key (required)")
|
|
259
|
+
print()
|
|
260
|
+
print("Examples:")
|
|
261
|
+
print(' dse inboxes create --name "Support Bot"')
|
|
262
|
+
print(" dse send --inbox inb_xxx --to user@example.com --subject Hi --body Hello")
|
|
263
|
+
print(" dse messages list inb_xxx")
|
|
264
|
+
print(" dse usage")
|
|
265
|
+
sys.exit(0)
|
|
266
|
+
|
|
267
|
+
cmd_name = args[0]
|
|
268
|
+
cmd_args = args[1:]
|
|
269
|
+
|
|
270
|
+
handler = COMMANDS.get(cmd_name)
|
|
271
|
+
if not handler:
|
|
272
|
+
_error(f"Unknown command: {cmd_name}. Run 'dse --help' for usage.")
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
handler(cmd_args)
|
|
276
|
+
except DeadSimpleError as e:
|
|
277
|
+
print(f"API Error: {e.message}", file=sys.stderr)
|
|
278
|
+
if e.details:
|
|
279
|
+
for d in e.details:
|
|
280
|
+
print(f" {d.get('field', '?')}: {d.get('message', '')}", file=sys.stderr)
|
|
281
|
+
sys.exit(1)
|
|
282
|
+
except KeyboardInterrupt:
|
|
283
|
+
sys.exit(130)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
if __name__ == "__main__":
|
|
287
|
+
main()
|