From a51e841fc92d9805aa6cc5091ac455df9a8ffad2 Mon Sep 17 00:00:00 2001 From: canglan Date: Mon, 13 Jul 2026 03:35:13 +0800 Subject: [PATCH] refactor: English subcommands, plugin login/track redesign, clean external API routes --- backend/routes/external.js | 101 ++++----- .../java/com/mcreport/plugin/ApiClient.java | 8 +- .../com/mcreport/plugin/MCReportPlugin.java | 5 + .../com/mcreport/plugin/ReportCommand.java | 196 ++++++++++-------- .../src/main/resources/plugin.yml | 19 +- 5 files changed, 163 insertions(+), 166 deletions(-) diff --git a/backend/routes/external.js b/backend/routes/external.js index 1a11c19..a632bb1 100644 --- a/backend/routes/external.js +++ b/backend/routes/external.js @@ -2,13 +2,11 @@ const express = require('express'); const bcrypt = require('bcryptjs'); const { v4: uuid } = require('uuid'); const { query, getRow, getConfig } = require('../db'); -const { generateToken } = require('../middleware/auth'); +const { generateToken, authenticate } = require('../middleware/auth'); const { sendEmail } = require('../mailer'); const router = express.Router(); -const { getConfig } = require('../db'); - function getExternalKey() { try { return getConfig()?.external_api_key || process.env.EXTERNAL_API_KEY || ''; } catch { return process.env.EXTERNAL_API_KEY || ''; } } @@ -20,6 +18,7 @@ function externalAuth(req, res, next) { return res.status(401).json({ error: '未授权' }); } +// ---- Auth (no external key needed) ---- router.post('/auth/register', async (req, res) => { try { const { username, password, email, game_name, game_uid } = req.body; @@ -29,14 +28,10 @@ router.post('/auth/register', async (req, res) => { if (await getRow('SELECT id FROM users WHERE email = ?', [email])) return res.status(400).json({ error: '邮箱已注册' }); const hashed = bcrypt.hashSync(password, 10); - const verifyToken = uuid(); - await query('INSERT INTO users(username,password,email,game_name,game_uid,verify_token,verify_expires) VALUES (?,?,?,?,?,?,DATE_ADD(NOW(), INTERVAL 24 HOUR))', [username, hashed, email, game_name, game_uid, verifyToken]); + await query('INSERT INTO users(username,password,email,game_name,game_uid,verify_token,verify_expires) VALUES (?,?,?,?,?,?,DATE_ADD(NOW(), INTERVAL 24 HOUR))', [username, hashed, email, game_name, game_uid, uuid()]); const site = await getRow("SELECT v FROM settings WHERE k='site_url'"); - const sent = await sendEmail(email, 'verify_email', { - username, game_name, game_uid, - verify_link: `${site?.v||'http://localhost:3100'}#/verify?token=${verifyToken}`, - }); + const sent = await sendEmail(email, 'verify_email', { username, game_name, game_uid, verify_link: `${site?.v||'http://localhost:3100'}#/verify?token=${uuid()}` }); if (!sent) { await query('UPDATE users SET email_verified=1,active=1,verify_token=NULL WHERE username=?', [username]); @@ -54,78 +49,66 @@ router.post('/auth/login', async (req, res) => { if (!user || !bcrypt.compareSync(password, user.password)) return res.status(401).json({ error: '用户名或密码错误' }); if (!user.active) return res.status(403).json({ error: '账号未激活' }); const token = generateToken(user); - res.json({ token, user: { id: user.id, username: user.username, game_name: user.game_name, game_uid: user.game_uid, role: user.role } }); + res.json({ token, user: { id:user.id, username:user.username, game_name:user.game_name, game_uid:user.game_uid, role:user.role } }); } catch (e) { res.status(500).json({ error: '服务器错误' }); } }); +// ---- Plugin API (JWT auth) ---- router.use(externalAuth); +router.get('/my-tickets', authenticate, async (req, res) => { + const { limit } = req.query; + const rows = await query(`SELECT id, type, title, status, priority, created_at, updated_at FROM tickets WHERE user_id = ? ORDER BY updated_at DESC LIMIT ?`, [req.user.id, Math.min(parseInt(limit)||10, 50)]); + res.json({ list: rows }); +}); + +router.get('/my-tickets/:id', authenticate, async (req, res) => { + const t = await getRow(`SELECT id, type, title, status, priority, reporter_game_name, reporter_game_uid, + target_game_name, target_game_uid, reason, description, assigned_to, claim_note, created_at, updated_at + FROM tickets WHERE id = ? AND user_id = ?`, [req.params.id, req.user.id]); + if (!t) return res.status(404).json({ error: '工单不存在' }); + res.json(t); +}); + router.post('/tickets', async (req, res) => { - const { type, title, reporter_game_name, reporter_game_uid, - target_game_name, target_game_uid, reason, description } = req.body; - + const { type, title, reporter_game_name, reporter_game_uid, target_game_name, target_game_uid, reason, description, is_admin_complaint } = req.body; if (!type || !['report','suggestion','appeal'].includes(type)) return res.status(400).json({ error: '类型不正确' }); - if (!title) return res.status(400).json({ error: '标题不能为空' }); - if (!reporter_game_name || !reporter_game_uid) return res.status(400).json({ error: '请填写游戏名称和UID' }); - - if (type === 'report') { - if (!reason) return res.status(400).json({ error: '请填写举报原因' }); - } - if (type === 'suggestion' && !description) return res.status(400).json({ error: '建议内容不能为空' }); - if (type === 'appeal') { - if (!reason) return res.status(400).json({ error: '请填写申诉理由' }); - if (!description) return res.status(400).json({ error: '请填写详细申诉内容' }); - } + if (!title || !reporter_game_name || !reporter_game_uid) return res.status(400).json({ error: '必填字段不完整' }); const countRow = await getRow("SELECT COUNT(*) as c FROM tickets WHERE (reporter_game_name = ? OR reporter_game_uid = ?) AND status IN ('pending','processing','awaiting_info','appealing')", [reporter_game_name, reporter_game_uid]); if (countRow.c >= 5) return res.status(400).json({ error: '待处理工单已达上限' }); - const trackingToken = require('uuid').v4(); - const [r] = await query(`INSERT INTO tickets(type,title,reporter_game_name,reporter_game_uid, - target_game_name,target_game_uid,reason,description,tracking_token) VALUES (?,?,?,?,?,?,?,?,?)`, - [type, title, reporter_game_name, reporter_game_uid, - (type==='report')?(target_game_name||''):null, - (type==='report')?(target_game_uid||''):null, - (type==='report'||type==='appeal')?reason:null, - (type==='suggestion'||type==='appeal')?description:'', - trackingToken]); - - await query("INSERT INTO responses(ticket_id, content, is_staff) VALUES (?,?,0)", [r.insertId, `游戏内${type==='report'?'举报':type==='appeal'?'申诉':'建议'}\n提交人: ${reporter_game_name} (UID: ${reporter_game_uid})\n${reason||description||''}`]); - + const trackingToken = uuid(); + const [r] = await query(`INSERT INTO tickets(type,title,reporter_game_name,reporter_game_uid,target_game_name,target_game_uid,reason,description,tracking_token,is_admin_complaint) VALUES (?,?,?,?,?,?,?,?,?,?,?)`, + [type, title, reporter_game_name, reporter_game_uid, target_game_name||'', target_game_uid||'', reason||'', description||'', trackingToken, is_admin_complaint?1:0]); + await query("INSERT INTO responses(ticket_id,content,is_staff) VALUES (?,?,0)", [r.insertId, `游戏内提交\n类型: ${type}\n提交人: ${reporter_game_name} (UID: ${reporter_game_uid})`]); res.status(201).json({ id: r.insertId, tracking_token: trackingToken }); }); -router.get('/tickets', async (req, res) => { +// ---- Java Server API (x-external-key only) ---- +router.get('/all-tickets', async (req, res) => { const { type, status, page, limit } = req.query; - let q = `SELECT t.id, t.type, t.title, t.status, t.priority, - t.reporter_game_name, t.reporter_game_uid, - t.target_game_name, t.target_game_uid, - t.reason, t.description, t.assigned_to, t.claim_note, - t.created_at, t.updated_at - FROM tickets t WHERE 1=1`; + let q = `SELECT id, type, title, status, priority, reporter_game_name, reporter_game_uid, target_game_name, target_game_uid, reason, description, assigned_to, claim_note, created_at, updated_at FROM tickets WHERE 1=1`; const p = []; - if (type) { q += ' AND t.type = ?'; p.push(type); } - if (status) { q += ' AND t.status = ?'; p.push(status); } + if (type) { q += ' AND type = ?'; p.push(type); } + if (status) { q += ' AND status = ?'; p.push(status); } const pg = parseInt(page) || 1; const lm = Math.min(parseInt(limit) || 50, 200); - q += ` ORDER BY t.updated_at DESC LIMIT ${(pg-1)*lm}, ${lm}`; + q += ` ORDER BY updated_at DESC LIMIT ${(pg-1)*lm}, ${lm}`; res.json(await query(q, p)); }); -router.get('/tickets/:id', async (req, res) => { - const t = await getRow(`SELECT id, type, title, status, priority, - reporter_game_name, reporter_game_uid, target_game_name, target_game_uid, - reason, description, assigned_to, claim_note, - created_at, updated_at FROM tickets WHERE id = ?`, [req.params.id]); - if (!t) return res.status(404).json({ error: '工单不存在' }); +router.get('/all-tickets/:id', async (req, res) => { + const t = await getRow('SELECT * FROM tickets WHERE id = ?', [req.params.id]); + if (!t) return res.status(404).json({ error: 'Not found' }); t.responses = await query('SELECT content, is_staff, created_at FROM responses WHERE ticket_id = ? ORDER BY created_at ASC', [req.params.id]); t.attachments = await query('SELECT id, original_name, mime_type, size FROM attachments WHERE ticket_id = ?', [req.params.id]); res.json(t); }); router.get('/reported-players', async (req, res) => { - const rows = await query(`SELECT target_game_name, target_game_uid, - COUNT(*) as total, SUM(CASE WHEN status IN ('pending','processing','awaiting_info','appealing') THEN 1 ELSE 0 END) as active, + const rows = await query(`SELECT target_game_name, target_game_uid, COUNT(*) as total, + SUM(CASE WHEN status IN ('pending','processing','awaiting_info','appealing') THEN 1 ELSE 0 END) as active, SUM(CASE WHEN status='resolved' THEN 1 ELSE 0 END) as resolved, MAX(created_at) as last_report FROM tickets WHERE type='report' AND target_game_name IS NOT NULL AND target_game_name != '' @@ -142,14 +125,4 @@ router.get('/stats', async (req, res) => { res.json({ total, byType, byStatus }); }); -router.get('/users', async (req, res) => { - res.json(await query('SELECT id, username, email, game_name, game_uid, role, active, created_at FROM users ORDER BY id')); -}); - -router.get('/user/:id', async (req, res) => { - const u = await getRow('SELECT id, username, email, game_name, game_uid, role, active, created_at FROM users WHERE id = ?', [req.params.id]); - if (!u) return res.status(404).json({ error: '用户不存在' }); - res.json(u); -}); - module.exports = router; diff --git a/mc-report-plugin/src/main/java/com/mcreport/plugin/ApiClient.java b/mc-report-plugin/src/main/java/com/mcreport/plugin/ApiClient.java index 789ec8f..34ec93a 100644 --- a/mc-report-plugin/src/main/java/com/mcreport/plugin/ApiClient.java +++ b/mc-report-plugin/src/main/java/com/mcreport/plugin/ApiClient.java @@ -91,7 +91,11 @@ public class ApiClient { return request("POST", "/tickets", body, token); } - public JsonObject trackTicket(String trackingToken) { - return request("GET", "/tickets/tracking?token=" + trackingToken, null, null); + public JsonObject getMyTickets(String token) { + return request("GET", "/my-tickets?limit=10", null, token); + } + + public JsonObject getTicket(String token, String id) { + return request("GET", "/my-tickets/" + id, null, token); } } diff --git a/mc-report-plugin/src/main/java/com/mcreport/plugin/MCReportPlugin.java b/mc-report-plugin/src/main/java/com/mcreport/plugin/MCReportPlugin.java index d68b2a9..d5597de 100644 --- a/mc-report-plugin/src/main/java/com/mcreport/plugin/MCReportPlugin.java +++ b/mc-report-plugin/src/main/java/com/mcreport/plugin/MCReportPlugin.java @@ -9,6 +9,7 @@ public final class MCReportPlugin extends JavaPlugin { private ApiClient apiClient; private final Map sessions = new HashMap<>(); + private final Map loginNames = new HashMap<>(); private List adminPerms; @Override @@ -42,8 +43,12 @@ public final class MCReportPlugin extends JavaPlugin { public void removeToken(Player player) { sessions.remove(player.getUniqueId()); + loginNames.remove(player.getUniqueId()); } + public void setLoginName(Player player, String name) { loginNames.put(player.getUniqueId(), name); } + public String getLoginName(Player player) { return loginNames.getOrDefault(player.getUniqueId(), player.getName()); } + public boolean isOnlineAdmin(Player target) { if (target.isOp()) return true; for (String perm : adminPerms) { diff --git a/mc-report-plugin/src/main/java/com/mcreport/plugin/ReportCommand.java b/mc-report-plugin/src/main/java/com/mcreport/plugin/ReportCommand.java index f8fe05f..5939493 100644 --- a/mc-report-plugin/src/main/java/com/mcreport/plugin/ReportCommand.java +++ b/mc-report-plugin/src/main/java/com/mcreport/plugin/ReportCommand.java @@ -1,5 +1,6 @@ package com.mcreport.plugin; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import org.bukkit.Bukkit; import org.bukkit.command.Command; @@ -7,7 +8,7 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import java.util.Arrays; +import java.util.*; public class ReportCommand implements CommandExecutor { @@ -20,7 +21,7 @@ public class ReportCommand implements CommandExecutor { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player player)) { - sender.sendMessage("§c仅玩家可使用此命令"); + sender.sendMessage("§cPlayer only"); return true; } @@ -29,99 +30,76 @@ public class ReportCommand implements CommandExecutor { return true; } - String sub = args[0].toLowerCase(); - - switch (sub) { - case "login": return handleLogin(player, args); + switch (args[0].toLowerCase()) { case "reg": case "register": return handleRegister(player, args); - case "track": return handleTrack(player, args); - case "举报": return handleReport(player, args, "report"); - case "建议": return handleReport(player, args, "suggestion"); - case "事项": return handleReport(player, args, "report"); + case "login": return handleLogin(player, args); + case "report": case "r": return handleSubmit(player, args, "report"); + case "suggest": case "s": return handleSubmit(player, args, "suggestion"); + case "track": case "t": return handleTrack(player, args); default: showHelp(player); return true; } } private void showHelp(Player player) { - player.sendMessage("§6===== MC举报系统 ====="); - String token = plugin.getToken(player); - if (token == null) { - player.sendMessage("§e/report reg <密码> <邮箱> §7- 注册账号"); - player.sendMessage("§e/report login <密码> §7- 登录"); + if (plugin.getToken(player) == null) { + player.sendMessage("§6===== MCReport ====="); + player.sendMessage("§e/report reg [username] §7- Register"); + player.sendMessage("§e/report login [username] §7- Login"); } else { - player.sendMessage("§e/report 举报 [玩家名] <原因> §7- 举报玩家"); - player.sendMessage("§e/report 建议 <内容> §7- 提交建议"); - player.sendMessage("§e/report 事项 <内容> §7- 事项提交"); - player.sendMessage("§e/report track <工单ID> §7- 追踪进度"); + player.sendMessage("§6===== MCReport ====="); + player.sendMessage("§e/report report [player] §7- Report a player"); + player.sendMessage("§e/report suggest §7- Submit suggestion"); + player.sendMessage("§e/report track [id] §7- List/view tickets"); } } private boolean handleLogin(Player player, String[] args) { - if (args.length < 2) { - player.sendMessage("§c用法: /report login <密码>"); - return true; - } - String password = args[1]; - String username = player.getName(); + if (args.length < 2) { player.sendMessage("§cUsage: /report login [username]"); return true; } + String pwd = args[1]; + String username = args.length > 2 ? args[2] : player.getName(); - player.sendMessage("§7登录中..."); + player.sendMessage("§7Logging in..."); Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { - JsonObject res = plugin.getApiClient().login(username, password); + JsonObject res = plugin.getApiClient().login(username, pwd); int status = res.get("_status").getAsInt(); if (status == 200) { - String token = res.get("token").getAsString(); - plugin.setToken(player, token); - player.sendMessage("§a登录成功!现在可以使用 /report 提交举报"); + plugin.setToken(player, res.get("token").getAsString()); + plugin.setLoginName(player, username); + player.sendMessage("§aLogged in as §e" + username); } else { - String err = res.has("error") ? res.get("error").getAsString() : "未知错误"; - player.sendMessage("§c登录失败: " + err); + player.sendMessage("§cLogin failed: " + res.get("error").getAsString()); } }); return true; } private boolean handleRegister(Player player, String[] args) { - if (args.length < 3) { - player.sendMessage("§c用法: /report reg <密码> <邮箱>"); - return true; - } - String password = args[1]; + if (args.length < 3) { player.sendMessage("§cUsage: /report reg [username]"); return true; } + String pwd = args[1]; String email = args[2]; + String username = args.length > 3 ? args[3] : player.getName(); - if (password.length() < 6) { - player.sendMessage("§c密码至少6位"); - return true; - } + if (pwd.length() < 6) { player.sendMessage("§cPassword must be at least 6 characters"); return true; } - player.sendMessage("§7注册中..."); + player.sendMessage("§7Registering..."); Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { JsonObject res = plugin.getApiClient().register( - player.getName(), password, email, + username, pwd, email, player.getName(), player.getUniqueId().toString() ); int status = res.get("_status").getAsInt(); if (status == 201) { - String msg = res.get("message").getAsString(); - player.sendMessage("§a" + msg); - if (msg.contains("自动激活")) { - player.sendMessage("§e账号已激活,请使用 §a/report login <密码> §e登录"); - } else { - player.sendMessage("§e请查收邮箱验证邮件后使用 §a/report login <密码> §e登录"); - } + player.sendMessage("§a" + res.get("message").getAsString()); + player.sendMessage("§6Username: §e" + username + " §6Game: §e" + player.getName()); } else { - String err = res.has("error") ? res.get("error").getAsString() : "未知错误"; - player.sendMessage("§c注册失败: " + err); + player.sendMessage("§cRegister failed: " + res.get("error").getAsString()); } }); return true; } - private boolean handleReport(Player player, String[] args, String type) { - String token = plugin.getToken(player); - if (token == null) { - player.sendMessage("§c请先登录: /report login <密码>"); - return true; - } + private boolean handleSubmit(Player player, String[] args, String type) { + if (plugin.getToken(player) == null) { player.sendMessage("§cLogin first: /report login "); return true; } int contentStart = 1; String targetName = null; @@ -139,59 +117,97 @@ public class ReportCommand implements CommandExecutor { } if (args.length <= contentStart) { - player.sendMessage("§c请填写" + (type.equals("report") ? "举报原因" : type.equals("suggestion") ? "建议内容" : "事项内容")); + player.sendMessage("§cPlease provide " + (type.equals("report")?"a reason":"content")); return true; } String content = String.join(" ", Arrays.copyOfRange(args, contentStart, args.length)); - String reason = type.equals("report") ? content : null; - String description = type.equals("suggestion") ? content : (type.equals("report") ? null : content); - String title = targetName != null - ? "游戏内举报: " + targetName + ? "In-game report: " + targetName : type.equals("suggestion") - ? "游戏内建议: " + player.getName() - : "游戏内举报: " + player.getName(); + ? "In-game suggestion: " + player.getName() + : "In-game report: " + player.getName(); - player.sendMessage("§7提交中..."); + player.sendMessage("§7Submitting..."); Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { JsonObject res = plugin.getApiClient().submitTicket( - token, type, title, + plugin.getToken(player), type, title, player.getName(), player.getUniqueId().toString(), - targetName, targetUid, reason, description, isAdminComplaint + targetName, targetUid, + type.equals("report")?content:null, + type.equals("suggestion")?content:null, + isAdminComplaint ); - int status = res.get("_status").getAsInt(); - if (status == 200 || status == 201) { - int id = res.get("id").getAsInt(); - String tracking = res.get("tracking_token").getAsString(); - player.sendMessage("§a提交成功!工单编号: §e#" + id); - player.sendMessage("§7追踪码: " + tracking.substring(0, 8) + "..." ); - player.sendMessage("§7可使用 /report track " + id + " 查询进度"); + if (res.get("_status").getAsInt() == 200 || res.get("_status").getAsInt() == 201) { + player.sendMessage("§aSubmitted! Ticket §e#" + res.get("id").getAsInt()); + player.sendMessage("§7Use §e/report track §7to check progress"); } else { - String err = res.has("error") ? res.get("error").getAsString() : "未知错误"; - player.sendMessage("§c提交失败: " + err); + player.sendMessage("§cFailed: " + res.get("error").getAsString()); } }); return true; } private boolean handleTrack(Player player, String[] args) { - if (args.length < 2) { - player.sendMessage("§c用法: /report track <工单ID>"); - return true; - } - String trackingToken = args[1]; - player.sendMessage("§7查询中..."); + if (plugin.getToken(player) == null) { player.sendMessage("§cLogin first"); return true; } + + player.sendMessage("§7Loading..."); Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { - JsonObject res = plugin.getApiClient().trackTicket(trackingToken); - // The API returns an array, we wrap in a JSON object - if (res.has("error")) { - player.sendMessage("§c工单不存在或无权查看"); - return; + if (args.length > 1) { + JsonObject res = plugin.getApiClient().getTicket(plugin.getToken(player), args[1]); + showTicketDetail(player, res); + } else { + JsonObject res = plugin.getApiClient().getMyTickets(plugin.getToken(player)); + showTicketList(player, res); } - player.sendMessage("§6===== 工单列表 ====="); - player.sendMessage("§e可通过 Web 端查看详情"); }); return true; } + + private void showTicketList(Player player, JsonObject res) { + if (res.get("_status").getAsInt() != 200 || !res.has("list")) { + player.sendMessage("§cFailed to load tickets"); + return; + } + JsonArray list = res.getAsJsonArray("list"); + if (list.size() == 0) { + player.sendMessage("§7No tickets yet"); + return; + } + player.sendMessage("§6===== Your Tickets (last " + list.size() + ") ====="); + for (int i = 0; i < list.size(); i++) { + JsonObject t = list.get(i).getAsJsonObject(); + String type = t.get("type").getAsString(); + String typeLabel = type.equals("report")?"雷":type.equals("suggestion")?"灯":type.equals("appeal")?"锤":"?"; + String status = t.get("status").getAsString(); + String statusColor = ""; + switch (status) { + case "pending": statusColor = "§e"; break; + case "processing": statusColor = "§d"; break; + case "resolved": statusColor = "§a"; break; + case "rejected": statusColor = "§c"; break; + case "closed": statusColor = "§7"; break; + case "awaiting_info": statusColor = "§6"; break; + case "appealing": statusColor = "§c"; break; + } + player.sendMessage(" §e#" + t.get("id").getAsInt() + " §7" + statusColor + status + " §f" + t.get("title").getAsString().substring(0, Math.min(30, t.get("title").getAsString().length()))); + } + player.sendMessage("§7Use §e/report track §7for details"); + } + + private void showTicketDetail(Player player, JsonObject res) { + if (res.get("_status").getAsInt() != 200 || res.has("error")) { + player.sendMessage("§cTicket not found"); + return; + } + JsonObject t = res; + player.sendMessage("§6===== Ticket #" + t.get("id").getAsInt() + " ====="); + player.sendMessage("§eTitle: §f" + t.get("title").getAsString()); + player.sendMessage("§eStatus: §f" + t.get("status").getAsString()); + player.sendMessage("§eType: §f" + t.get("type").getAsString()); + if (t.has("assigned_to") && !t.get("assigned_to").isJsonNull()) { + player.sendMessage("§eHandler: §f#" + t.get("assigned_to").getAsInt()); + } + player.sendMessage("§eDate: §7" + t.get("created_at").getAsString()); + } } diff --git a/mc-report-plugin/src/main/resources/plugin.yml b/mc-report-plugin/src/main/resources/plugin.yml index 13fad97..cff75c9 100644 --- a/mc-report-plugin/src/main/resources/plugin.yml +++ b/mc-report-plugin/src/main/resources/plugin.yml @@ -3,22 +3,21 @@ version: 1.0.0 main: com.mcreport.plugin.MCReportPlugin api-version: 1.20 author: Sea-Studio -description: MC举报系统 - 游戏内举报/建议/注册登录一体化插件 +description: MC Report System Plugin commands: report: - description: 游戏内举报/建议/账号管理 + description: Game reporting & account management usage: | - §6===== MC举报系统 ===== - §e/report login <密码> §7- 登录已有账号 - §e/report reg <密码> <邮箱> §7- 注册新账号 - §e/report 举报 [目标] <原因> §7- 举报玩家 - §e/report 建议 <内容> §7- 提交建议 - §e/report 事项 <内容> §7- 事项提交 - §e/report track <工单ID> §7- 追踪进度 + §6===== MCReport ===== + §e/report reg [username] §7- Register + §e/report login [username] §7- Login + §e/report report [player] §7- Report player + §e/report suggest §7- Submit suggestion + §e/report track [id] §7- Track tickets aliases: [jubao,举报] permissions: mcreport.use: - description: 使用举报功能 + description: Use report functions default: true