sendora 2.4.0__tar.gz → 2.5.1__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.4.0 → sendora-2.5.1}/CHANGELOG.md +12 -0
- {sendora-2.4.0 → sendora-2.5.1}/PKG-INFO +18 -13
- {sendora-2.4.0 → sendora-2.5.1}/README.md +17 -12
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/__init__.py +2 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_version.py +1 -1
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_webhooks.py +2 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/types.py +21 -2
- {sendora-2.4.0 → sendora-2.5.1}/.gitignore +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/LICENSE +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/pyproject.toml +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_account.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_answers.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_client.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_errors.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_json.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_keys.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_model.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_options.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_pages.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_retry.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_transport.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/_wire.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/py.typed +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/__init__.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/broadcasts.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/domains.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/email.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/erasures.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/inbound.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/inbound_domains.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/messages.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/server.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/servers.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/streams.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/suppressions.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/tokens.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/tracking_domains.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/tracking_opt_outs.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/resources/webhooks.py +0 -0
- {sendora-2.4.0 → sendora-2.5.1}/src/sendora/skills/sendora/SKILL.md +0 -0
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.5.1
|
|
4
|
+
|
|
5
|
+
- The README no longer says that tracking is not open to accounts. It says
|
|
6
|
+
that an administrator switches tracking on for a stream in the dashboard.
|
|
7
|
+
No call changes.
|
|
8
|
+
|
|
9
|
+
## 2.5.0
|
|
10
|
+
|
|
11
|
+
- A new event, `TrackingOptedOutEvent`, for `tracking_opted_out`: a recipient
|
|
12
|
+
asked not to be tracked, through the link that `{{ tracking_opt_out_url }}`
|
|
13
|
+
puts in your mail. `webhooks.create` without `events` takes it.
|
|
14
|
+
|
|
3
15
|
## 2.4.0
|
|
4
16
|
|
|
5
17
|
- `sendora.tracking_opt_outs` manages a server's not-tracked list: `list`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sendora
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.1
|
|
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
|
|
@@ -148,8 +148,8 @@ list of them), `from_` and `to` as an aware `datetime` or ISO 8601,
|
|
|
148
148
|
|
|
149
149
|
Where mail is tracked, each recipient has `tracked`, `opens`, `clicks`,
|
|
150
150
|
`first_open_at` and `first_human_open_at`. `messages.get` adds `tracking`, and
|
|
151
|
-
its timeline holds `Open` and `Click` events with their labels.
|
|
152
|
-
|
|
151
|
+
its timeline holds `Open` and `Click` events with their labels. An
|
|
152
|
+
administrator switches tracking on for a stream in the dashboard.
|
|
153
153
|
|
|
154
154
|
```python
|
|
155
155
|
from datetime import UTC, datetime
|
|
@@ -250,8 +250,7 @@ sendora.suppressions.delete(address="anna@example.com")
|
|
|
250
250
|
|
|
251
251
|
Addresses the server never tracks, on any of its streams. Mail to one still
|
|
252
252
|
goes out, untracked, and its opens and clicks of mail already sent are not
|
|
253
|
-
recorded.
|
|
254
|
-
until it is.
|
|
253
|
+
recorded.
|
|
255
254
|
|
|
256
255
|
| Call | Answers |
|
|
257
256
|
| ------------------------------------------------ | -------------------------------------------------------- |
|
|
@@ -262,8 +261,14 @@ until it is.
|
|
|
262
261
|
|
|
263
262
|
An entry's `source` is `manual`, or `recipient` if the recipient asked for it.
|
|
264
263
|
Remove a recipient's own entry only after new consent. An entry's `address` is
|
|
265
|
-
`None`
|
|
266
|
-
|
|
264
|
+
`None` when only a keyed hash of it is left, after an erasure or when the
|
|
265
|
+
recipient asked from mail that had left the log. `delete` still finds that entry
|
|
266
|
+
by its address, and nothing removes it without the address.
|
|
267
|
+
|
|
268
|
+
To let a recipient ask, write `{{ tracking_opt_out_url }}` in the text or the
|
|
269
|
+
HTML of a message on any stream. Each recipient then gets a copy with their own
|
|
270
|
+
link to a page in Swedish and English. Its button lists the address with the
|
|
271
|
+
`source` `recipient`, and sends the `tracking_opted_out` event.
|
|
267
272
|
|
|
268
273
|
```python
|
|
269
274
|
sendora.tracking_opt_outs.add(address="anna@example.com")
|
|
@@ -335,7 +340,9 @@ Events: `delivered`, `bounced`, `deferred`, `spam_complaint` and
|
|
|
335
340
|
`details` from the receiver, or for `unsubscribed` the `source` (`link` or
|
|
336
341
|
`one_click`); `opened` and `clicked` about a tracked copy, sent only to a
|
|
337
342
|
webhook that names them, with the same fields and `details` that say whether
|
|
338
|
-
a machine did it and, for `clicked`, give the `link`; `
|
|
343
|
+
a machine did it and, for `clicked`, give the `link`; `tracking_opted_out`
|
|
344
|
+
about a recipient who asked not to be tracked, with the same fields and no
|
|
345
|
+
`details`; `cap_warning` and `cap_reached` about usage, with `scope`,
|
|
339
346
|
`cap`, `used` and the period; `inbound` about a message received on an
|
|
340
347
|
inbound stream, with `inbound_message_id`, `envelope_recipient`, the
|
|
341
348
|
sizes, the `authentication` verdicts and, for a webhook with
|
|
@@ -343,8 +350,7 @@ sizes, the `authentication` verdicts and, for a webhook with
|
|
|
343
350
|
`to`, `subject`, `text`, `html`, `attachments` and the rest.
|
|
344
351
|
`inbound_cap_warning` and `inbound_cap_reached` say that received mail
|
|
345
352
|
nears or reaches the account's monthly cap, with `cap`, `used` and the
|
|
346
|
-
period. Every event carries `id`, the delivery id, and `attempt`.
|
|
347
|
-
not open to accounts yet, so `opened` and `clicked` are not sent until it is.
|
|
353
|
+
period. Every event carries `id`, the delivery id, and `attempt`.
|
|
348
354
|
|
|
349
355
|
A failed delivery is retried with growing delays for about a day, then it
|
|
350
356
|
is `dead` until you replay it. Deliveries arrive at least once and not
|
|
@@ -469,9 +475,8 @@ print(domain.dkim.type, domain.dkim.host, domain.dkim.value)
|
|
|
469
475
|
|
|
470
476
|
A tracking domain is the host that tracked links and the open pixel use: a
|
|
471
477
|
subdomain of a verified sending domain, with one CNAME record to add. CAA
|
|
472
|
-
records above it must let Let's Encrypt issue its certificate.
|
|
473
|
-
|
|
474
|
-
whose records are not right within 7 days goes.
|
|
478
|
+
records above it must let Let's Encrypt issue its certificate. A host whose
|
|
479
|
+
records are not right within 7 days goes.
|
|
475
480
|
|
|
476
481
|
| Call | Answers |
|
|
477
482
|
| ----------------------------------------------------- | --------------------------------------------------------------------- |
|
|
@@ -126,8 +126,8 @@ list of them), `from_` and `to` as an aware `datetime` or ISO 8601,
|
|
|
126
126
|
|
|
127
127
|
Where mail is tracked, each recipient has `tracked`, `opens`, `clicks`,
|
|
128
128
|
`first_open_at` and `first_human_open_at`. `messages.get` adds `tracking`, and
|
|
129
|
-
its timeline holds `Open` and `Click` events with their labels.
|
|
130
|
-
|
|
129
|
+
its timeline holds `Open` and `Click` events with their labels. An
|
|
130
|
+
administrator switches tracking on for a stream in the dashboard.
|
|
131
131
|
|
|
132
132
|
```python
|
|
133
133
|
from datetime import UTC, datetime
|
|
@@ -228,8 +228,7 @@ sendora.suppressions.delete(address="anna@example.com")
|
|
|
228
228
|
|
|
229
229
|
Addresses the server never tracks, on any of its streams. Mail to one still
|
|
230
230
|
goes out, untracked, and its opens and clicks of mail already sent are not
|
|
231
|
-
recorded.
|
|
232
|
-
until it is.
|
|
231
|
+
recorded.
|
|
233
232
|
|
|
234
233
|
| Call | Answers |
|
|
235
234
|
| ------------------------------------------------ | -------------------------------------------------------- |
|
|
@@ -240,8 +239,14 @@ until it is.
|
|
|
240
239
|
|
|
241
240
|
An entry's `source` is `manual`, or `recipient` if the recipient asked for it.
|
|
242
241
|
Remove a recipient's own entry only after new consent. An entry's `address` is
|
|
243
|
-
`None`
|
|
244
|
-
|
|
242
|
+
`None` when only a keyed hash of it is left, after an erasure or when the
|
|
243
|
+
recipient asked from mail that had left the log. `delete` still finds that entry
|
|
244
|
+
by its address, and nothing removes it without the address.
|
|
245
|
+
|
|
246
|
+
To let a recipient ask, write `{{ tracking_opt_out_url }}` in the text or the
|
|
247
|
+
HTML of a message on any stream. Each recipient then gets a copy with their own
|
|
248
|
+
link to a page in Swedish and English. Its button lists the address with the
|
|
249
|
+
`source` `recipient`, and sends the `tracking_opted_out` event.
|
|
245
250
|
|
|
246
251
|
```python
|
|
247
252
|
sendora.tracking_opt_outs.add(address="anna@example.com")
|
|
@@ -313,7 +318,9 @@ Events: `delivered`, `bounced`, `deferred`, `spam_complaint` and
|
|
|
313
318
|
`details` from the receiver, or for `unsubscribed` the `source` (`link` or
|
|
314
319
|
`one_click`); `opened` and `clicked` about a tracked copy, sent only to a
|
|
315
320
|
webhook that names them, with the same fields and `details` that say whether
|
|
316
|
-
a machine did it and, for `clicked`, give the `link`; `
|
|
321
|
+
a machine did it and, for `clicked`, give the `link`; `tracking_opted_out`
|
|
322
|
+
about a recipient who asked not to be tracked, with the same fields and no
|
|
323
|
+
`details`; `cap_warning` and `cap_reached` about usage, with `scope`,
|
|
317
324
|
`cap`, `used` and the period; `inbound` about a message received on an
|
|
318
325
|
inbound stream, with `inbound_message_id`, `envelope_recipient`, the
|
|
319
326
|
sizes, the `authentication` verdicts and, for a webhook with
|
|
@@ -321,8 +328,7 @@ sizes, the `authentication` verdicts and, for a webhook with
|
|
|
321
328
|
`to`, `subject`, `text`, `html`, `attachments` and the rest.
|
|
322
329
|
`inbound_cap_warning` and `inbound_cap_reached` say that received mail
|
|
323
330
|
nears or reaches the account's monthly cap, with `cap`, `used` and the
|
|
324
|
-
period. Every event carries `id`, the delivery id, and `attempt`.
|
|
325
|
-
not open to accounts yet, so `opened` and `clicked` are not sent until it is.
|
|
331
|
+
period. Every event carries `id`, the delivery id, and `attempt`.
|
|
326
332
|
|
|
327
333
|
A failed delivery is retried with growing delays for about a day, then it
|
|
328
334
|
is `dead` until you replay it. Deliveries arrive at least once and not
|
|
@@ -447,9 +453,8 @@ print(domain.dkim.type, domain.dkim.host, domain.dkim.value)
|
|
|
447
453
|
|
|
448
454
|
A tracking domain is the host that tracked links and the open pixel use: a
|
|
449
455
|
subdomain of a verified sending domain, with one CNAME record to add. CAA
|
|
450
|
-
records above it must let Let's Encrypt issue its certificate.
|
|
451
|
-
|
|
452
|
-
whose records are not right within 7 days goes.
|
|
456
|
+
records above it must let Let's Encrypt issue its certificate. A host whose
|
|
457
|
+
records are not right within 7 days goes.
|
|
453
458
|
|
|
454
459
|
| Call | Answers |
|
|
455
460
|
| ----------------------------------------------------- | --------------------------------------------------------------------- |
|
|
@@ -42,6 +42,7 @@ from .types import (
|
|
|
42
42
|
OpenedEvent,
|
|
43
43
|
SendoraErrorCode,
|
|
44
44
|
SpamComplaintEvent,
|
|
45
|
+
TrackingOptedOutEvent,
|
|
45
46
|
UnknownEvent,
|
|
46
47
|
UnsubscribedEvent,
|
|
47
48
|
WebhookEvent,
|
|
@@ -79,6 +80,7 @@ __all__ = [
|
|
|
79
80
|
"SendoraError",
|
|
80
81
|
"SendoraErrorCode",
|
|
81
82
|
"SpamComplaintEvent",
|
|
83
|
+
"TrackingOptedOutEvent",
|
|
82
84
|
"UnknownEvent",
|
|
83
85
|
"UnprocessableEntityError",
|
|
84
86
|
"UnsubscribedEvent",
|
|
@@ -24,6 +24,7 @@ from .types import (
|
|
|
24
24
|
InboundEventContent,
|
|
25
25
|
OpenedEvent,
|
|
26
26
|
SpamComplaintEvent,
|
|
27
|
+
TrackingOptedOutEvent,
|
|
27
28
|
UnknownEvent,
|
|
28
29
|
UnsubscribedEvent,
|
|
29
30
|
WebhookEvent,
|
|
@@ -173,6 +174,7 @@ _EVENTS: dict[str, Callable[[dict[str, object]], WebhookEvent]] = {
|
|
|
173
174
|
"unsubscribed": lambda payload: decode(UnsubscribedEvent, payload),
|
|
174
175
|
"opened": lambda payload: decode(OpenedEvent, payload),
|
|
175
176
|
"clicked": lambda payload: decode(ClickedEvent, payload),
|
|
177
|
+
"tracking_opted_out": lambda payload: decode(TrackingOptedOutEvent, payload),
|
|
176
178
|
"cap_warning": lambda payload: decode(CapWarningEvent, payload),
|
|
177
179
|
"cap_reached": lambda payload: decode(CapReachedEvent, payload),
|
|
178
180
|
"inbound": _inbound,
|
|
@@ -146,6 +146,7 @@ __all__ = [
|
|
|
146
146
|
"TrackingOptOut",
|
|
147
147
|
"TrackingOptOutPage",
|
|
148
148
|
"TrackingOptOutSource",
|
|
149
|
+
"TrackingOptedOutEvent",
|
|
149
150
|
"TrackingReason",
|
|
150
151
|
"TrackingSkipped",
|
|
151
152
|
"TxtRecord",
|
|
@@ -1057,8 +1058,9 @@ class TrackingOptOut(Answer):
|
|
|
1057
1058
|
"""An address that the server never tracks.
|
|
1058
1059
|
|
|
1059
1060
|
Attributes:
|
|
1060
|
-
address: The address, lower-cased. ``None``
|
|
1061
|
-
|
|
1061
|
+
address: The address, lower-cased. ``None`` when only a keyed hash is
|
|
1062
|
+
left: after an erasure, or when the recipient asked from mail that
|
|
1063
|
+
had left the log.
|
|
1062
1064
|
source: ``manual`` if added through the API or the dashboard,
|
|
1063
1065
|
``recipient`` if the recipient asked for it. Remove a recipient's
|
|
1064
1066
|
own entry only after new consent.
|
|
@@ -1236,6 +1238,7 @@ WebhookEventName = Literal[
|
|
|
1236
1238
|
"unsubscribed",
|
|
1237
1239
|
"opened",
|
|
1238
1240
|
"clicked",
|
|
1241
|
+
"tracking_opted_out",
|
|
1239
1242
|
"cap_warning",
|
|
1240
1243
|
"cap_reached",
|
|
1241
1244
|
"inbound",
|
|
@@ -2210,6 +2213,21 @@ class ClickedEvent(MessageEventFields):
|
|
|
2210
2213
|
details: ClickedDetails
|
|
2211
2214
|
|
|
2212
2215
|
|
|
2216
|
+
@model
|
|
2217
|
+
class TrackingOptedOutEvent(MessageEventFields):
|
|
2218
|
+
"""The recipient asked not to be tracked.
|
|
2219
|
+
|
|
2220
|
+
They used the page that ``{{ tracking_opt_out_url }}`` links to, so the
|
|
2221
|
+
address is on the server's not-tracked list. A webhook that leaves out
|
|
2222
|
+
``events`` gets this event.
|
|
2223
|
+
|
|
2224
|
+
Attributes:
|
|
2225
|
+
event: ``tracking_opted_out``.
|
|
2226
|
+
"""
|
|
2227
|
+
|
|
2228
|
+
event: Literal["tracking_opted_out"]
|
|
2229
|
+
|
|
2230
|
+
|
|
2213
2231
|
@model
|
|
2214
2232
|
class CapEventFields(Answer):
|
|
2215
2233
|
"""What both usage events carry.
|
|
@@ -2445,6 +2463,7 @@ WebhookEvent = (
|
|
|
2445
2463
|
| UnsubscribedEvent
|
|
2446
2464
|
| OpenedEvent
|
|
2447
2465
|
| ClickedEvent
|
|
2466
|
+
| TrackingOptedOutEvent
|
|
2448
2467
|
| CapWarningEvent
|
|
2449
2468
|
| CapReachedEvent
|
|
2450
2469
|
| 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
|
|
File without changes
|
|
File without changes
|