Files
MC_Report/docs/PLUGIN-GUIDE.md
canglan 356d7ab5ab chore: remove plugin from repo, add plugin guide, docs + AGPLv3 license
- mc-report-plugin removed from git tracking (kept locally), gitignored;
  plugin code no longer distributed (old x-external-key auth, outdated)
- docs/PLUGIN-GUIDE.md: Java plugin integration guide using new
  ID+Secret -> SESSION auth (session mgmt, submit ticket, track, bans)
- README: updated external API table (SESSION), dynamic sources,
  removed obsolete plugin commands/tech row, new dir structure, doc links
- INSTALL: drop restart requirement, add upgrade-migration/log/sources
  sections, license section
- LICENSE: GNU AGPL v3 full text
- verified: 24 checks (git tracking, docs consistency, license integrity)
2026-08-19 20:23:04 +08:00

149 lines
6.0 KiB
Markdown

# MC 插件对接指南(外部 API)
本文面向 Bukkit/Paper/Spigot 插件开发者,演示如何用 Java 对接 MC Report 外部 API(提交举报、同步封禁、查询进度)。完整接口定义见 [EXTERNAL-API.md](EXTERNAL-API.md)。
> 仓库不再内置插件代码 —— 按本指南即可自行实现;凭据(ID+Secret)在站点后台「外部API」页创建。
---
## 一、鉴权流程(Java 实现)
外部 API 用 **ID + Secret 换取 SESSION**,之后所有请求带 `Authorization: Bearer <SESSION>`
```java
public class McReportApi {
private final String baseUrl; // 如 https://mc.example.com/api/external
private final String clientId; // 后台创建: 16 位数字
private final String clientSecret; // 后台创建: 32 位随机串
private String sessionToken;
private long sessionExpiresAt; // 到期时间戳(ms)
public McReportApi(String baseUrl, String clientId, String clientSecret) {
this.baseUrl = baseUrl;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
/** 获取有效 SESSION, 过期则自动换新 */
public synchronized String session() throws Exception {
if (sessionToken != null && System.currentTimeMillis() < sessionExpiresAt) {
return sessionToken;
}
JSONObject body = new JSONObject();
HttpURLConnection conn = open("POST", "/auth/session", null);
conn.setRequestProperty("x-api-client-id", clientId);
conn.setRequestProperty("x-api-secret", clientSecret);
conn.setDoOutput(true);
conn.getOutputStream().write(body.toString().getBytes(StandardCharsets.UTF_8));
JSONObject resp = readJson(conn);
sessionToken = resp.getString("session_token");
sessionExpiresAt = System.currentTimeMillis() + resp.getLong("expires_in") * 1000L - 60_000L; // 提前1分钟过期
return sessionToken;
}
private HttpURLConnection open(String method, String path, String token) throws Exception {
HttpURLConnection conn = (HttpURLConnection) new URL(baseUrl + path).openConnection();
conn.setRequestMethod(method);
conn.setConnectTimeout(5000);
conn.setReadTimeout(10000);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("User-Agent", "MCReportPlugin/1.0");
if (token != null) conn.setRequestProperty("Authorization", "Bearer " + token);
return conn;
}
private JSONObject readJson(HttpURLConnection conn) throws Exception {
int code = conn.getResponseCode();
InputStream in = code >= 200 && code < 300 ? conn.getInputStream() : conn.getErrorStream();
String text = new String(in.readAllBytes(), StandardCharsets.UTF_8);
JSONObject json = new JSONObject(text);
if (code >= 400) throw new RuntimeException(json.optString("error", "HTTP " + code));
return json;
}
}
```
## 二、提交举报工单
```java
/** 玩家在游戏内执行 /report 命令时调用 */
public JSONObject submitReport(String reporterName, String targetName, String reason,
String serverName, String description) throws Exception {
String token = session();
JSONObject body = new JSONObject();
body.put("type", "report");
body.put("title", "游戏内举报: " + targetName);
body.put("reporter_game_name", reporterName);
body.put("target_game_name", targetName);
body.put("reason", reason);
body.put("description", description);
body.put("server", serverName); // 子服别名或「分组/子服」
HttpURLConnection conn = open("POST", "/tickets", token);
conn.setDoOutput(true);
conn.getOutputStream().write(body.toString().getBytes(StandardCharsets.UTF_8));
JSONObject resp = readJson(conn);
// resp: {"id": 123, "tracking_token": "..."}
return resp;
}
```
**返回的 `tracking_token` 请保存**(例如写进玩家持久数据),玩家可用它查询处理进度:
```java
public JSONObject trackTicket(String trackingToken) throws Exception {
return readJson(open("GET", "/tickets/track?token=" + URLEncoder.encode(trackingToken, "UTF-8"), session()));
}
```
## 三、拉取/同步封禁
```java
/** 定时任务: 每 5 分钟同步一次本服生效封禁 */
public JSONArray fetchActiveBans(String serverName) throws Exception {
String token = session();
String path = "/bans?status=active&limit=500"
+ (serverName != null && !serverName.isEmpty()
? "&server=" + URLEncoder.encode(serverName, "UTF-8") : "");
return readJson(open("GET", path, token)).getJSONArray("data");
}
/** 服务器内封禁玩家后同步到系统 */
public JSONObject createBan(String playerName, String reason, String type,
String duration, String serverName) throws Exception {
JSONObject body = new JSONObject();
body.put("player_name", playerName);
body.put("reason", reason);
body.put("type", type); // ban / mute / warn
body.put("duration", duration); // 如 "7d", 空=永久
body.put("server", serverName);
return readJson(open("POST", "/bans", session())); // 需带 body
}
```
> 注意:`POST /bans` 需设置 `setDoOutput(true)` 并写入 body(与 submitReport 相同写法)。
## 四、配置
`plugins/<你的插件>/config.yml`:
```yaml
# MC Report 外部 API 配置
api_url: "https://mc.example.com/api/external"
client_id: "1829473056482917" # 后台「外部API」创建
client_secret: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
server_name: "survival" # 本服别名(后台服务器管理配置)
```
---
## 常见问题
| 问题 | 处理 |
|------|------|
| `401` | ID/Secret 错误或客户端被停用;检查后台「外部API」 |
| `401 SESSION 无效或已过期` | 重新调用 `/auth/session` 换取(示例代码已自动处理) |
| `400 待处理工单已达上限` | 同一玩家待处理工单最多 5 个 |
| 换服/改名 | 用 `server` 参数按子服隔离数据;玩家身份以 `game_name` 为准 |