跳到主要内容

进阶教程 - 实用功能开发

在完成基础入门教程后,你已经学会了如何创建一个简单的插件。本教程将介绍更多高级功能,帮助你开发更强大的插件。

1. 使用 Network API 调用外部接口

network API 是 EasyBot 提供的 HTTP 客户端,支持 GET、POST、PUT、DELETE 等常见请求方法。

基础请求

/// <reference path="easybot-sdk/easybot.d.ts" />

// 发送 GET 请求
async function fetchData() {
const response = await network.get("https://api.example.com/data");
if (response.status === 200) {
logger.info("获取数据成功");
return response.data; // 自动 JSON 解析
}
logger.error(`请求失败,状态码: ${response.status}`);
return null;
}

发送 POST 请求

async function submitData(data) {
const response = await network.post(
"https://api.example.com/submit",
JSON.stringify(data)
);
return response.status === 200;
}

自定义请求头

async function apiCallWithAuth() {
const response = await network.request({
url: "https://api.example.com/protected",
method: "GET",
headers: {
"Authorization": "Bearer your-api-token",
"Content-Type": "application/json"
}
});
return response.data;
}

2. 使用 WebKit API 创建自定义页面

WebKit API 允许插件在 EasyBot 的 Web 管理界面中注册自定义页面和菜单。

注册一个简单页面

bus.on("menu_ready", (pluginMenu) => {
pluginMenu.register(
"hello_world", // ID
"/pages/hello", // 页面路径
"你好世界", // 菜单标题
"HomeOutlined", // 图标
(action) => { // 处理前端交互
if (action.method === "greet") {
return { message: `你好,${action.parameters.name}` };
}
},
(sessionId) => { // 返回页面HTML
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>你好世界</title>
<style>
body { font-family: sans-serif; padding: 40px; text-align: center; }
input { padding: 8px; font-size: 16px; width: 200px; }
button { padding: 8px 16px; font-size: 16px; margin-left: 8px; }
#result { margin-top: 20px; font-size: 18px; color: #333; }
</style>
</head>
<body>
<h1>👋 你好世界</h1>
<p>输入你的名字:</p>
<input id="nameInput" type="text" placeholder="你的名字" />
<button onclick="sayHello()">打招呼</button>
<div id="result"></div>
<script>
async function sayHello() {
const name = document.getElementById('nameInput').value;
// 调用后端方法
const result = await window.webuikit.action('greet', { name });
document.getElementById('result').textContent = result.message;
}
</script>
</body>
</html>`;
},
null, // 父菜单(根级)
true, // 默认可见
10, // 排序
false // 不锁定
);
});

3. 使用 Archive API 读取插件文件

archive API 允许多文件插件读取自身包内的资源文件。

读取配置文件

function loadSettings() {
if (!archive.supportReadFile()) {
logger.warning("单文件插件,使用默认配置");
return { enabled: true, threshold: 100 };
}

const configJson = archive.readString("config/settings.json");
if (configJson) {
try {
return JSON.parse(configJson);
} catch (e) {
logger.error(`配置解析失败: ${e}`);
}
}

return { enabled: true, threshold: 100 };
}

// 使用
const settings = loadSettings();
logger.info(`当前配置: 阈值 = ${settings.threshold}`);

读取 HTML 模板

async function renderPage(templateName, data) {
if (!archive.supportReadFile()) {
return "<html><body><h1>单文件插件不支持模板</h1></body></html>";
}

let html = await archive.readStringAsync(`templates/${templateName}`);
if (!html) return null;

// 简单模板变量替换
for (const key in data) {
html = html.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), data[key]);
}

return html;
}

4. 使用黑白名单 API

自动封禁检测

const spamCounter = {};

bus.on("group_message_event", (event) => {
const senderId = event.SenderId;
const now = Date.now();

// 检查是否已封禁
if (blacklist.InBlackList(senderId)) {
event.IsCanceled = true;
return;
}

// 频率检测
if (!spamCounter[senderId]) {
spamCounter[senderId] = { count: 1, firstTime: now };
} else {
const record = spamCounter[senderId];
record.count++;

// 5秒内超过10条消息视为刷屏
if (now - record.firstTime < 5000 && record.count > 10) {
blacklist.AddBlackList(
senderId,
event.SelfId,
"反刷屏系统",
event.PeerId,
"刷屏(5秒内发送超过10条消息)"
);
event.Context.Reply(new MessageChain().Text("您因刷屏已被自动封禁"));
event.IsCanceled = true;
return;
}

// 重置计数器
if (now - record.firstTime > 5000) {
spamCounter[senderId] = { count: 1, firstTime: now };
}
}
});

白名单验证登录

bus.on("player_login", (server, playerName, playerUuid) => {
const serverToken = server.Session.Info.CachedServerToken;

// 检查服务器是否开启白名单
if (!whitelist.IsServerWhiteListEnabled(serverToken)) {
return; // 未开启,放行
}

// 检查玩家绑定的账号是否在白名单中
const player = db.GetPlayerByName(playerName);
if (!player?.SocialAccount) {
return { kicked: true, reason: "请先绑定账号后再进入服务器" };
}

if (!whitelist.InWhiteList(player.SocialAccount.Uuid)) {
return { kicked: true, reason: "您不在服务器白名单中,请联系管理员" };
}
});

5. 配置事件监听

监听配置变更,实现动态响应:

// 监听所有配置变更
bus.on("config_changed", (data) => {
logger.info(`[配置变更] 分组: ${data.groupId}, 类型: ${data.changeType}`);

if (data.changeType === "updated" && data.groupId === "my_plugin_settings") {
// 重新加载配置
const newConfig = config.getConfigObject("my_plugin_settings");
applyNewSettings(newConfig);
}
});

// 监听特定分组配置变更
bus.on("config_changed:my_plugin_settings", (data) => {
logger.info("插件配置已更新,重新加载...");
reloadPluginConfig();
});

// 配置启用/禁用事件
bus.on("config_enabled", (groupId, configId, configName, configData) => {
logger.info(`配置已启用: ${groupId}/${configName}`);
});

bus.on("config_disabled", (groupId, configId, configName) => {
logger.info(`配置已禁用: ${groupId}/${configName}`);
});

6. 完整示例:天气查询插件

结合多个 API,实现一个带 Web 管理页面的天气查询插件:

/// <reference path="easybot-sdk/easybot.d.ts" />

// ========== 插件生命周期 ==========
bus.on("enable", () => {
logger.info("天气查询插件已启用");
});

bus.on("disable", () => {
logger.info("天气查询插件已禁用");
});

// ========== 注册 Web 管理页面 ==========
bus.on("menu_ready", (pluginMenu) => {
pluginMenu.register(
"weather_settings",
"/pages/weather",
"天气设置",
"CloudServerOutlined",
(action) => {
if (action.method === "getSettings") {
const apiKey = config.getString("weather_plugin", "apiKey", "");
const city = config.getString("weather_plugin", "defaultCity", "北京");
return { apiKey, city };
}
},
(sessionId) => {
let html = archive.supportReadFile()
? archive.readString("templates/weather_settings.html")
: null;

if (!html) {
html = `<html><body><h1>天气设置</h1><p>Session: ${sessionId}</p></body></html>`;
}

return html.replace("{{sessionId}}", sessionId);
},
null, true, 10, false, "天气查询相关设置"
);
});

// ========== 群消息处理 ==========
bus.on("group_message_event", async (event) => {
const message = event.RawMessage;

// 检查黑名单
if (blacklist.InBlackList(event.SenderId)) {
event.IsCanceled = true;
return;
}

// /天气 <城市名>
if (message.startsWith("/天气 ")) {
const city = message.substring(4).trim();
const weather = await queryWeather(city);

if (weather) {
event.Context.Reply(new MessageChain().Text(
`🌤 ${weather.city}天气:${weather.text}\n` +
`🌡 温度:${weather.temp}°C\n` +
`💧 湿度:${weather.humidity}%\n` +
`🌬 风力:${weather.windScale}`
));
} else {
event.Context.Reply(new MessageChain().Text("天气查询失败,请检查城市名称"));
}
}
});

// ========== 天气查询函数 ==========
async function queryWeather(city) {
const apiKey = config.getString("weather_plugin", "apiKey", "");
const defaultCity = config.getString("weather_plugin", "defaultCity", "北京");

const targetCity = city || defaultCity;

try {
const response = await network.get(
`https://api.weather.example.com/v1/now?city=${encodeURIComponent(targetCity)}&key=${apiKey}`
);

if (response.status === 200 && response.data) {
return {
city: targetCity,
text: response.data.now.text,
temp: response.data.now.temp,
humidity: response.data.now.humidity,
windScale: response.data.now.windScale
};
}
} catch (err) {
logger.error(`天气查询失败: ${err}`);
}

return null;
}

下一步

恭喜你完成了进阶教程!现在你可以:

遇到麻烦了?

有偿服务

我们提供有偿代安装服务,解决您的环境配置烦恼。

了解详情
Miku

少年,买服务器吗?

持证经营

专注高性价比游戏云VPS,铂金 / I7 / R9 / 物理机

💰 最低六元起、买不了吃亏买不了上当,快来看看吧~

快来看看