LinuxDoSpace Python SDK for HTTPS mail streaming
Project description
LinuxDoSpace Python SDK
LinuxDoSpace 是 LinuxDoSpace 邮件 HTTPS 实时流的 Python SDK。
它当前专注在一个目标能力:
- 使用站点后台签发的 API Token
- 通过
HTTPS长连接实时接收邮件事件 - 把原始邮件解析成带完整属性提示的 Python 对象
安装
当前 PyPI 安装命令是:
pip install linuxdospace
如果你更习惯显式调用当前 Python 解释器,也可以使用:
python -m pip install linuxdospace
如果你在当前仓库里本地开发 SDK,再使用可编辑安装:
python -m pip install -e .
快速开始
关于 Suffix.linuxdo_space:
- 它是语义后缀,不是字面父域名
- SDK 会在内部把它解析到当前 token 拥有者在
linuxdo.space下的第一方邮件命名空间 - 当前至少自动兼容:
<owner_username>-mail.linuxdo.space<owner_username>-mail<suffix_fragment>.linuxdo.space
Suffix.linuxdo_space当前默认表示<owner_username>-mail.linuxdo.spaceSuffix.linuxdo_space.with_suffix("foo")表示<owner_username>-mailfoo.linuxdo.space- 如果你需要字面自定义后缀,请直接传普通字符串
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
for item in client.listen(timeout=60):
print(item.address)
print(item.sender)
print(item.subject)
print(item.text)
更推荐使用显式注册接口:
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
mail = client.mail.bind(prefix="alice", suffix=Suffix.linuxdo_space)
try:
for item in mail.listen(timeout=60):
print(item.address)
print(item.sender)
print(item.subject)
print(item.text)
finally:
mail.close()
如果你更喜欢 with,它只是上面显式注册写法的语法糖,并且会在离开作用域时自动解绑:
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
with client.mail.bind(prefix="alice", suffix=Suffix.linuxdo_space) as mail:
for item in mail.listen(timeout=60):
print(item.address)
print(item.sender)
print(item.subject)
print(item.text)
如果你要使用一个额外的动态 mail suffix:
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
with client.mail.bind(
prefix="alice",
suffix=Suffix.linuxdo_space.with_suffix("foo"),
) as mail:
print(mail.address) # alice@<owner_username>-mailfoo.linuxdo.space
for item in mail.listen(timeout=60):
print(item.address)
print(item.subject)
这里不需要你自己拼出真实域名。SDK 会自动把当前活跃的动态 suffix
同步回后端,后端只接受当前连接实际注册过的 -mail<suffix> 域。
如果你要一次性注册多条绑定,可以显式批量注册:
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
with client.mail.bind_many(
client.mail.spec(pattern=r".*", suffix=Suffix.linuxdo_space, allow_overlap=True),
client.mail.spec(prefix="alice", suffix=Suffix.linuxdo_space),
client.mail.spec(prefix="bob", suffix=Suffix.linuxdo_space),
) as bindings:
catch_all = bindings[0]
alice = bindings[1]
bob = bindings[2]
for item in alice.listen(timeout=60):
print("alice", item.subject)
多个邮箱可以复用同一个 Client:
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
with client.mail.bind(prefix="alice", suffix=Suffix.linuxdo_space) as alice:
with client.mail.bind(prefix="bob", suffix=Suffix.linuxdo_space) as bob:
for item in client.listen(timeout=60):
for mailbox in client.mail.route(item):
if mailbox is alice:
print("alice", item.subject)
elif mailbox is bob:
print("bob", item.subject)
如果你确实要消费多个 mailbox 自己的本地队列,就需要让这些
mail.listen(...) 同时处于活动状态,例如放到不同线程或任务里。
顺序调用 alice.listen(...) 再 bob.listen(...) 并不等于并行监听。
正则绑定也可以直接使用:
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
with client.mail.bind(pattern=r".*", suffix=Suffix.linuxdo_space, allow_overlap=True) as catch_all:
for item in catch_all.listen(timeout=60):
print(item.address, item.subject)
如果你确实想保留旧的简写风格,client.mail(...) 仍然可用,但它只是 client.mail.bind(...) 的同义写法。
显式解绑
除了 with 自动解绑以外,也可以显式调用:
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
alice = client.mail.bind(prefix="alice", suffix=Suffix.linuxdo_space)
catch_all = client.mail.bind(pattern=r".*", suffix=Suffix.linuxdo_space, allow_overlap=True)
try:
for item in alice.listen(timeout=60):
print(item.subject)
finally:
client.mail.unbind(alice, catch_all)
全量流到子绑定的本地路由辅助
client.listen(...) 是完整接收接口。
如果你在消费完整流时,想知道当前消息会命中哪些本地子绑定,可以使用只读路由辅助:
from LinuxDoSpace import Client, Suffix
with Client(token="你的 API Token") as client:
with client.mail.bind(pattern=r".*", suffix=Suffix.linuxdo_space, allow_overlap=True) as catch_all:
with client.mail.bind(prefix="alice", suffix=Suffix.linuxdo_space) as alice:
for item in client.listen(timeout=60):
matched = client.mail.route(item)
print(item.address, [mailbox.address or mailbox.pattern for mailbox in matched])
client.mail.route(item) 只会基于这条 MailMessage 当前的 item.address 做匹配,不会把整封原始多收件人事件重新展开。
它返回的是“当前时刻的本地匹配结果”,不是对过去已经发生的队列投递做历史回放。
还需要注意一件事:
client.listen(...)是完整流视角,每个上游事件只会向你暴露一条MailMessage- 这条
MailMessage.address是当前事件的投影地址 - 原始完整收件人列表仍然保留在
item.recipients mail.listen(...)则是 mailbox 视角,会针对每个命中的收件地址分别投递
异常处理
from LinuxDoSpace import AuthenticationError, Client, LinuxDoSpaceError, StreamError
try:
with Client(token="你的 API Token") as client:
for item in client.listen(timeout=60):
print(item.subject)
except AuthenticationError:
print("Token 无效,或后端拒绝了当前 Token。")
except StreamError:
print("HTTPS 实时流建立失败,或者流中断。")
except LinuxDoSpaceError as exc:
print(f"SDK 运行失败: {exc}")
设计说明
Client创建后会立即建立一条共享的 HTTPS 上游连接- 一个
Client始终只维护一条到/v1/token/email/stream的真实连接 Client会统一接收、统一解析、统一分发收到的所有邮件事件client.listen(timeout=-1)是最核心的“全量接收”接口client.listen(...)与mail.listen(...)的正数timeout都表示该迭代器的最长总时长,不是空闲超时client.mail.bind(...)在创建时就会立即注册本地绑定mail.close()会立即解绑;离开with作用域也会立即解绑bind(...)不会为尚未开始listen()的 mailbox 悄悄积压历史消息client.mail.bind(prefix=..., suffix=...).listen(...)是精确邮箱绑定client.mail.bind(pattern=..., suffix=...).listen(...)是正则邮箱绑定client.mail.bind_many(...)可以一次注册多条有序绑定client.mail.route(message)只查看这条消息当前address会命中哪些本地子绑定client.listen(...)每次返回一条上游事件投影,message.address是当前投影地址,message.recipients保留完整原始收件人列表mail.listen(...)每次返回一条命中 mailbox 的收件地址投影,因此它看到的message.address可以和全量流视角不同client.mail(...)只是client.mail.bind(...)的语法糖Suffix.linuxdo_space当前会解析成当前用户的基础 mail 命名空间<owner_username>-mail.linuxdo.spaceSuffix.linuxdo_space.with_suffix("foo")会解析成<owner_username>-mailfoo.linuxdo.space- SDK 会自动把活跃的动态
-mail<suffix>过滤列表同步到/v1/token/email/filters - SDK 会忽略
ready与heartbeat事件,只向你暴露真正的邮件事件 - 如果
timeout为正数,则表示本次监听的最长总时长(秒) - 返回对象会尽量把常用信息都变成属性,方便 IDE 自动补全
架构说明
- 服务端只知道一个 Token 对应一个客户端连接
- 服务端不会知道客户端内部绑定了哪些邮箱
- 客户端内部会根据邮件中的收件地址,在本地内存里完成筛选和分发
- 因此多个
mail.bind()绑定不会增加服务端的上游连接数 client.listen(...)负责完整接收client.mail.route(...)负责说明当前这条消息的address在本地会落到哪些子绑定mail.listen(...)负责消费某一个已经注册完成、且当前处于监听状态的本地子队列
匹配规则
prefix和pattern必须二选一- 精确绑定和正则绑定不会分成两套优先级
- 所有同一
suffix下的绑定都按创建顺序进入同一条匹配链 - 某个绑定一旦匹配成功:
- 它一定会收到消息
- 如果
allow_overlap=False,则立即停止,不再继续检查后面的绑定 - 如果
allow_overlap=True,则继续向后匹配,允许多个绑定同时收到
- 正则匹配使用的是邮箱本地前缀的
fullmatch(),不是search()
示例:
- 如果先创建
pattern=r".*", 后创建prefix="alice",而第一个绑定没有开启allow_overlap那么alice@<owner_username>.linuxdo.space或alice@<owner_username>-mail.linuxdo.space都会先命中.*,并在那里停止,后面的精确绑定不会收到 - 如果先创建
pattern=r".*", 且它设置了allow_overlap=True后面创建的prefix="alice"也能继续收到
当前暴露的主要属性
每一封邮件会被解析成 MailMessage 对象,常用属性包括:
addresssenderrecipientsreceived_atsubjectmessage_iddatefrom_headerto_headercc_headerreply_to_headerfrom_addressesto_addressescc_addressesreply_to_addressestexthtmlheadersrawraw_bytesmessage
注意事项
- API Token 明文只会在创建时返回一次,请妥善保存
- Token 目标只有在客户端实际建立 HTTPS 流连接时才会收到邮件事件
- 如果服务端发现当前没有任何客户端连接,邮件事件会被直接丢弃,不会排队补发
- SDK 默认要求远程后端使用
https://;只有localhost/127.0.0.1/::1这类本地调试地址允许使用http:// - 如果你需要对一个 Token 下的所有邮件做统一处理,请优先使用
client.listen(...) - 同一个
MailBox实例只允许一个活动监听器;如果你需要并行消费,请显式注册多个绑定实例
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file linuxdospace-0.3.0a4.tar.gz.
File metadata
- Download URL: linuxdospace-0.3.0a4.tar.gz
- Upload date:
- Size: 26.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
037620100489afa185bb321e7170154b4c7a7d9eb99e0e3bb03f0cde837bd5d4
|
|
| MD5 |
97d6477f9cc6990364b0adc98d01b53e
|
|
| BLAKE2b-256 |
07449dad73845db4ff9aa701e01be216cacbc0949441e3d170ea7f2d5f03b93b
|
Provenance
The following attestation bundles were made for linuxdospace-0.3.0a4.tar.gz:
Publisher:
pypi-release.yml on MoYeRanqianzhi/LinuxDoSpacePythonSDK
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linuxdospace-0.3.0a4.tar.gz -
Subject digest:
037620100489afa185bb321e7170154b4c7a7d9eb99e0e3bb03f0cde837bd5d4 - Sigstore transparency entry: 1190673642
- Sigstore integration time:
-
Permalink:
MoYeRanqianzhi/LinuxDoSpacePythonSDK@c096d20081084a5fe20afbe0b007bc20334dd4fa -
Branch / Tag:
refs/tags/v0.3.0a4 - Owner: https://github.com/MoYeRanqianzhi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-release.yml@c096d20081084a5fe20afbe0b007bc20334dd4fa -
Trigger Event:
push
-
Statement type:
File details
Details for the file linuxdospace-0.3.0a4-py3-none-any.whl.
File metadata
- Download URL: linuxdospace-0.3.0a4-py3-none-any.whl
- Upload date:
- Size: 20.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db4b00cd6a0bfee9317c15ade6d815d440e00db1aa20d32b7c30565469415077
|
|
| MD5 |
7f6b0c842820dc114afb5944ab4cec6c
|
|
| BLAKE2b-256 |
38d8eb0ff6b56e10448b19a643f7f9ea4e527d81711cd6075e15e85b988e4802
|
Provenance
The following attestation bundles were made for linuxdospace-0.3.0a4-py3-none-any.whl:
Publisher:
pypi-release.yml on MoYeRanqianzhi/LinuxDoSpacePythonSDK
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linuxdospace-0.3.0a4-py3-none-any.whl -
Subject digest:
db4b00cd6a0bfee9317c15ade6d815d440e00db1aa20d32b7c30565469415077 - Sigstore transparency entry: 1190673643
- Sigstore integration time:
-
Permalink:
MoYeRanqianzhi/LinuxDoSpacePythonSDK@c096d20081084a5fe20afbe0b007bc20334dd4fa -
Branch / Tag:
refs/tags/v0.3.0a4 - Owner: https://github.com/MoYeRanqianzhi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-release.yml@c096d20081084a5fe20afbe0b007bc20334dd4fa -
Trigger Event:
push
-
Statement type: