sendora 2.2.0__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sendora-2.2.0 → sendora-2.3.0}/CHANGELOG.md +7 -0
- {sendora-2.2.0 → sendora-2.3.0}/PKG-INFO +6 -3
- {sendora-2.2.0 → sendora-2.3.0}/README.md +5 -2
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/__init__.py +4 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_version.py +1 -1
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_webhooks.py +4 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/webhooks.py +4 -2
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/types.py +89 -1
- {sendora-2.2.0 → sendora-2.3.0}/.gitignore +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/LICENSE +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/pyproject.toml +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_account.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_answers.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_client.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_errors.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_json.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_keys.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_model.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_options.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_pages.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_retry.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_transport.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/_wire.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/py.typed +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/__init__.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/broadcasts.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/domains.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/email.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/erasures.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/inbound.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/inbound_domains.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/messages.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/server.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/servers.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/streams.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/suppressions.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/tokens.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/resources/tracking_domains.py +0 -0
- {sendora-2.2.0 → sendora-2.3.0}/src/sendora/skills/sendora/SKILL.md +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.0
|
|
4
|
+
|
|
5
|
+
- Two events, `OpenedEvent` and `ClickedEvent`, for `opened` and `clicked`:
|
|
6
|
+
a tracked copy's pixel loaded, or a tracked link was followed. A webhook
|
|
7
|
+
gets them only if it names them, so `webhooks.create` without `events`
|
|
8
|
+
takes every other event.
|
|
9
|
+
|
|
3
10
|
## 2.2.0
|
|
4
11
|
|
|
5
12
|
- Each `MessageRecipient` has `tracked`, `opens`, `clicks`, `first_open_at`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sendora
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: The Python client for Sendora's email API.
|
|
5
5
|
Project-URL: Homepage, https://sendora.se/docs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -310,7 +310,9 @@ Events: `delivered`, `bounced`, `deferred`, `spam_complaint` and
|
|
|
310
310
|
`unsubscribed` about a message, with `message_id`, `stream_id`,
|
|
311
311
|
`recipient`, `occurred_at`, `server_id`, your `tag` and `metadata`, and
|
|
312
312
|
`details` from the receiver, or for `unsubscribed` the `source` (`link` or
|
|
313
|
-
`one_click`); `
|
|
313
|
+
`one_click`); `opened` and `clicked` about a tracked copy, sent only to a
|
|
314
|
+
webhook that names them, with the same fields and `details` that say whether
|
|
315
|
+
a machine did it and, for `clicked`, give the `link`; `cap_warning` and `cap_reached` about usage, with `scope`,
|
|
314
316
|
`cap`, `used` and the period; `inbound` about a message received on an
|
|
315
317
|
inbound stream, with `inbound_message_id`, `envelope_recipient`, the
|
|
316
318
|
sizes, the `authentication` verdicts and, for a webhook with
|
|
@@ -318,7 +320,8 @@ sizes, the `authentication` verdicts and, for a webhook with
|
|
|
318
320
|
`to`, `subject`, `text`, `html`, `attachments` and the rest.
|
|
319
321
|
`inbound_cap_warning` and `inbound_cap_reached` say that received mail
|
|
320
322
|
nears or reaches the account's monthly cap, with `cap`, `used` and the
|
|
321
|
-
period. Every event carries `id`, the delivery id, and `attempt`.
|
|
323
|
+
period. Every event carries `id`, the delivery id, and `attempt`. Tracking is
|
|
324
|
+
not open to accounts yet, so `opened` and `clicked` are not sent until it is.
|
|
322
325
|
|
|
323
326
|
A failed delivery is retried with growing delays for about a day, then it
|
|
324
327
|
is `dead` until you replay it. Deliveries arrive at least once and not
|
|
@@ -288,7 +288,9 @@ Events: `delivered`, `bounced`, `deferred`, `spam_complaint` and
|
|
|
288
288
|
`unsubscribed` about a message, with `message_id`, `stream_id`,
|
|
289
289
|
`recipient`, `occurred_at`, `server_id`, your `tag` and `metadata`, and
|
|
290
290
|
`details` from the receiver, or for `unsubscribed` the `source` (`link` or
|
|
291
|
-
`one_click`); `
|
|
291
|
+
`one_click`); `opened` and `clicked` about a tracked copy, sent only to a
|
|
292
|
+
webhook that names them, with the same fields and `details` that say whether
|
|
293
|
+
a machine did it and, for `clicked`, give the `link`; `cap_warning` and `cap_reached` about usage, with `scope`,
|
|
292
294
|
`cap`, `used` and the period; `inbound` about a message received on an
|
|
293
295
|
inbound stream, with `inbound_message_id`, `envelope_recipient`, the
|
|
294
296
|
sizes, the `authentication` verdicts and, for a webhook with
|
|
@@ -296,7 +298,8 @@ sizes, the `authentication` verdicts and, for a webhook with
|
|
|
296
298
|
`to`, `subject`, `text`, `html`, `attachments` and the rest.
|
|
297
299
|
`inbound_cap_warning` and `inbound_cap_reached` say that received mail
|
|
298
300
|
nears or reaches the account's monthly cap, with `cap`, `used` and the
|
|
299
|
-
period. Every event carries `id`, the delivery id, and `attempt`.
|
|
301
|
+
period. Every event carries `id`, the delivery id, and `attempt`. Tracking is
|
|
302
|
+
not open to accounts yet, so `opened` and `clicked` are not sent until it is.
|
|
300
303
|
|
|
301
304
|
A failed delivery is retried with growing delays for about a day, then it
|
|
302
305
|
is `dead` until you replay it. Deliveries arrive at least once and not
|
|
@@ -33,11 +33,13 @@ from .types import (
|
|
|
33
33
|
BouncedEvent,
|
|
34
34
|
CapReachedEvent,
|
|
35
35
|
CapWarningEvent,
|
|
36
|
+
ClickedEvent,
|
|
36
37
|
DeferredEvent,
|
|
37
38
|
DeliveredEvent,
|
|
38
39
|
InboundCapReachedEvent,
|
|
39
40
|
InboundCapWarningEvent,
|
|
40
41
|
InboundEvent,
|
|
42
|
+
OpenedEvent,
|
|
41
43
|
SendoraErrorCode,
|
|
42
44
|
SpamComplaintEvent,
|
|
43
45
|
UnknownEvent,
|
|
@@ -58,6 +60,7 @@ __all__ = [
|
|
|
58
60
|
"BouncedEvent",
|
|
59
61
|
"CapReachedEvent",
|
|
60
62
|
"CapWarningEvent",
|
|
63
|
+
"ClickedEvent",
|
|
61
64
|
"ConflictError",
|
|
62
65
|
"DeferredEvent",
|
|
63
66
|
"DeliveredEvent",
|
|
@@ -67,6 +70,7 @@ __all__ = [
|
|
|
67
70
|
"InboundEvent",
|
|
68
71
|
"InternalServerError",
|
|
69
72
|
"NotFoundError",
|
|
73
|
+
"OpenedEvent",
|
|
70
74
|
"PermissionDeniedError",
|
|
71
75
|
"RateLimitError",
|
|
72
76
|
"ResponseValidationError",
|
|
@@ -15,12 +15,14 @@ from .types import (
|
|
|
15
15
|
BouncedEvent,
|
|
16
16
|
CapReachedEvent,
|
|
17
17
|
CapWarningEvent,
|
|
18
|
+
ClickedEvent,
|
|
18
19
|
DeferredEvent,
|
|
19
20
|
DeliveredEvent,
|
|
20
21
|
InboundCapReachedEvent,
|
|
21
22
|
InboundCapWarningEvent,
|
|
22
23
|
InboundEvent,
|
|
23
24
|
InboundEventContent,
|
|
25
|
+
OpenedEvent,
|
|
24
26
|
SpamComplaintEvent,
|
|
25
27
|
UnknownEvent,
|
|
26
28
|
UnsubscribedEvent,
|
|
@@ -169,6 +171,8 @@ _EVENTS: dict[str, Callable[[dict[str, object]], WebhookEvent]] = {
|
|
|
169
171
|
"deferred": lambda payload: decode(DeferredEvent, payload),
|
|
170
172
|
"spam_complaint": lambda payload: decode(SpamComplaintEvent, payload),
|
|
171
173
|
"unsubscribed": lambda payload: decode(UnsubscribedEvent, payload),
|
|
174
|
+
"opened": lambda payload: decode(OpenedEvent, payload),
|
|
175
|
+
"clicked": lambda payload: decode(ClickedEvent, payload),
|
|
172
176
|
"cap_warning": lambda payload: decode(CapWarningEvent, payload),
|
|
173
177
|
"cap_reached": lambda payload: decode(CapReachedEvent, payload),
|
|
174
178
|
"inbound": _inbound,
|
|
@@ -44,7 +44,8 @@ class Webhooks:
|
|
|
44
44
|
Args:
|
|
45
45
|
url: Where the events are posted: https, without credentials or
|
|
46
46
|
a fragment.
|
|
47
|
-
events: The events to deliver; every event
|
|
47
|
+
events: The events to deliver; every event except ``opened`` and
|
|
48
|
+
``clicked`` when left out.
|
|
48
49
|
stream_id: The one stream whose message events it receives;
|
|
49
50
|
every stream when left out. Usage events arrive either way.
|
|
50
51
|
inbound_content: How the ``inbound`` event carries a received
|
|
@@ -261,7 +262,8 @@ class AsyncWebhooks:
|
|
|
261
262
|
Args:
|
|
262
263
|
url: Where the events are posted: https, without credentials or
|
|
263
264
|
a fragment.
|
|
264
|
-
events: The events to deliver; every event
|
|
265
|
+
events: The events to deliver; every event except ``opened`` and
|
|
266
|
+
``clicked`` when left out.
|
|
265
267
|
stream_id: The one stream whose message events it receives;
|
|
266
268
|
every stream when left out. Usage events arrive either way.
|
|
267
269
|
inbound_content: How the ``inbound`` event carries a received
|
|
@@ -48,6 +48,8 @@ __all__ = [
|
|
|
48
48
|
"CapEventFields",
|
|
49
49
|
"CapReachedEvent",
|
|
50
50
|
"CapWarningEvent",
|
|
51
|
+
"ClickedDetails",
|
|
52
|
+
"ClickedEvent",
|
|
51
53
|
"CnameRecord",
|
|
52
54
|
"CreatedServer",
|
|
53
55
|
"CreatedToken",
|
|
@@ -99,6 +101,8 @@ __all__ = [
|
|
|
99
101
|
"MessageStatus",
|
|
100
102
|
"MessageTracking",
|
|
101
103
|
"MxRecord",
|
|
104
|
+
"OpenedDetails",
|
|
105
|
+
"OpenedEvent",
|
|
102
106
|
"OwnServer",
|
|
103
107
|
"RecipientKind",
|
|
104
108
|
"RecipientStatus",
|
|
@@ -493,7 +497,7 @@ report received by mail, ``Feedback`` for a spam complaint, ``Open`` and
|
|
|
493
497
|
|
|
494
498
|
TrackingAutomated = Literal["likely", "unlikely", "unknown"]
|
|
495
499
|
"""Whether a machine rather than a person did it: ``unknown`` where nobody
|
|
496
|
-
can tell, as with Apple
|
|
500
|
+
can tell, as with Apple's privacy protection."""
|
|
497
501
|
|
|
498
502
|
TrackingAgent = Literal[
|
|
499
503
|
"apple_privacy",
|
|
@@ -1189,6 +1193,8 @@ WebhookEventName = Literal[
|
|
|
1189
1193
|
"deferred",
|
|
1190
1194
|
"spam_complaint",
|
|
1191
1195
|
"unsubscribed",
|
|
1196
|
+
"opened",
|
|
1197
|
+
"clicked",
|
|
1192
1198
|
"cap_warning",
|
|
1193
1199
|
"cap_reached",
|
|
1194
1200
|
"inbound",
|
|
@@ -2083,6 +2089,86 @@ class UnsubscribedEvent(MessageEventFields):
|
|
|
2083
2089
|
details: UnsubscribedDetails
|
|
2084
2090
|
|
|
2085
2091
|
|
|
2092
|
+
@model
|
|
2093
|
+
class OpenedDetails(Answer):
|
|
2094
|
+
"""What tracking knows of an open.
|
|
2095
|
+
|
|
2096
|
+
Attributes:
|
|
2097
|
+
automated: Whether a machine rather than a person did it.
|
|
2098
|
+
agent: The proxy or machine it came through, where Sendora knows it.
|
|
2099
|
+
reasons: Why it was labelled a machine's.
|
|
2100
|
+
client: The mail program, where Sendora knows it.
|
|
2101
|
+
device: The kind of device, where Sendora knows it.
|
|
2102
|
+
first: The recipient's first event of this kind.
|
|
2103
|
+
first_human: The recipient's first event of this kind that was
|
|
2104
|
+
likely a person's.
|
|
2105
|
+
"""
|
|
2106
|
+
|
|
2107
|
+
automated: TrackingAutomated
|
|
2108
|
+
agent: TrackingAgent | None
|
|
2109
|
+
reasons: list[TrackingReason]
|
|
2110
|
+
client: TrackingClient | None
|
|
2111
|
+
device: TrackingDevice | None
|
|
2112
|
+
first: bool
|
|
2113
|
+
first_human: bool
|
|
2114
|
+
|
|
2115
|
+
|
|
2116
|
+
@model
|
|
2117
|
+
class OpenedEvent(MessageEventFields):
|
|
2118
|
+
"""The pixel of a tracked copy loaded, for a person or a machine.
|
|
2119
|
+
|
|
2120
|
+
A webhook gets this event only if it names it.
|
|
2121
|
+
|
|
2122
|
+
Attributes:
|
|
2123
|
+
event: ``opened``.
|
|
2124
|
+
details: What tracking knows of it.
|
|
2125
|
+
"""
|
|
2126
|
+
|
|
2127
|
+
event: Literal["opened"]
|
|
2128
|
+
details: OpenedDetails
|
|
2129
|
+
|
|
2130
|
+
|
|
2131
|
+
@model
|
|
2132
|
+
class ClickedDetails(Answer):
|
|
2133
|
+
"""What tracking knows of a click.
|
|
2134
|
+
|
|
2135
|
+
Attributes:
|
|
2136
|
+
automated: Whether a machine rather than a person did it.
|
|
2137
|
+
agent: The proxy or machine it came through, where Sendora knows it.
|
|
2138
|
+
reasons: Why it was labelled a machine's.
|
|
2139
|
+
client: The mail program, where Sendora knows it.
|
|
2140
|
+
device: The kind of device, where Sendora knows it.
|
|
2141
|
+
first: The recipient's first event of this kind.
|
|
2142
|
+
first_human: The recipient's first event of this kind that was
|
|
2143
|
+
likely a person's.
|
|
2144
|
+
link: The link that was followed.
|
|
2145
|
+
"""
|
|
2146
|
+
|
|
2147
|
+
automated: TrackingAutomated
|
|
2148
|
+
agent: TrackingAgent | None
|
|
2149
|
+
reasons: list[TrackingReason]
|
|
2150
|
+
client: TrackingClient | None
|
|
2151
|
+
device: TrackingDevice | None
|
|
2152
|
+
first: bool
|
|
2153
|
+
first_human: bool
|
|
2154
|
+
link: TrackedLink
|
|
2155
|
+
|
|
2156
|
+
|
|
2157
|
+
@model
|
|
2158
|
+
class ClickedEvent(MessageEventFields):
|
|
2159
|
+
"""A tracked link was followed, by a person or a machine.
|
|
2160
|
+
|
|
2161
|
+
A webhook gets this event only if it names it.
|
|
2162
|
+
|
|
2163
|
+
Attributes:
|
|
2164
|
+
event: ``clicked``.
|
|
2165
|
+
details: What tracking knows of it.
|
|
2166
|
+
"""
|
|
2167
|
+
|
|
2168
|
+
event: Literal["clicked"]
|
|
2169
|
+
details: ClickedDetails
|
|
2170
|
+
|
|
2171
|
+
|
|
2086
2172
|
@model
|
|
2087
2173
|
class CapEventFields(Answer):
|
|
2088
2174
|
"""What both usage events carry.
|
|
@@ -2316,6 +2402,8 @@ WebhookEvent = (
|
|
|
2316
2402
|
| DeferredEvent
|
|
2317
2403
|
| SpamComplaintEvent
|
|
2318
2404
|
| UnsubscribedEvent
|
|
2405
|
+
| OpenedEvent
|
|
2406
|
+
| ClickedEvent
|
|
2319
2407
|
| CapWarningEvent
|
|
2320
2408
|
| CapReachedEvent
|
|
2321
2409
|
| InboundEvent
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|