Metadata-Version: 2.1
Name: axtbot-plugin-certd
Version: 1.0.1
Summary: AxTBot 插件：接收 Certd 自定义 webhook 推送并转发证书通知到指定 QQ 群
Keywords: axtbot,plugin,qq,certd,webhook,certificate
Author-Email: Shanshui2024 <shanshui@shanshui.site>
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Framework :: AsyncIO
Project-URL: Homepage, https://github.com/Shanshui2024/axtbot-plugin-certd
Project-URL: Source, https://github.com/Shanshui2024/axtbot-plugin-certd
Requires-Python: >=3.11
Description-Content-Type: text/markdown

<div align="center">

# axtbot-plugin-certd

_✨ 接收 Certd 证书通知，转发到指定 QQ 群的 AxTBot 插件 ✨_

<a href="https://pypi.python.org/pypi/axtbot-plugin-certd">
    <img src="https://img.shields.io/pypi/v/axtbot-plugin-certd.svg" alt="pypi">
</a>
<img src="https://img.shields.io/badge/python-3.11+-blue.svg" alt="python">

</div>

## 介绍

本插件为 [AxTBot](https://github.com/AxT-Team/AxTBot) 提供 Certd 证书通知的接入能力：
通过挂载一个 `/notify/certd` 的 POST 接口，接收 [Certd](https://github.com/certd/certd)「自定义 webhook」推送的证书申请 / 续期 / 部署通知，并转发为 markdown 消息到指定 QQ 群。

## 工作原理

```
Certd 自定义 webhook ──POST──▶ AxTBot FastAPI (/notify/certd)
                                        │
                                        │ 校验请求头 X-Appid / X-Bot-Secret
                                        ▼
                              组装 markdown 消息
                                        │
                                        ▼
                                 指定 QQ 群
```

本插件的 Webhook 路由**直接挂载到 AxTBot 的 FastAPI 实例**上：

- 复用 AxTBot 主服务的 ip:port，**不额外占用端口**；
- 路径使用 `/notify/*` 前缀，与 QQ 回调 `/webhook` 区分，互不冲突；
- 生命周期由 uvicorn 统一管理，无需手动起停。

## 安装方法

<details>
<summary>使用包管理器安装</summary>
在项目的插件目录下, 打开命令行, 根据你使用的包管理器, 输入相应的安装命令

<details>
<summary>pip</summary>

    pip install axtbot-plugin-certd
</details>
<details>
<summary>pdm</summary>

    pdm add axtbot-plugin-certd
</details>
<details>
<summary>poetry</summary>

    poetry add axtbot-plugin-certd
</details>

</details>

安装后无需手动注册，AxTBot 启动时会通过 `axtbot.plugins` entry point 自动发现。

> 本地包方式：将 `src/axtbot_plugin_certd` 目录复制到 AxTBot 的 `plugins/` 目录下，重启 AxTBot 生效。

## 配置

鉴权所需的 `appid` / `bot_secret` **直接复用框架核心配置**，无需额外配置：

| 配置项 | 必填 | 说明 |
|---|---|---|
| `FRAMEWORK_APPID` | 是 | 框架核心配置中的 AppID（`local.env`） |
| `FRAMEWORK_BOT_SECRET` | 是 | 框架核心配置中的 Bot Secret（`local.env`） |
| `PLUGIN_CERTD_GROUP_OPENID` | 否 | 兜底通知群 openid；Certd 推送的 body 里已写死 `group_openid` 时可留空 |

示例：

```env
FRAMEWORK_APPID=your_appid
FRAMEWORK_BOT_SECRET=your_bot_secret
PLUGIN_CERTD_GROUP_OPENID=
```

## Certd 接线

在 Certd 中新增「自定义 webhook」通知渠道，并按下述示例填写：

### 1. Webhook 地址

```
https://<AxTBot公网域名或IP>:<AxTBot端口>/notify/certd
```

### 2. 请求方式 / ContentType

- 请求方式：`POST`
- ContentType：`application/json`

### 3. Headers（一行一个 `key=value`）

```
X-Appid=<与 FRAMEWORK_APPID 一致>
X-Bot-Secret=<与 FRAMEWORK_BOT_SECRET 一致>
```

### 4. 消息 body 模板

严格按以下结构填写（`appid` 与 `group_openid` 可直接写死，`{title}` / `{content}` 为 Certd 内置变量）：

```json
{
  "appid": "<与 FRAMEWORK_APPID 一致>",
  "data": {
    "title": "{title}",
    "content": "{content}\n[查看详情]({url})",
    "group_openid": "<目标 QQ 群 openid>"
  }
}
```

> 说明：
> - `data.content` 会原样作为 markdown 推送到 QQ 群（Certd 的 `{content}` 本身即 markdown 结构）；
> - `data.group_openid` 建议在模板里写死；若留空则回退到 `PLUGIN_CERTD_GROUP_OPENID` 配置；
> - `appid` 必须与框架核心配置一致，否则请求会被忽略。

### 5. 公网可达

本插件不单独监听端口，直接复用 AxTBot 主服务端口。确保 AxTBot 主端口通过
frp / 反向代理对外可达，Certd 才能推送成功。

## 开发

```bash
pdm install --dev
pdm run format       # isort + black 格式化
```

代码规范：Python ≥ 3.11，`black`（line-length 90）+ `isort` 配置见 `pyproject.toml`。

## 构建发布

```bash
pdm build            # 输出到 dist/
pip install dist/axtbot_plugin_certd-*.whl
```

## 常见问题

**Q：重启后日志里没看到 `Certd通知 >>> 已挂载 /notify/certd`？**

确认插件是否被加载（日志应出现 `插件 >>> 从 PyPI 加载插件: Certd证书通知`）。
本地包方式请检查 `plugins/axtbot_plugin_certd/` 目录是否完整。

**Q：Certd 推送返回 401？**

请求头 `X-Appid` / `X-Bot-Secret` 与框架核心配置 `FRAMEWORK_APPID` / `FRAMEWORK_BOT_SECRET` 不一致。

**Q：收到 200 但群内没消息？**

检查 body 中的 `group_openid` 是否正确、机器人是否开启群聊 markdown 能力。
