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)
This commit is contained in:
148
docs/PLUGIN-GUIDE.md
Normal file
148
docs/PLUGIN-GUIDE.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# 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` 为准 |
|
||||
Reference in New Issue
Block a user