From c4e66c243cb477df7c4979da155e4b75c4671c1f Mon Sep 17 00:00:00 2001 From: linyuchen Date: Fri, 24 Apr 2026 23:39:54 +0800 Subject: [PATCH 01/11] ci: use yarn instead of npm in publish workflow --- .github/workflows/publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8f824693e..55b715421 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -37,7 +37,8 @@ jobs: - name: Install dependencies run: | export ELECTRON_SKIP_BINARY_DOWNLOAD=1 - npm install + corepack enable + yarn install --no-immutable cd src/webui/FE npm install From 3242aff12ab7acfcd195116b1e44a147085ced00 Mon Sep 17 00:00:00 2001 From: linyuchen Date: Fri, 24 Apr 2026 23:39:54 +0800 Subject: [PATCH 02/11] ci: use yarn instead of npm in publish workflow --- .github/workflows/publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8f824693e..55b715421 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -37,7 +37,8 @@ jobs: - name: Install dependencies run: | export ELECTRON_SKIP_BINARY_DOWNLOAD=1 - npm install + corepack enable + yarn install --no-immutable cd src/webui/FE npm install From 73a31863f706a85c20cdfcd33f7f2a72509c1cf5 Mon Sep 17 00:00:00 2001 From: linyuchen Date: Wed, 29 Apr 2026 11:13:28 +0800 Subject: [PATCH 03/11] feat(webui): add logout button to sidebar (clears token, does not affect QQ) --- src/webui/FE/App.tsx | 5 +++++ src/webui/FE/components/layout/Sidebar.tsx | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/webui/FE/App.tsx b/src/webui/FE/App.tsx index a11e03d73..e032bd383 100644 --- a/src/webui/FE/App.tsx +++ b/src/webui/FE/App.tsx @@ -16,6 +16,7 @@ import { import { WebQQPage, WebQQFullscreen } from './components/WebQQ'; import { Config, ResConfig, EmailConfig } from './types'; import { apiFetch, setPasswordPromptHandler } from './utils/api'; +import { deleteCookie } from './utils/cookie'; import { Save, Loader2, Eye, EyeOff, Plus, Trash2, Menu, Cpu, Milk, ExternalLink } from 'lucide-react'; import { defaultConfig } from '../../main/config/defaultConfig' import { version } from '../../version' @@ -280,6 +281,10 @@ function App() { collapsed={sidebarCollapsed} onToggleCollapse={() => setSidebarCollapsed(!sidebarCollapsed)} onOpenSettings={() => setShowSettingsDialog(true)} + onLogout={() => { + deleteCookie('webui_token') + window.location.reload() + }} />
diff --git a/src/webui/FE/components/layout/Sidebar.tsx b/src/webui/FE/components/layout/Sidebar.tsx index 13691cad7..86fc76233 100644 --- a/src/webui/FE/components/layout/Sidebar.tsx +++ b/src/webui/FE/components/layout/Sidebar.tsx @@ -12,6 +12,7 @@ import { ChevronLeft, ChevronRight, Settings, + LogOut, } from 'lucide-react' interface SidebarProps { @@ -26,6 +27,7 @@ interface SidebarProps { collapsed?: boolean; onToggleCollapse?: () => void; onOpenSettings?: () => void; + onLogout?: () => void; } const Sidebar: React.FC = ({ @@ -37,6 +39,7 @@ const Sidebar: React.FC = ({ collapsed = false, onToggleCollapse, onOpenSettings, + onLogout, }) => { const menuItems = [ { id: 'dashboard', icon: LayoutDashboard, label: 'Dashboard' }, @@ -165,6 +168,13 @@ const Sidebar: React.FC = ({ > + From 80ff96b9cd18974b28216be71667ff4481d830c4 Mon Sep 17 00:00:00 2001 From: idranme <96647698+idranme@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:06:27 +0800 Subject: [PATCH 04/11] refactor --- src/ntqqapi/api/user.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ntqqapi/api/user.ts b/src/ntqqapi/api/user.ts index 8207ddcf2..58aabdf22 100644 --- a/src/ntqqapi/api/user.ts +++ b/src/ntqqapi/api/user.ts @@ -73,7 +73,7 @@ export class NTQQUserApi extends Service { return (await this.ctx.pmhq.invoke('nodeIKernelUixConvertService/getUin', [[uid]])).uinInfo.get(uid) }, async () => { - return (await this.fetchUserDetailInfo(uid)).detail.get(uid)?.uin + return (await this.getUserSimpleInfo(uid)).uin }, ] @@ -91,7 +91,7 @@ export class NTQQUserApi extends Service { return '' } - // 这个会从服务器拉取,比较可靠 + /** 始终会从服务器拉取 */ async fetchUserDetailInfo(uid: string) { return await this.ctx.pmhq.invoke( 'nodeIKernelProfileService/fetchUserDetailInfo', @@ -119,6 +119,7 @@ export class NTQQUserApi extends Service { return result } + /** 无缓存时会从服务器拉取 */ async getUserSimpleInfo(uid: string, force = true) { const data = await this.ctx.pmhq.invoke>( 'nodeIKernelProfileService/getUserSimpleInfo', @@ -134,6 +135,7 @@ export class NTQQUserApi extends Service { return data.get(uid)! } + /** 无缓存时会获取不到用户信息 */ async getCoreAndBaseInfo(uids: string[]) { return await this.ctx.pmhq.invoke( 'nodeIKernelProfileService/getCoreAndBaseInfo', From d8eeb5fddb741c12895c3792fd0baaa847723b4e Mon Sep 17 00:00:00 2001 From: linyuchen Date: Wed, 29 Apr 2026 15:22:18 +0800 Subject: [PATCH 05/11] feat(webui): implement logout functionality in SettingsDialog with confirmation prompt --- src/webui/FE/App.tsx | 8 ++-- .../FE/components/common/SettingsDialog.tsx | 42 +++++++++++++++++-- src/webui/FE/components/layout/Sidebar.tsx | 10 ----- src/webui/FE/vite.config.ts | 3 +- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/webui/FE/App.tsx b/src/webui/FE/App.tsx index e032bd383..4e1dfff2d 100644 --- a/src/webui/FE/App.tsx +++ b/src/webui/FE/App.tsx @@ -281,10 +281,6 @@ function App() { collapsed={sidebarCollapsed} onToggleCollapse={() => setSidebarCollapsed(!sidebarCollapsed)} onOpenSettings={() => setShowSettingsDialog(true)} - onLogout={() => { - deleteCookie('webui_token') - window.location.reload() - }} />
@@ -941,6 +937,10 @@ function App() { setShowSettingsDialog(false)} + onLogout={() => { + deleteCookie('webui_token') + window.location.reload() + }} /> ); diff --git a/src/webui/FE/components/common/SettingsDialog.tsx b/src/webui/FE/components/common/SettingsDialog.tsx index bf5b4aba1..e92aac68d 100644 --- a/src/webui/FE/components/common/SettingsDialog.tsx +++ b/src/webui/FE/components/common/SettingsDialog.tsx @@ -1,16 +1,18 @@ import React from 'react' -import { X, Sun, Moon, Monitor, Eye, EyeOff } from 'lucide-react' +import { X, Sun, Moon, Monitor, Eye, EyeOff, LogOut } from 'lucide-react' import { useThemeStore } from '../../stores/themeStore' import { useSettingsStore } from '../../stores/settingsStore' interface SettingsDialogProps { visible: boolean onClose: () => void + onLogout?: () => void } -const SettingsDialog: React.FC = ({ visible, onClose }) => { +const SettingsDialog: React.FC = ({ visible, onClose, onLogout }) => { const { mode, setMode } = useThemeStore() const { autoHideSidebarInWebQQ, setAutoHideSidebarInWebQQ, showWebQQFullscreenButton, setShowWebQQFullscreenButton } = useSettingsStore() + const [showLogoutConfirm, setShowLogoutConfirm] = React.useState(false) if (!visible) return null @@ -111,7 +113,14 @@ const SettingsDialog: React.FC = ({ visible, onClose }) => {/* Footer */} -
+
+
+ + {/* Logout Confirm Dialog */} + {showLogoutConfirm && ( +
+
setShowLogoutConfirm(false)} /> +
+

确认退出

+

+ 确定要退出 WebUI 吗?这不会退出 QQ,仅清除 WebUI 登录状态。 +

+
+ + +
+
+
+ )}
) } diff --git a/src/webui/FE/components/layout/Sidebar.tsx b/src/webui/FE/components/layout/Sidebar.tsx index 86fc76233..13691cad7 100644 --- a/src/webui/FE/components/layout/Sidebar.tsx +++ b/src/webui/FE/components/layout/Sidebar.tsx @@ -12,7 +12,6 @@ import { ChevronLeft, ChevronRight, Settings, - LogOut, } from 'lucide-react' interface SidebarProps { @@ -27,7 +26,6 @@ interface SidebarProps { collapsed?: boolean; onToggleCollapse?: () => void; onOpenSettings?: () => void; - onLogout?: () => void; } const Sidebar: React.FC = ({ @@ -39,7 +37,6 @@ const Sidebar: React.FC = ({ collapsed = false, onToggleCollapse, onOpenSettings, - onLogout, }) => { const menuItems = [ { id: 'dashboard', icon: LayoutDashboard, label: 'Dashboard' }, @@ -168,13 +165,6 @@ const Sidebar: React.FC = ({ > - diff --git a/src/webui/FE/vite.config.ts b/src/webui/FE/vite.config.ts index 37a939a9c..3d86f6744 100644 --- a/src/webui/FE/vite.config.ts +++ b/src/webui/FE/vite.config.ts @@ -15,7 +15,8 @@ export default defineConfig({ }, }, server: { - port: 5173, + host: '127.0.0.1', + port: 15173, open: true, proxy: { '/api': { From d2cd3982fefd7816181d5ef6c28b208afc597a7a Mon Sep 17 00:00:00 2001 From: idranme <96647698+idranme@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:51:04 +0800 Subject: [PATCH 06/11] fix --- src/ntqqapi/api/msg.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ntqqapi/api/msg.ts b/src/ntqqapi/api/msg.ts index 9e84df4a8..24dd8ded9 100644 --- a/src/ntqqapi/api/msg.ts +++ b/src/ntqqapi/api/msg.ts @@ -290,7 +290,7 @@ export class NTQQMsgApi extends Service { chatInfo: peer, filterMsgType: [], filterSendersUid, - filterMsgToTime: filterMsgTime, + filterMsgToTime: String(filterMsgTime + 1), // 获取到的消息时间可能比 replyMsgTime 多一毫秒 filterMsgFromTime: filterMsgTime, isReverseOrder: true, isIncludeCurrent: true, From 10bb429f59885e1726844c3088f7efe5c73e417a Mon Sep 17 00:00:00 2001 From: idranme <96647698+idranme@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:59:01 +0800 Subject: [PATCH 07/11] fix --- src/main/pmhq/mixins/user.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/pmhq/mixins/user.ts b/src/main/pmhq/mixins/user.ts index 7ab39dae6..53e0a0c06 100644 --- a/src/main/pmhq/mixins/user.ts +++ b/src/main/pmhq/mixins/user.ts @@ -36,6 +36,7 @@ export function UserMixin PMHQBase>(Base: T) { const numbers = Object.fromEntries(info.body.properties.numberProperties.map(p => [p.key, p.value])) const bytes = Object.fromEntries(info.body.properties.bytesProperties.map(p => [p.key, p.value])) const business = bytes[107] ? Misc.UserInfoBusiness.decode(bytes[107]) : undefined + const vipInfo = business?.body.lists.find((e) => e.type === 1) return { uin: info.body.uin, nick: bytes[20002]?.toString() ?? '', @@ -53,9 +54,9 @@ export function UserMixin PMHQBase>(Base: T) { labels: bytes[104] ? Misc.UserInfoLabel.decode(bytes[104]).labels.map(e => e.content) : [], school: bytes[20021]?.toString() ?? '', remark: bytes[103]?.toString() ?? '', - isVip: !!business?.body.lists[0], - isYearsVip: !!business?.body.lists[0]?.isYear, - vipLevel: business?.body.lists[0]?.level ?? 0 + isVip: !!vipInfo, + isYearsVip: !!vipInfo?.isYear, + vipLevel: vipInfo?.level ?? 0 } } From 95913482578fde9a81b775861bf83184256f7f8f Mon Sep 17 00:00:00 2001 From: idranme <96647698+idranme@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:03:36 +0800 Subject: [PATCH 08/11] fix --- src/milky/transform/message/outgoing.ts | 3 ++- src/ntqqapi/entities.ts | 1 - src/onebot11/helper/createMessage.ts | 3 ++- src/satori/message.ts | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/milky/transform/message/outgoing.ts b/src/milky/transform/message/outgoing.ts index fc6a8152e..8d97df9c2 100644 --- a/src/milky/transform/message/outgoing.ts +++ b/src/milky/transform/message/outgoing.ts @@ -30,7 +30,8 @@ export async function transformOutgoingMessage( } else if (segment.type === 'mention' && isGroup) { const memberUin = segment.data.user_id.toString() const memberUid = await ctx.ntUserApi.getUidByUin(memberUin, peerUid) - elements.push(SendElement.at(memberUin, memberUid, AtType.One, '')) + const info = await ctx.ntGroupApi.getGroupMember(peerUid, memberUid) + elements.push(SendElement.at(memberUin, memberUid, AtType.One, `@${info.cardName || info.nick}`)) } else if (segment.type === 'mention_all' && isGroup) { elements.push(SendElement.at('', '', AtType.All, '@全体成员')) } else if (segment.type === 'face') { diff --git a/src/ntqqapi/entities.ts b/src/ntqqapi/entities.ts index f807b5656..6a1240cd5 100644 --- a/src/ntqqapi/entities.ts +++ b/src/ntqqapi/entities.ts @@ -1,4 +1,3 @@ -import ffmpeg from 'fluent-ffmpeg' import faceConfig from './helper/face_config.json' import pathLib from 'node:path' import { diff --git a/src/onebot11/helper/createMessage.ts b/src/onebot11/helper/createMessage.ts index 5ed943617..b2de6058a 100644 --- a/src/onebot11/helper/createMessage.ts +++ b/src/onebot11/helper/createMessage.ts @@ -65,7 +65,8 @@ export async function createSendElements( } else if (peer.chatType === ChatType.Group) { const uid = await ctx.ntUserApi.getUidByUin(atQQ, peer.peerUid) - let display = '' + const info = await ctx.ntGroupApi.getGroupMember(peer.peerUid, uid) + let display = `@${info.cardName || info.nick}` if (segment.data.name) { display = `@${segment.data.name}` } diff --git a/src/satori/message.ts b/src/satori/message.ts index 200abfd18..e0a148dbf 100644 --- a/src/satori/message.ts +++ b/src/satori/message.ts @@ -383,7 +383,8 @@ export class MessageEncoder { this.elements.push(SendElement.at('', '', NT.AtType.All, '@全体成员')) } else { const uid = await this.ctx.ntUserApi.getUidByUin(attrs.id, this.peer.peerUid) - const display = attrs.name ? '@' + attrs.name : '' + const info = await this.ctx.ntGroupApi.getGroupMember(this.peer.peerUid, uid) + const display = attrs.name ? '@' + attrs.name : `@${info.cardName || info.nick}` this.elements.push(SendElement.at(attrs.id, uid, NT.AtType.One, display)) } } else if (type === 'a') { From fe1826ce730c722cb0deaa897bda024a0c42451b Mon Sep 17 00:00:00 2001 From: idranme <96647698+idranme@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:08:02 +0800 Subject: [PATCH 09/11] chore: update version to 7.12.10 --- .../\346\233\264\346\226\260\346\227\245\345\277\227.txt" | 8 ++++++++ package-dist.json | 2 +- src/version.ts | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git "a/doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" "b/doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" index 662f1a8b8..6a1ef4dfd 100644 --- "a/doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" +++ "b/doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" @@ -1,3 +1,11 @@ +V7.12.10 +更新时间 2026-04-30 + +* 修复偶现获取不到引用的消息 +* 修复发送的 at 消息可能显示为空白 +* 修复 get_stranger_info API 获取的会员相关信息不正确 + +================= V7.12.9 更新时间 2026-04-29 diff --git a/package-dist.json b/package-dist.json index d639eeafd..73dab60a5 100644 --- a/package-dist.json +++ b/package-dist.json @@ -1 +1 @@ -{"name":"llonebot-dist","version":"7.12.9","type":"module","description":"","main":"llbot.js","author":"linyuchen","repository":{"type":"git","url":"https://github.com/LLOneBot/LuckyLilliaBot"}} \ No newline at end of file +{"name":"llonebot-dist","version":"7.12.10","type":"module","description":"","main":"llbot.js","author":"linyuchen","repository":{"type":"git","url":"https://github.com/LLOneBot/LuckyLilliaBot"}} \ No newline at end of file diff --git a/src/version.ts b/src/version.ts index 8645968f8..1ee4a8853 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const version = '7.12.9' +export const version = '7.12.10' From aeef97320b477f0694bb6bf628d4d19481db3261 Mon Sep 17 00:00:00 2001 From: idranme <96647698+idranme@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:20:35 +0800 Subject: [PATCH 10/11] refactor --- src/onebot11/helper/createMessage.ts | 6 ++++-- src/satori/message.ts | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/onebot11/helper/createMessage.ts b/src/onebot11/helper/createMessage.ts index b2de6058a..d17366ba0 100644 --- a/src/onebot11/helper/createMessage.ts +++ b/src/onebot11/helper/createMessage.ts @@ -65,10 +65,12 @@ export async function createSendElements( } else if (peer.chatType === ChatType.Group) { const uid = await ctx.ntUserApi.getUidByUin(atQQ, peer.peerUid) - const info = await ctx.ntGroupApi.getGroupMember(peer.peerUid, uid) - let display = `@${info.cardName || info.nick}` + let display if (segment.data.name) { display = `@${segment.data.name}` + } else { + const info = await ctx.ntGroupApi.getGroupMember(peer.peerUid, uid) + display = `@${info.cardName || info.nick}` } sendElements.push(SendElement.at(atQQ, uid, AtType.One, display)) } diff --git a/src/satori/message.ts b/src/satori/message.ts index e0a148dbf..30f9d3149 100644 --- a/src/satori/message.ts +++ b/src/satori/message.ts @@ -383,8 +383,13 @@ export class MessageEncoder { this.elements.push(SendElement.at('', '', NT.AtType.All, '@全体成员')) } else { const uid = await this.ctx.ntUserApi.getUidByUin(attrs.id, this.peer.peerUid) - const info = await this.ctx.ntGroupApi.getGroupMember(this.peer.peerUid, uid) - const display = attrs.name ? '@' + attrs.name : `@${info.cardName || info.nick}` + let display + if (attrs.name) { + display = `@${attrs.name}` + } else { + const info = await this.ctx.ntGroupApi.getGroupMember(this.peer.peerUid, uid) + display = `@${info.cardName || info.nick}` + } this.elements.push(SendElement.at(attrs.id, uid, NT.AtType.One, display)) } } else if (type === 'a') { From 34dee171da21188798fc51c40d73860556949bd0 Mon Sep 17 00:00:00 2001 From: idranme <96647698+idranme@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:31:11 +0800 Subject: [PATCH 11/11] docs(changelog): update release notes --- "doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" | 1 + 1 file changed, 1 insertion(+) diff --git "a/doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" "b/doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" index 6a1ef4dfd..75f862dc7 100644 --- "a/doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" +++ "b/doc/\346\233\264\346\226\260\346\227\245\345\277\227.txt" @@ -4,6 +4,7 @@ V7.12.10 * 修复偶现获取不到引用的消息 * 修复发送的 at 消息可能显示为空白 * 修复 get_stranger_info API 获取的会员相关信息不正确 +* 支持退出 WebUI 登录 ================= V7.12.9