xvmail服主使用教程文档
xvmail 服主完全使用教程
对应版本:xvmail 1.7.0。构建环境 Java 21,Paper API 编译目标 1.21.4-R0.1-SNAPSHOT,plugin.yml 声明 api-version: 1.21 与 folia-supported: true。Folia 运行与多版本兼容尚未在真实服务器验证,请勿直接对外做宽泛兼容承诺。
一、部署前必做的三件事(先看这里)
新装或升级后,请按顺序确认,否则会出现「邮件发不出去」「收不到真实邮件」「付费扣不了钱」:
- 处理 AI 审核:
moderation.enabled默认true,但api-key与model默认为空,审核是 fail-closed 的——没配好服务时所有发信都会被拒绝。要么填好兼容接口的 URL / Key / 模型,要么明确改成enabled: false(关闭后仍保留本地链接与宣传拦截)。 - 配置 SMTP:
smtp.enabled默认false。验证码、绑定、真实邮件同步全部依赖 SMTP;不启用就只有游戏内邮件。 - 确认经济插件:付费发送需要 Vault 加一个经济实现(EssentialsX / CMI 等),或 PlayerPoints;并把
sending.currency改成实际使用的那一个。启动时看日志付费发送货币=...:<状态>就能确认。
改完执行 /mailadmin reload,必要时重启服务器。
二、安装与配置文件
把 release/xvmail-1.7.0.jar 放进 plugins/,启动服务器。JAR 已打包 Gson 与 Jakarta Mail(Angus 实现),不需要额外下载依赖;Vault、PlayerPoints 只是软依赖,仅在付费发送时需要。
首次启动会生成 plugins/xvmail/config.yml。从旧版本升级时,saveDefaultConfig() 不会覆盖已有配置,因此插件做了一次性迁移:若配置里没有 config-version: 2,会在首次启动把 email-bind.auto-send-real-email 打开并写入 config-version: 2,后台会打印一条升级日志。之后服主手动改回 false 不会被再次覆盖。
完整配置示例
config-version: 2
storage:
directory: mail
mail:
max-inbox-size: 200
unread-reminder-on-join: true
subject-max-length: 80
content-max-length: 2000
console-sender-name: "服务器"
console-notify-online: true
smtp:
enabled: true
host: smtp.qq.com
port: 465
username: "your@qq.com"
password: "邮箱 SMTP 授权码"
from-address: "your@qq.com"
from-name: "teana server"
use-ssl: true
use-tls: false
timeout: 10000
email-bind:
enabled: true
auto-send-real-email: true
send-cooldown-seconds: 60
code-expire-minutes: 10
max-attempts: 5
moderation:
enabled: false
api-url: "https://api.openai.com/v1/chat/completions"
api-key: ""
model: ""
timeout-ms: 8000
sending:
daily-free-limit: 10
daily-paid-limit: 20
currency: vault
paid-cost: 100.0
attachment-max-bytes: 32768
claim:
command-template: ""
templates:
welcome:
subject: "欢迎来到服务器"
content: "你好 {player},欢迎加入服务器!有问题可以联系管理员。"
event:
subject: "活动通知"
content: "你好 {player},服务器有新活动开启,欢迎上线参与。"
上面示例把 moderation.enabled 设成了 false,是为了让插件开箱可发信;生产环境若要 AI 审核,请改成 true 并补齐 Key 与模型。
关键配置项说明
| 配置 | 作用 |
|---|---|
mail.max-inbox-size |
每个玩家的邮件上限,满了会拒绝新邮件 |
mail.subject-max-length / content-max-length |
主题、正文长度上限 |
mail.console-sender-name |
管理员 / 控制台邮件的发件人显示名;不要设成某个玩家名,否则真实邮件会把服务器邮件误标成玩家邮件 |
mail.console-notify-online |
在线收件人是否收到游戏内新邮件提示 |
smtp.from-name |
真实邮件里显示的服务器名,也是 HTML 模板标题栏和「来自」一行的文字 |
smtp.from-address |
实际发信地址,必须与 SMTP 账号一致,否则多数服务商会拒收 |
email-bind.auto-send-real-email |
是否把游戏邮件同步到已验证绑定邮箱,默认开启 |
sending.currency |
vault 或 playerpoints,必须与实际安装的经济插件一致 |
claim.command-template |
领取邮件触发的控制台命令模板 |
三、真实邮箱同步与 HTML 邮件
覆盖范围
同步默认开启。以下路径只要收件人有已验证绑定邮箱、SMTP 可用,都会额外发一封真实邮件:
- 玩家之间发信(GUI 写信、
/mail send、/mail sendex、模板邮件) - 管理员 GUI / 控制台
send、claim领取邮件 recent按最近上线时间筛选的群发(只对真正投递成功的收件人发送)- 外部插件调用
XvMailAPI的基础发信接口
游戏内投递始终优先,真实邮件失败不影响游戏内邮件。未绑定邮箱、SMTP 未启用、SMTP 投递失败时只保留游戏内邮件;失败原因写入后台日志,不会打扰收件人。关闭同步:把 auto-send-real-email 设为 false 后 /mailadmin reload。
发件人显示规则(1.7.0 起)
| 邮件来源 | 使用的模板与展示 | SMTP 发件人显示名 |
|---|---|---|
| 玩家 A → 玩家 B | Player Mail:圆形首字母头像 + 玩家 A 的 ID +「向你发送了一封邮件 · <服务器名>」 | 玩家A (via <服务器名>) |
| 管理员 / 控制台 / 系统 / 群发 / 验证码 | System Notice:系统通知 或 邮箱验证 标签 + 主题 + 正文 |
<服务器名> |
例如服务器名设为 teana server、玩家 Steve 给 Alex 发信,Alex 收到的邮件发件人显示 Steve (via teana server),头像圆里是 S,正文上方写「Steve / 向你发送了一封邮件 · teana server」。这样玩家互发不会再被误写成服务器发的邮件。判断依据是发件人名是否等于 console-sender-name 或「系统」,所以请保持 console-sender-name 是服务器身份名。
HTML 邮件模板(可自定义)
1.7.0 起使用两套独立的 HTML 模板,都是 Content-Type: text/html; charset=UTF-8,带 X-Mailer: xvmail/1.7.0:
| 模板 | 用于 | 文件 |
|---|---|---|
| Player Mail | 玩家发给玩家 | plugins/xvmail/templates/player-mail.html |
| System Notice | 管理员 / 控制台 / 系统邮件、群发、领取邮件、邮箱验证码 | plugins/xvmail/templates/system-mail.html |
首次启动时插件会把两份默认模板释放到 plugins/xvmail/templates/,之后优先读取数据目录里的文件,所以改样式、品牌名、页脚文案都不需要重新编译 JAR。改完执行 /mailadmin reload 即可生效(重载会重新读取模板与 SMTP 设置);如果模板文件读取失败,会回退到 JAR 内置版本并在后台记日志,两个文件都缺失时使用极简内置兜底模板。
占位符(方括号形式,插件一次性替换,替换值不会二次解析):
| 模板 | 占位符 | 内容 |
|---|---|---|
| 玩家邮件 | [player_name] |
发件玩家 ID |
| 玩家邮件 | [player_initial] |
发件玩家名首字母,用于圆形头像 |
| 玩家邮件 | [server_name] |
smtp.from-name |
| 玩家邮件 | [Article] |
邮件正文(转义后换行转 <br>) |
| 玩家邮件 | [Attachment_content] |
附件说明:有物品 / 有可领取奖励 / 无附件 |
| 两套模板 | [server_name] |
smtp.from-name,用于邮件标题、页脚、版权行和「来自」一行 |
| 两套模板 | [brand_first] / [brand_rest] |
smtp.from-name 按第一个空格拆成两段,用于双色品牌栏;无空格时前半为全名、后半为空 |
| 系统邮件 | [notice_type] |
系统通知 或 邮箱验证 |
| 系统邮件 | [notice_title] |
邮件主题 / 验证码标题 |
| 系统邮件 | [notice_content] |
正文或验证码高亮区块 |
模板里已经没有写死的服务器名:品牌栏、<title>、页脚、版权行全部取自 smtp.from-name,改配置即可全站生效;想显示成 Teana Server 这种大小写,把 smtp.from-name 设成对应写法即可。两套模板的版权区底部都加了一行「邮件系统由 xvmail 插件提供」。
升级行为:插件用 templates/template-version.txt 记录模板版本。从没有该文件的旧版本升级时,会自动把旧模板备份成 templates/*.old 并释放新模板,启动日志会提示;之后手动改过模板也不会再被覆盖(除非删除版本文件或升级到更高模板版本)。
玩家填写的主题、正文、玩家名、服务器名都会做 HTML 转义,作为文本显示,避免注入标签或脚本;因此玩家在游戏里写 <b> 不会变成加粗,这是刻意的安全设计。模板用了渐变、圆角、flex 等样式,Outlook 等客户端可能降级显示,建议实发到常用邮箱验收。
SMTP 配置要点
- QQ / 163 邮箱:通常
port: 465、use-ssl: true、use-tls: false,password填邮箱设置里生成的 SMTP 授权码,不是登录密码。 - Gmail / Outlook:通常用
port: 587、use-ssl: false、use-tls: true(STARTTLS),以服务商当前要求为准。 from-address必须与username一致;from-name只影响显示名,可自由设成服务器名。- 二者不要同时开
use-ssl与use-tls,容易握手失败。 - 大批量群发时 SMTP 任务队列上限为 128,超出的同步请求会被拒绝并在后台记日志,建议分批群发。
合规提示:邮件主题与正文会经由你选择的 SMTP 服务商,开启 AI 审核时还会发到第三方审核接口。请确认服务商数据政策、在服规里告知玩家,并妥善保管
config.yml(含 API Key 与邮箱授权码),不要公开或发给玩家。
四、邮箱绑定与验证码
/mail bindeamil <邮箱> 玩家申请验证码(兼容 /mail bindemail)
/mail verifyemail <验证码> 完成验证
/mail myemail 查询绑定
/mail unbindemail 解绑
- 只有验证成功才会写入
email_bindings.yml,结构为bindings.<uuid>.email/verified。 - 验证码有发送冷却(
send-cooldown-seconds)、有效期(code-expire-minutes)和错误次数上限(max-attempts),全部存在内存中;服务器重启或插件重载后验证码失效,需要重新申请。 - 验证码邮件发送失败时会提示玩家检查服主 SMTP 配置,且不会占用绑定数据。
- 关闭
email-bind.enabled后玩家无法申请绑定,但已有绑定仍可用于同步。
五、AI 内容审核
审核只作用于玩家发的邮件。管理员 / 控制台 send、管理员 GUI 写信、recent 群发、claim 领取邮件一律跳过本地规则与 AI,直接投递,后台会留一条 管理员邮件跳过内容审核 日志便于审计。这些路径仍受主题 / 正文长度、收件人存在性和收件箱容量限制。OP 权限请只给可信的人:管理员邮件可以带链接等内容且不受审核。
moderation:
enabled: true
local-rules: false
api-url: "https://api.openai.com/v1/chat/completions"
api-key: "替换为你的API密钥"
model: "你的审核模型"
timeout-ms: 8000
- 执行顺序:先跑本地规则(若开启),通过后才调用 AI。以前本地规则排在 AI 前面且不可关闭,命中就直接拒绝,AI 再正常也不会被调用——这是「AI 明明正常却一直发送失败」的最常见原因。现在
local-rules默认false,可由服主决定是否开启。 - 本地规则已收紧:域名只在常见顶级域名后缀下判定(
Hi.Hello不再误伤),宣传关键词去掉了「欢迎 / 加入」(服务器欢迎新人加入不再误伤);链接、IP、邮箱地址、以及「服务器 + IP / 地址 / 进群 / 群号 / 推广」仍会被拦。 - AI 判定已放宽:返回内容会去掉标点、Markdown 星号、空白再判断,包含
APPROVE且不含REJECT即放行,所以APPROVE.、**APPROVE**、审批结果:APPROVE都能通过。响应结构兼容choices[0].message.content、choices[0].delta.content、choices[0].text与顶层output_text。 - 超时、HTTP 错误、响应格式异常、Key / 模型为空,仍然一律拒绝(fail-closed)。
- 所有拒绝都会写日志:后台会打印
邮件被拒绝:发件人=… 收件人=… 原因=…,在线发件玩家也会收到一条具体原因提示;收件人不存在、内容超长、收件箱已满同样有日志。排查发送失败先看这条日志。 - 审核会把邮件主题和正文发给第三方,请按隐私政策告知玩家,不要泄露 API Key。
六、每日额度与付费发送
sending:
daily-free-limit: 10
daily-paid-limit: 20
currency: vault
paid-cost: 100.0
attachment-max-bytes: 32768
/mail send用免费额度,用完提示/mail sendex;付费发送另受daily-paid-limit限制。管理员邮件不占玩家额度。- 只有投递成功才保留额度与扣费;审核拒绝、收件人不存在、邮箱满、服务异常时会自动退回次数和费用,附带物品也会退回。
- Vault 只是桥接层,必须再装 EssentialsX / CMI 等经济实现;只装 Vault 会提示「Vault 未注册经济服务」。
- 用 PlayerPoints 请把
currency改成playerpoints;插件启动日志会在 Vault 不可用却检测到 PlayerPoints 时给出这条提示。 - 扣费失败时,玩家消息和后台日志都会显示具体原因(未安装 / 未注册经济服务 / 余额不足 / API 版本不兼容),不再只提示「请检查经济插件」。
- 退款使用离线玩家接口,玩家掉线时也会尝试退回并在失败时记日志。
七、模板与领取邮件
模板
templates 下每个条目含 subject 与 content,支持 {player}(收件人)和 {sender}(发件人)。玩家可在 GUI 模板菜单选择,或用 /mail template <模板名> <玩家名>。
领取邮件
/mailadmin claimcommand give {player} diamond 3
/mailadmin claim Alex 新手奖励 完成新手任务后点击领取
- 模板必须且只能包含一个
{player},只允许安全字符;分号、换行、引号、命令拼接字符会被拒绝。 - 玩家点击领取后,由控制台执行替换后的命令;领取状态先写盘再执行命令,双击不会重复发奖。
- 属于「最多执行一次」的取舍:若在写盘后、命令执行前服务器崩溃,可能出现已标记领取但奖励未发。建议把奖励命令设计成幂等,并保留发放日志。
- 同样地,如果模板后来被改坏或命令执行返回失败,玩家也无法重领;请只填写可信且稳定的命令。
八、管理员 GUI 与命令
/mailadmin(GUI)提供:发送邮件、管理指定玩家邮箱、重载配置、近期上线筛选、发送领取邮件、设置领取命令。
/mailadmin 管理主菜单(仅玩家可用 GUI)
/mailadmin send [online|offline] <玩家> <主题> <正文> 向在线/离线玩家投递
/mailadmin recent <天数> <主题> <正文> 近 N 天上线过的玩家群发(1–3650)
/mailadmin claim <玩家> <主题> <正文> 发送领取邮件
/mailadmin claimcommand <命令模板> 设置领取命令
/mailadmin listall <玩家> 查看该玩家邮件数/未读数
/mailadmin clearall <玩家> 清空该玩家邮箱
/mailadmin reload 重载配置并刷新 SMTP 设置
recent 依据服务器已知玩家的最后上线时间筛选;离线投递要求玩家曾加入过服务器。
九、数据文件与备份
| 文件 | 内容 |
|---|---|
mail/<uuid>.yml |
每个玩家的邮件(含附件、领取状态) |
mail/pending-attachments.yml |
发送失败 / 领取失败待退回的物品 |
email_bindings.yml |
已验证的邮箱绑定 |
daily_quotas.yml |
每日免费 / 付费发送计数 |
config.yml |
配置(含密钥,注意保密) |
建议停服后一起备份。所有文件写入都在有界后台线程池中进行,并采用「临时文件 + 原子替换」,避免写一半崩溃损坏数据。
十、性能与并发
- 邮件、绑定、额度读写使用独立的有界后台线程池;文件写盘采用临时文件原子替换。
- SMTP、验证码、AI HTTP 请求全部在后台线程执行,不阻塞游戏主线程;涉及玩家、GUI、控制台命令的 Bukkit 操作回到 Paper/Folia 调度器。
- 玩家主手物品在发送成功后才扣除;发送失败会退回,玩家离线时暂存到
pending-attachments.yml,下次登录补发。 - 已知取舍:删除 / 清空带未领取附件的邮件不会退回物品;SMTP 队列 128;验证码与最近上线记录存在内存中,重启后重建。
十一、排障速查
| 现象 | 常见原因 | 处理 |
|---|---|---|
| 所有玩家都发不出邮件 | moderation.enabled: true 但 Key / 模型为空,fail-closed |
配好审核服务,或明确设为 false |
| AI 正常却一直发送失败 | 本地规则先于 AI 命中;AI 返回不是纯 APPROVE;接口 HTTP / 结构异常 |
看后台 邮件被拒绝:… 原因=… 日志;确认 moderation.local-rules: false,按日志原因修配置 |
| 某句话发不出去 | 服主开启了 local-rules 时的本地粗筛误伤 |
关掉 local-rules,或换掉正文里的域名 / IP / 邮箱写法 |
| 提示「具体原因见服务器日志」 | 收件人不存在、主题/正文超长、收件箱已满 | 看后台 邮件投递失败 / 邮件未写入 日志 |
| 游戏里收到了,真实邮箱没有 | SMTP 未启用、玩家未绑定、同步被关、SMTP 队列满、投递失败 | 看启动日志与后台警告,逐项排查;检查垃圾邮件箱 |
| 真实邮件发件人显示成服务器名 | 该邮件确实是管理员 / 系统发的;或 console-sender-name 被设成玩家名 |
检查 mail.console-sender-name 与 smtp.from-name |
| 邮件看着不像 HTML | 客户端把 HTML 当纯文本显示,或用了只显示纯文本的客户端 | 查看邮件源码应有 Content-Type: text/html; charset=UTF-8 与 X-Mailer |
| 付费提示扣费失败 | 没装经济实现、currency 不匹配、余额不足、API 版本不兼容 |
看启动日志 付费发送货币=... 与扣费失败的具体原因 |
| 领取后没拿到奖励 | 命令模板失效、命令执行失败、服务器在写盘后崩溃 | 查后台 Claim redeemed 日志;奖励命令做成幂等 |
recent 群发有人没收到真实邮件 |
邮箱满未投递成功,或 SMTP 队列 128 被占满 | 分批群发,看后台队列拒绝日志 |
十二、已知限制与未验证项
- 存储为 YAML,没有实现 SQLite;也没有过期邮件自动清理。
- 领取奖励为「最多执行一次」,存在写盘后崩溃导致奖励未发的窗口。
- 删除带未领取附件的邮件会丢失物品。
- 外部
XvMailAPI只覆盖基础收发、未读查询、真实邮件与绑定查询,不含附件、领取邮件、群发、额度与审核管理。 - 编译目标为 Paper API 1.21.4,源码声明 Folia 调度支持;未在真实 Paper/Folia 服务器完成装载测试,也没有自动化测试。上线前请在测试服验证启动/关闭、在线与离线收件、GUI 点击路径、SMTP 凭据、经济扣费与退款、附件与领取的并发路径。
- 兼容范围只承诺已核对到的编译目标与源码声明,不建议宣传「兼容所有 1.21.x–26.x」。
十三、升级与重载
config.yml改动后执行/mailadmin reload,会重新读取配置并刷新 SMTP 设置;模板、额度、审核配置即时生效。- 领取命令模板通过
/mailadmin claimcommand或 GUI 设置,会写回config.yml。 - 升级到 1.7.0 后的首次启动会执行
config-version: 2迁移(详见第二节)。

