Skip to content

Latest commit

 

History

98 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EverywhereYouGo (EGo) v1.3.2

English | 中文

通用信息转发平台 — 数据 → 解析 → 路由 → 推送

接收任意 HTTP 请求,经解析器提取结构化字段,按条件路由到多个推送渠道。

Docker 部署

一键部署(推荐):

curl -O https://raw.githubusercontent.com/codename-test/EverywhereYouGo/main/deploy/init.sh
chmod +x init.sh
./init.sh
# 按提示选择部署模式

支持 5 种部署模式:default(快速起步)、t1-host(host 网络)、t2-bridge(bridge 网络)、t3-nginx(Nginx + 手动证书)、t4-acme(Nginx + Let's Encrypt 全自动证书)。

更多部署形态说明见 deploy/README.md。

启动后:管理页面 https://<主机IP>:5001(自签名证书,浏览器需放行);Webhook 接收与健康检查走 http://<主机IP>:5000。

架构

HTTP POST → 数据源 → 解析器 → 路由匹配 → 模板渲染 → 推送渠道
组件 说明
数据源 监听端口接收 HTTP POST
解析器 Python 脚本,提取字段并定义变量名
路由 条件表达式匹配渠道-模板对
模板 Simple / Jinja2 渲染标题和内容
渠道 企业微信、钉钉、飞书、Telegram、Bark、邮件 (SMTP)

认证

设置 EGO_AUTH_TOKEN 环境变量后开启访问控制:

EGO_AUTH_TOKEN=your-secret-token python3 main.py
  • Web 页面需通过登录页输入 Token
  • API 调用需携带 Authorization: Bearer your-secret-token 请求头
  • 健康检查 /api/health 无需认证

可选设置 EGO_SECRET_KEY 自定义 Flask session 密钥。

配置存储

配置有两份,角色不同:

存储 角色
SQLite(ego.db) 运行时真相源 —— 所有读写以库内数据为准
config/*.json 导出 / 备份介质 —— 便于备份、版本管理与迁移
文件 内容
config/parsers.json 解析器元信息
config/sources.json 数据源定义
config/channels.json 推送通道配置
config/templates.json 推送模板
config/bindings.json 渠道绑定(含条件表达式)

启动时的加载规则:

  1. 数据库已有配置 → 以数据库为准,不读 JSON,并把当前配置刷写回 config/*.json
  2. 数据库为空且有 JSON → 从 JSON 导入(首次启动 / 迁移 / 恢复)
  3. 数据库为空且无 JSON → 导出初始配置到 JSON

因此 日常改配置请用 WebUI(改完即时生效)。直接编辑 config/*.json 只在 「数据库为空」的首次导入场景才会被读取,不是常规生效路径。

系统设置(DND、日志级别等)、消息日志与队列同样存储在 SQLite。 配置备份 / 恢复请用「系统设置 → 备份」,会打包 config/*.json + 用户上传的 parsers/*.py 与 channels/*.py。

恢复(Restore)与启动加载不是同一条路径:恢复时把备份里的配置写入数据库 (config/*.json → SQLite),随后重载插件并重启各数据源监听。

恢复是"部分恢复":只把备份里存在的配置文件写入数据库,未包含的配置保持原样 (不会被清空),并在结果里给出提示。所以手搓/截断的 ZIP 不会把没带上的数据弄丢。 需要把某张表恢复成空,请在 ZIP 里放一个内容为 [] 的文件,而不是删掉它 (文件存在且为 [] → 清空该表;文件不存在 → 该表不动)。

⚠️ 升级前请先备份:任何版本 / 镜像升级前,请先在「系统设置 → 备份」导出 ZIP。 备份文件含完整推送凭据(SMTP 密码 / 授权码 / token 等),请妥善保管。

插件目录

内置插件与用户上传的插件分开放:

目录 内容 Docker
parsers_builtin/ 内置解析器 随镜像发布,不打卷
parsers/ 你上传的解析器 挂 ego_parsers 卷,容器重建不丢
channels_builtin/ 内置通道插件 随镜像发布,不打卷
channels/ 你上传的通道插件 挂 ego_channels 卷,容器重建不丢

规则:

  • 解析顺序:用户目录优先,其次内置目录
  • 与内置插件同名不允许上传(返回明确错误)—— 内置插件随镜像更新, 同名文件不会生效,不如一开始就说清楚
  • 内置插件只读:WebUI 里可以查看,但不能改、不能删
  • 确实要改内置插件的行为:把改好的文件换个名字放进用户目录, 或直接改源码重建镜像

为什么必须分开:如果把卷挂在放内置插件的目录上,named volume 首次创建会把 镜像里该目录的内容拷进卷里,之后就以卷为准 —— 镜像升级再也更新不到内置插件。 两者同目录无解:要么丢用户文件,要么内置永远升不上去。

从旧版本升级(v1.3.0 → v1.3.1+) 旧版本把用户上传的插件直接写在内置目录里,而该目录没有持久化——容器重建 就会丢失。升级到 v1.3.1+ 前,请先把用户插件备份出来:

  1. 导出(二选一):
    • 旧容器里「系统设置 → 备份」导出 ZIP(若旧版支持);
    • 或直接 docker cp 导出旧容器里的插件目录(旧版用户插件与内置混在同一 未挂载卷的目录里,具体路径看你的旧版部署):
      docker cp <ego-container>:/app/parsers_builtin  /tmp/old-parsers
      docker cp <ego-container>:/app/channels_builtin /tmp/old-channels
  2. 升级镜像:v1.3.1+ 起用户插件落在用户目录并挂 named volume (ego_parsers/ego_channels),普通镜像升级(拉新镜像、重建容器)无需 重传插件——插件随卷保留。
  3. 恢复(仅从旧版迁移时需要):恢复会写进新的用户目录,并自动跳过与内置 同名的条目(避免用旧副本遮蔽新版内置插件)。

卷操作语义(ego_parsers / ego_channels):

  • docker compose up(或重建容器)→ 保留卷,插件不丢
  • docker compose down → 默认保留 named volume,插件不丢
  • docker compose down -v → 删除卷,用户插件全部丢失,操作前先确认

解析器

放在 parsers/ 目录下的 .py 文件,定义一个 parse() 函数:

def parse(raw_body: bytes, headers: dict, query_params: dict) -> dict:
    data = json.loads(raw_body)
    event = data.get("Event", "")
    name = data.get("Item", {}).get("Name", "")
    return {
        "title": name,
        "event": event,
        "name": name,
    }

返回 dict 中除 title 外的字段同时用于:

  • 路由条件匹配:event == 'library.new' and media_type == 'Movie'
  • 模板变量引用:{name} / {{ msg.name }}

路由条件

支持 and、or、括号分组:

示例 说明
event == 'library.new' 仅新入库
event == 'library.new' and media_type == 'Movie' 仅新入库电影
event == 'library.new' or event == 'test' 新入库或测试消息

功能特性

免打扰(DND)

设置免打扰时段后,消息进入队列等待,结束后自动刷新。紧急路由不受 DND 影响。

消息去重

去重粒度是 消息 × 通道:每个渠道绑定各自配置 dedup_key_expr 与 dedup_window (默认 3600 秒),互不影响。命中的渠道被跳过,其余渠道照常发送; 只有全部渠道命中时,整条消息才标记为 DISCARDED。

并行推送

多渠道匹配时线程池并行发送,总延迟取决于最慢的单个渠道。

样本数据与在线调试

每个数据源自动保存最近 20 条请求样本,可在 WebUI 中选取样本进行测试解析和推送。

消息重发

失败消息支持原始重发(使用已解析的 msg_json)或重新解析后重发。 默认只重发上次失败的渠道,不会把已经成功的渠道重复推送一遍; 需要整体重推时可用 scope=all。

导入导出

  • 备份:下载 ZIP 包(config/*.json + parsers/*.py + channels/*.py)
  • 恢复:上传 ZIP 包,备份中的配置写入数据库(JSON → DB),插件文件同步恢复并热重载。 备份未包含的配置保持原样,并在结果里提示缺了哪些文件
  • 预览:点「预览」会先跑一遍与真实恢复相同的校验(体积 / 完整性 / JSON 结构 / 插件可加载), 校验不通过(如 JSON 结构非法)时不会给出"确认恢复"按钮
  • JSON 导入:支持 dry_run 预览、insert/overwrite 两种模式、依赖检查

安全提示

  • 备份 ZIP 内含完整推送凭据(SMTP 密码 / 授权码 / token 等),需妥善保管,勿外泄。
  • JSON 导出已对敏感字段(password / token / secret / webhook / device_key 等)脱敏为 ***,仅用于展示与归档;完整凭据仅经备份 ZIP 保留并恢复。

通道熔断

第三方渠道持续故障时自动隔离,避免拖垮整条发送链路:

  • 滑动窗口(默认 60s)内失败率 > 50%,或连续失败 ≥ 5 次 → 熔断
  • 冷却时间指数退避 30 → 60 → 120 → … → 600 秒(封顶 10 分钟)
  • 冷却结束后进入半开探测,连续 3 次成功才恢复
  • 4xx 不计失败(业务侧拒绝不等于服务故障),只对 5xx / 超时 / 连接类错误计数
  • 熔断期间消息留在队列等待:不消耗重试次数、不丢弃;状态持久化,重启后仍生效

出站限流

按通道独立限流(条/分钟),防止发送过快被对方封禁。重试解决不了 429—— 限流必须发生在发送之前。拿不到令牌的消息会排队等待,而不是被丢弃。

令牌桶(Token Bucket):每个通道独立令牌桶,桶容量(burst)等于配置的分钟 额度,按 额度 ÷ 60 每秒补充令牌;发一条消息需取 1 个令牌,桶空时不立即 丢弃而是排队直到有令牌(最长等 EGO_RATE_MAX_WAIT 秒,超时则延迟重排)。

韧性界面

位置 能做什么
通道列表 → 「韧性」列 查看限流徽章与熔断倒计时;熔断时可一键「手工恢复」
通道编辑弹窗 设置该通道的出站限流(留空/0 = 不限流)
系统设置 → 韧性(通道熔断) 手工调整滑动窗口、连续失败阈值、冷却基数/上限、探测次数等参数

参数取值优先级:system_config(设置页 / 直接改库) > 环境变量 > 内置默认, 改完即时生效、无需重启。

可观测性

端点 说明
GET /api/metrics 队列深度、死信总数、各通道成功率、端到端延迟、熔断与限流状态;?hours=N 调整统计窗口(默认 24h)
GET /api/resilience 当前处于熔断 / 限流状态的通道
GET /api/queue/stats 队列与死信计数
GET /api/health 健康检查(SQLite / 磁盘 / 配置 / 队列)

优雅停机

收到 SIGTERM 时先停止接收新消息,再等待在途任务完成(最长 30 秒), 超时未完成的转入死信队列——容器重启不会丢消息。

国际化

内置中英文双语支持,通过导航栏右上角语言切换按钮随时切换。

通道类型

通道 方式 类型标识
企业微信 Bot Webhook wechat_work_bot
企业微信 API 应用消息 wechat_work_api
钉钉 Webhook dingtalk
飞书 Webhook feishu
Telegram Bot API telegram_bot
Bark API bark
邮件 (SMTP) SMTP smtp_email

环境变量

变量 默认值 说明
WEB_PORT 5000 HTTP 端口(Webhook 接收 / 健康检查)
WEB_SSL_PORT 5001 HTTPS 端口(管理页面,证书缺失时不启用)
EGO_SSL_ENABLED 1 设为 0 完全关闭内置 HTTPS(仅 HTTP,不跳转、不生成证书)
EGO_SSL_DIR ./certs SSL 证书目录,ego.crt 和 ego.key 存放位置
EGO_SSL_CERT ./certs/ego.crt 证书文件路径(覆盖 EGO_SSL_DIR)
EGO_SSL_KEY ./certs/ego.key 私钥文件路径(覆盖 EGO_SSL_DIR)
DB_PATH ego.db 数据库路径
LOG_LEVEL INFO 日志等级
EGO_AUTH_TOKEN (空) 访问控制 Token
EGO_SECRET_KEY (自动) Flask session 密钥
EGO_INGRESS_WORKERS 8 每个端口数据源的入口工作线程数
EGO_INGRESS_MAX_QUEUE 200 入口等待队列上限,超出返回 503(背压)
EGO_CLEANUP_INTERVAL 600 旧消息 / 去重键的清理间隔(秒)
EGO_BREAKER_WINDOW 60 熔断滑动窗口(秒)
EGO_BREAKER_MIN_SAMPLES 5 窗口内触发失败率判定的最少样本数
EGO_BREAKER_FAILURE_RATIO 0.5 窗口失败率阈值(超过则熔断)
EGO_BREAKER_CONSECUTIVE 5 连续失败阈值(照顾低频通道)
EGO_BREAKER_OPEN_BASE 30 熔断冷却基数(秒),逐次翻倍
EGO_BREAKER_OPEN_MAX 600 熔断冷却上限(秒)
EGO_BREAKER_HALF_OPEN_OK 3 恢复所需连续探测成功次数
EGO_RATE_MAX_WAIT 1.0 限流取令牌的最长等待(秒),超时改为延迟重排
EGO_RATE_MISS_TTL 30 未配置限流的通道,回查数据库的间隔(秒)

License

MIT

About

EGo — 可自由拓展的通用消息网关。Python 可编程,渠道可插拔,接入任意 HTTP,送达任何终点。A freely extensible universal message gateway. Python-programmable, channel-pluggable, accepting any HTTP request and delivering to any destination.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages