koishi-plugin-adapter-onebot-multi 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/index.js +151 -37
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -1628,6 +1628,32 @@ var StatusPanel = class {
1628
1628
  this.ctx.logger.info(`重启 Bot 配置: #${id} ${config.name}`);
1629
1629
  ctx.body = { success: true };
1630
1630
  });
1631
+ router.post(`${adminPath}/api/bots/update`, authMiddleware, async (ctx) => {
1632
+ const data = await this.parseJsonBody(ctx);
1633
+ const id = Number(data.id);
1634
+ const existing = await this.configManager.getConfigById(id);
1635
+ if (!existing) {
1636
+ ctx.status = 404;
1637
+ ctx.body = { error: `配置 #${id} 不存在` };
1638
+ return;
1639
+ }
1640
+ const updates = {};
1641
+ if (data.name !== void 0) updates.name = data.name;
1642
+ if (data.protocol !== void 0) updates.protocol = data.protocol;
1643
+ if (data.endpoint !== void 0) updates.endpoint = data.endpoint;
1644
+ if (data.path !== void 0) updates.path = data.path;
1645
+ if (data.token !== void 0) updates.token = data.token;
1646
+ await this.configManager.updateConfigById(id, updates);
1647
+ this.ctx.logger.info(`更新 Bot 配置: #${id} ${existing.name}`);
1648
+ if (existing.enabled && existing.selfId) {
1649
+ await this.stopBot(existing.selfId);
1650
+ const newConfig = await this.configManager.getConfigById(id);
1651
+ if (newConfig) {
1652
+ await this.startBot(this.configManager.toBotConfig(newConfig));
1653
+ }
1654
+ }
1655
+ ctx.body = { success: true };
1656
+ });
1631
1657
  router.post(`${adminPath}/api/bots/profile`, authMiddleware, async (ctx) => {
1632
1658
  const data = await this.parseJsonBody(ctx);
1633
1659
  const selfId = String(data.selfId);
@@ -1640,6 +1666,10 @@ var StatusPanel = class {
1640
1666
  }
1641
1667
  try {
1642
1668
  await bot.internal.setQqProfile(nickname, "", "", "", "");
1669
+ const loginInfo = await bot.internal.getLoginInfo();
1670
+ if (loginInfo?.nickname) {
1671
+ bot.user.name = loginInfo.nickname;
1672
+ }
1643
1673
  this.ctx.logger.info(`修改 Bot ${selfId} 昵称为: ${nickname}`);
1644
1674
  ctx.body = { success: true };
1645
1675
  } catch (error) {
@@ -1975,30 +2005,67 @@ var StatusPanel = class {
1975
2005
  </div>
1976
2006
  </div>
1977
2007
 
1978
- <!-- 编辑 Bot 模态框 -->
1979
- <div class="modal" id="editModal">
2008
+ <!-- 编辑 Bot 配置模态框 -->
2009
+ <div class="modal" id="configModal">
1980
2010
  <div class="modal-content">
1981
- <h2>编辑 Bot 资料</h2>
1982
- <form id="editForm">
1983
- <input type="hidden" id="editSelfId">
2011
+ <h2>编辑 Bot 配置</h2>
2012
+ <form id="configForm">
2013
+ <input type="hidden" id="configId">
2014
+ <div class="form-group">
2015
+ <label for="configEditName">配置名称 *</label>
2016
+ <input type="text" id="configEditName" required placeholder="例如: 主号、小号">
2017
+ </div>
1984
2018
  <div class="form-group">
1985
- <label for="editNickname">昵称</label>
1986
- <input type="text" id="editNickname" placeholder="新的QQ昵称">
2019
+ <label for="configProtocol">协议</label>
2020
+ <select id="configProtocol" onchange="toggleConfigEndpoint()">
2021
+ <option value="ws-reverse">反向 WebSocket (ws-reverse)</option>
2022
+ <option value="ws">正向 WebSocket (ws)</option>
2023
+ </select>
2024
+ </div>
2025
+ <div class="form-group" id="configEndpointGroup" style="display: none;">
2026
+ <label for="configEndpoint">Endpoint *</label>
2027
+ <input type="text" id="configEndpoint" placeholder="例如: ws://127.0.0.1:6700">
2028
+ </div>
2029
+ <div class="form-group" id="configPathGroup">
2030
+ <label for="configPath">路径</label>
2031
+ <input type="text" id="configPath" placeholder="例如: /onebot">
1987
2032
  </div>
1988
2033
  <div class="form-group">
1989
- <label for="editAvatar">头像</label>
1990
- <input type="file" id="editAvatar" accept="image/*" style="display:none">
2034
+ <label for="configToken">Token(可选)</label>
2035
+ <input type="password" id="configToken" placeholder="访问令牌(留空不修改)">
2036
+ </div>
2037
+ <div class="modal-actions">
2038
+ <button type="submit" class="btn">保存</button>
2039
+ <button type="button" class="btn btn-secondary" onclick="hideConfigModal()">取消</button>
2040
+ </div>
2041
+ </form>
2042
+ </div>
2043
+ </div>
2044
+
2045
+ <!-- 修改 Bot 资料模态框 -->
2046
+ <div class="modal" id="profileModal">
2047
+ <div class="modal-content">
2048
+ <h2>修改 Bot 资料</h2>
2049
+ <form id="profileForm">
2050
+ <input type="hidden" id="profileSelfId">
2051
+ <div class="form-group">
2052
+ <label for="profileNickname">昵称</label>
2053
+ <input type="text" id="profileNickname" placeholder="新的QQ昵称">
2054
+ </div>
2055
+ <div class="form-group">
2056
+ <label for="profileAvatar">头像</label>
2057
+ <input type="file" id="profileAvatar" accept="image/*" style="display:none">
1991
2058
  <div style="display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;">
1992
2059
  <img id="avatarPreview" style="width:64px;height:64px;border:var(--nl-border);border-radius:50%;object-fit:cover;">
1993
- <button type="button" class="btn btn-secondary" onclick="document.getElementById('editAvatar').click()" style="padding:0.5rem 1rem;">选择图片</button>
2060
+ <button type="button" class="btn btn-secondary" onclick="document.getElementById('profileAvatar').click()" style="padding:0.5rem 1rem;">选择图片</button>
1994
2061
  <span id="avatarFileName" style="font-size:0.85rem;color:#92400e;"></span>
1995
2062
  </div>
1996
2063
  <p style="font-size:0.8rem;color:#92400e;margin-top:0.5rem;">支持本地图片或输入图片URL</p>
1997
- <input type="text" id="editAvatarUrl" placeholder="或输入图片URL" style="margin-top:0.5rem;">
2064
+ <input type="text" id="profileAvatarUrl" placeholder="或输入图片URL" style="margin-top:0.5rem;">
1998
2065
  </div>
1999
2066
  <div class="modal-actions">
2000
2067
  <button type="submit" class="btn">保存</button>
2001
- <button type="button" class="btn btn-secondary" onclick="hideEditModal()">取消</button>
2068
+ <button type="button" class="btn btn-secondary" onclick="hideProfileModal()">取消</button>
2002
2069
  </div>
2003
2070
  </form>
2004
2071
  </div>
@@ -2190,7 +2257,7 @@ var StatusPanel = class {
2190
2257
  const selfIdDisplay = bot.selfId ? ('QQ: ' + bot.selfId) : '等待连接...'
2191
2258
  const uptime = bot.startupTime ? formatUptime(bot.startupTime) : '-'
2192
2259
  const lastMsg = bot.lastMessageTime ? new Date(bot.lastMessageTime * 1000).toLocaleTimeString() : '-'
2193
- const avatarUrl = bot.selfId ? ('http://q.qlogo.cn/headimg_dl?dst_uin=' + bot.selfId + '&spec=640') : ''
2260
+ const avatarUrl = bot.selfId ? ('http://q.qlogo.cn/headimg_dl?dst_uin=' + bot.selfId + '&spec=640&t=' + Date.now()) : ''
2194
2261
 
2195
2262
  return '<div class="bot-card' + (!bot.enabled ? ' disabled' : '') + '">' +
2196
2263
  '<div class="bot-header">' +
@@ -2212,8 +2279,9 @@ var StatusPanel = class {
2212
2279
  '<div class="bot-actions">' +
2213
2280
  '<button class="btn btn-secondary" onclick="toggleBot(' + bot.id + ')">' + (bot.enabled ? '禁用' : '启用') + '</button>' +
2214
2281
  '<button class="btn btn-secondary" onclick="restartBot(' + bot.id + ')">重启</button>' +
2215
- (bot.status === 'online' ? '<button class="btn btn-secondary" onclick="showEditModal(\\'' + bot.selfId + '\\', \\'' + (bot.nickname || '').replace(/'/g, "\\\\'") + '\\')">编辑</button>' : '') +
2216
- '<button class="btn btn-danger" onclick="deleteBot(' + bot.id + ', \\'' + bot.name + '\\')">删除</button>' +
2282
+ '<button class="btn btn-secondary" onclick="showConfigModal(' + bot.id + ', \\'' + bot.name.replace(/'/g, "\\\\'") + '\\', \\'' + (bot.protocol || 'ws-reverse') + '\\', \\'' + (bot.endpoint || '').replace(/'/g, "\\\\'") + '\\', \\'' + (bot.path || '/onebot').replace(/'/g, "\\\\'") + '\\')">配置</button>' +
2283
+ (bot.status === 'online' ? '<button class="btn btn-secondary" onclick="showProfileModal(\\'' + bot.selfId + '\\', \\'' + (bot.nickname || '').replace(/'/g, "\\\\'") + '\\')">资料</button>' : '') +
2284
+ '<button class="btn btn-danger" onclick="deleteBot(' + bot.id + ', \\'' + bot.name.replace(/'/g, "\\\\'") + '\\')">删除</button>' +
2217
2285
  '</div>' +
2218
2286
  '</div>'
2219
2287
  }).join('')
@@ -2296,29 +2364,75 @@ var StatusPanel = class {
2296
2364
  }
2297
2365
  })
2298
2366
 
2299
- // 编辑 Bot 模态框
2300
- let editAvatarFile = null
2367
+ // 编辑 Bot 配置模态框
2368
+ function showConfigModal(id, name, protocol, endpoint, path) {
2369
+ document.getElementById('configId').value = id
2370
+ document.getElementById('configEditName').value = name || ''
2371
+ document.getElementById('configProtocol').value = protocol || 'ws-reverse'
2372
+ document.getElementById('configEndpoint').value = endpoint || ''
2373
+ document.getElementById('configPath').value = path || '/onebot'
2374
+ document.getElementById('configToken').value = ''
2375
+ toggleConfigEndpoint()
2376
+ document.getElementById('configModal').classList.add('active')
2377
+ }
2378
+
2379
+ function hideConfigModal() {
2380
+ document.getElementById('configModal').classList.remove('active')
2381
+ }
2382
+
2383
+ function toggleConfigEndpoint() {
2384
+ const protocol = document.getElementById('configProtocol').value
2385
+ document.getElementById('configEndpointGroup').style.display = protocol === 'ws' ? 'block' : 'none'
2386
+ document.getElementById('configPathGroup').style.display = protocol === 'ws-reverse' ? 'block' : 'none'
2387
+ }
2388
+
2389
+ document.getElementById('configForm').addEventListener('submit', async (e) => {
2390
+ e.preventDefault()
2391
+ const id = document.getElementById('configId').value
2392
+ const data = {
2393
+ id: Number(id),
2394
+ name: document.getElementById('configEditName').value,
2395
+ protocol: document.getElementById('configProtocol').value,
2396
+ endpoint: document.getElementById('configEndpoint').value || undefined,
2397
+ path: document.getElementById('configPath').value || undefined,
2398
+ }
2399
+ // Token 留空不修改
2400
+ const token = document.getElementById('configToken').value
2401
+ if (token) data.token = token
2402
+
2403
+ try {
2404
+ await api('/bots/update', data)
2405
+ showToast('配置更新成功')
2406
+ hideConfigModal()
2407
+ refreshBots()
2408
+ } catch (e) {
2409
+ showToast('更新失败: ' + e.message, 'error')
2410
+ }
2411
+ })
2412
+
2413
+ // 修改 Bot 资料模态框
2414
+ let profileAvatarFile = null
2301
2415
 
2302
- function showEditModal(selfId, nickname) {
2303
- document.getElementById('editSelfId').value = selfId
2304
- document.getElementById('editNickname').value = nickname || ''
2416
+ function showProfileModal(selfId, nickname) {
2417
+ document.getElementById('profileSelfId').value = selfId
2418
+ document.getElementById('profileNickname').value = nickname || ''
2305
2419
  document.getElementById('avatarPreview').src = 'http://q.qlogo.cn/headimg_dl?dst_uin=' + selfId + '&spec=640'
2306
2420
  document.getElementById('avatarFileName').textContent = ''
2307
- document.getElementById('editAvatarUrl').value = ''
2308
- editAvatarFile = null
2309
- document.getElementById('editModal').classList.add('active')
2421
+ document.getElementById('profileAvatarUrl').value = ''
2422
+ profileAvatarFile = null
2423
+ document.getElementById('profileModal').classList.add('active')
2310
2424
  }
2311
2425
 
2312
- function hideEditModal() {
2313
- document.getElementById('editModal').classList.remove('active')
2314
- editAvatarFile = null
2426
+ function hideProfileModal() {
2427
+ document.getElementById('profileModal').classList.remove('active')
2428
+ profileAvatarFile = null
2315
2429
  }
2316
2430
 
2317
2431
  // 头像文件选择
2318
- document.getElementById('editAvatar').addEventListener('change', function(e) {
2432
+ document.getElementById('profileAvatar').addEventListener('change', function(e) {
2319
2433
  const file = e.target.files[0]
2320
2434
  if (file) {
2321
- editAvatarFile = file
2435
+ profileAvatarFile = file
2322
2436
  document.getElementById('avatarFileName').textContent = file.name
2323
2437
  // 预览
2324
2438
  const reader = new FileReader()
@@ -2329,12 +2443,12 @@ var StatusPanel = class {
2329
2443
  }
2330
2444
  })
2331
2445
 
2332
- // 编辑表单提交
2333
- document.getElementById('editForm').addEventListener('submit', async (e) => {
2446
+ // 资料表单提交
2447
+ document.getElementById('profileForm').addEventListener('submit', async (e) => {
2334
2448
  e.preventDefault()
2335
- const selfId = document.getElementById('editSelfId').value
2336
- const nickname = document.getElementById('editNickname').value
2337
- const avatarUrl = document.getElementById('editAvatarUrl').value
2449
+ const selfId = document.getElementById('profileSelfId').value
2450
+ const nickname = document.getElementById('profileNickname').value
2451
+ const avatarUrl = document.getElementById('profileAvatarUrl').value
2338
2452
 
2339
2453
  let hasChange = false
2340
2454
 
@@ -2350,7 +2464,7 @@ var StatusPanel = class {
2350
2464
  }
2351
2465
 
2352
2466
  // 修改头像
2353
- if (editAvatarFile) {
2467
+ if (profileAvatarFile) {
2354
2468
  // 本地文件转 base64
2355
2469
  const reader = new FileReader()
2356
2470
  reader.onload = async function(ev) {
@@ -2362,7 +2476,7 @@ var StatusPanel = class {
2362
2476
  showToast('头像修改失败: ' + e.message, 'error')
2363
2477
  }
2364
2478
  }
2365
- reader.readAsDataURL(editAvatarFile)
2479
+ reader.readAsDataURL(profileAvatarFile)
2366
2480
  hasChange = true
2367
2481
  } else if (avatarUrl) {
2368
2482
  // URL 方式
@@ -2376,7 +2490,7 @@ var StatusPanel = class {
2376
2490
  }
2377
2491
 
2378
2492
  if (hasChange) {
2379
- hideEditModal()
2493
+ hideProfileModal()
2380
2494
  setTimeout(refreshBots, 1000)
2381
2495
  } else {
2382
2496
  showToast('未做任何修改', 'error')
@@ -2414,7 +2528,7 @@ var StatusPanel = class {
2414
2528
  return `
2415
2529
  <div class="bot-card">
2416
2530
  <div class="bot-header">
2417
- <img class="bot-avatar" src="http://q.qlogo.cn/headimg_dl?dst_uin=${bot.selfId}&spec=640" alt="avatar">
2531
+ <img class="bot-avatar" src="http://q.qlogo.cn/headimg_dl?dst_uin=${bot.selfId}&spec=640&t=${Date.now()}" alt="avatar">
2418
2532
  <div class="bot-info">
2419
2533
  <div class="bot-id">${displayName}</div>
2420
2534
  <a class="bot-protocol" href="tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=${bot.selfId}" target="_blank" title="点击添加好友">QQ: ${bot.selfId}</a>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-adapter-onebot-multi",
3
3
  "description": "奶龙bot定制版onebot适配器,支持自动负载均衡,适配器级黑名单/白名单,提供webui,可指定端口",
4
- "version": "0.0.11",
4
+ "version": "0.0.13",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [