mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-01-15 12:50:33 +00:00
Merge branch 'main' into webui-new
This commit is contained in:
commit
7fba9960bf
BIN
external/LiteLoaderWrapper.zip
vendored
BIN
external/LiteLoaderWrapper.zip
vendored
Binary file not shown.
@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "4.1.18",
|
"version": "4.1.21",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -74,6 +74,26 @@ export class QQLoginManager {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public async checkQQLoginStatusWithQrcode(): Promise<{ qrcodeurl: string, isLogin: string } | undefined> {
|
||||||
|
try {
|
||||||
|
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/CheckLoginStatus`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + this.retCredential,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (QQLoginResponse.status == 200) {
|
||||||
|
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||||
|
if (QQLoginResponseJson.code == 0) {
|
||||||
|
return QQLoginResponseJson.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking QQ login status:', error);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
public async checkWebUiLogined(): Promise<boolean> {
|
public async checkWebUiLogined(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -54,7 +54,7 @@ const selectedAccount = ref<string>('');
|
|||||||
const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
|
const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
|
||||||
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || '');
|
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || '');
|
||||||
let heartBeatTimer: number | null = null;
|
let heartBeatTimer: number | null = null;
|
||||||
|
let qrcodeUrl: string = '';
|
||||||
const selectAccount = async (accountName: string): Promise<void> => {
|
const selectAccount = async (accountName: string): Promise<void> => {
|
||||||
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
|
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
|
||||||
if (result) {
|
if (result) {
|
||||||
@ -80,19 +80,26 @@ const generateQrCode = (data: string, canvas: HTMLCanvasElement | null): void =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
const HeartBeat = async (): Promise<void> => {
|
const HeartBeat = async (): Promise<void> => {
|
||||||
const isLogined = await qqLoginManager.checkQQLoginStatus();
|
const isLogined = await qqLoginManager.checkQQLoginStatusWithQrcode();
|
||||||
if (isLogined) {
|
if (isLogined?.isLogin) {
|
||||||
if (heartBeatTimer) {
|
if (heartBeatTimer) {
|
||||||
clearInterval(heartBeatTimer);
|
clearInterval(heartBeatTimer);
|
||||||
}
|
}
|
||||||
|
//判断是否已经调转
|
||||||
|
if (router.currentRoute.value.path !== '/dashboard/basic-info') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await router.push({ path: '/dashboard/basic-info' });
|
await router.push({ path: '/dashboard/basic-info' });
|
||||||
|
} else if (isLogined?.qrcodeurl && qrcodeUrl !== isLogined.qrcodeurl) {
|
||||||
|
qrcodeUrl = isLogined.qrcodeurl;
|
||||||
|
generateQrCode(qrcodeUrl, qrcodeCanvas.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const InitPages = async (): Promise<void> => {
|
const InitPages = async (): Promise<void> => {
|
||||||
quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
|
quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
|
||||||
const qrcodeData = await qqLoginManager.getQQLoginQrcode();
|
qrcodeUrl = await qqLoginManager.getQQLoginQrcode();
|
||||||
generateQrCode(qrcodeData, qrcodeCanvas.value);
|
generateQrCode(qrcodeUrl, qrcodeCanvas.value);
|
||||||
heartBeatTimer = window.setInterval(HeartBeat, 3000);
|
heartBeatTimer = window.setInterval(HeartBeat, 3000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.1.18",
|
"version": "4.1.21",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
||||||
"build:shell": "npm run build:webui && vite build --mode shell || exit 1",
|
"build:shell": "npm run build:webui && vite build --mode shell || exit 1",
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export const napCatVersion = '4.1.18';
|
export const napCatVersion = '4.1.21';
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import pathLib from 'node:path';
|
|||||||
import { defaultVideoThumbB64, getVideoInfo } from '@/common/video';
|
import { defaultVideoThumbB64, getVideoInfo } from '@/common/video';
|
||||||
import ffmpeg from 'fluent-ffmpeg';
|
import ffmpeg from 'fluent-ffmpeg';
|
||||||
import { encodeSilk } from '@/common/audio';
|
import { encodeSilk } from '@/common/audio';
|
||||||
import { MessageContext } from '@/onebot/api';
|
import { SendMessageContext } from '@/onebot/api';
|
||||||
import { getFileTypeForSendType } from '../helper/msg';
|
import { getFileTypeForSendType } from '../helper/msg';
|
||||||
|
|
||||||
export class NTQQFileApi {
|
export class NTQQFileApi {
|
||||||
@ -91,7 +91,7 @@ export class NTQQFileApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendFileElement(context: MessageContext, filePath: string, fileName: string = '', folderId: string = '',): Promise<SendFileElement> {
|
async createValidSendFileElement(context: SendMessageContext, filePath: string, fileName: string = '', folderId: string = '',): Promise<SendFileElement> {
|
||||||
const {
|
const {
|
||||||
fileName: _fileName,
|
fileName: _fileName,
|
||||||
path,
|
path,
|
||||||
@ -113,7 +113,7 @@ export class NTQQFileApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendPicElement(context: MessageContext, picPath: string, summary: string = '', subType: PicSubType = 0): Promise<SendPicElement> {
|
async createValidSendPicElement(context: SendMessageContext, picPath: string, summary: string = '', subType: PicSubType = 0): Promise<SendPicElement> {
|
||||||
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
|
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
|
||||||
if (fileSize === 0) {
|
if (fileSize === 0) {
|
||||||
throw new Error('文件异常,大小为0');
|
throw new Error('文件异常,大小为0');
|
||||||
@ -141,7 +141,7 @@ export class NTQQFileApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendVideoElement(context: MessageContext, filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
|
async createValidSendVideoElement(context: SendMessageContext, filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
|
||||||
const logger = this.core.context.logger;
|
const logger = this.core.context.logger;
|
||||||
let videoInfo = {
|
let videoInfo = {
|
||||||
width: 1920,
|
width: 1920,
|
||||||
|
|||||||
@ -383,12 +383,39 @@ export enum MemberAddShowType {
|
|||||||
K_YOU_INVITE_OTHER = 7,
|
K_YOU_INVITE_OTHER = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群提示元素成员角色枚举
|
||||||
|
*/
|
||||||
|
export enum NTGroupGrayElementRole {
|
||||||
|
KOTHER = 0,
|
||||||
|
KMEMBER = 1,
|
||||||
|
KADMIN = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群灰色提示成员接口
|
||||||
|
* */
|
||||||
|
|
||||||
|
export interface NTGroupGrayMember {
|
||||||
|
serialVersionUID: string;
|
||||||
|
uid: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 群灰色提示邀请者和被邀请者接口
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
export interface NTGroupGrayInviterAndInvite {
|
||||||
|
invited: NTGroupGrayMember;
|
||||||
|
inviter: NTGroupGrayMember;
|
||||||
|
serialVersionUID: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 群提示元素接口
|
* 群提示元素接口
|
||||||
*/
|
*/
|
||||||
export interface TipGroupElement {
|
export interface TipGroupElement {
|
||||||
type: TipGroupElementType;
|
type: TipGroupElementType;
|
||||||
role: 0;
|
role: NTGroupGrayElementRole;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
memberUid: string;
|
memberUid: string;
|
||||||
memberNick: string;
|
memberNick: string;
|
||||||
@ -399,13 +426,13 @@ export interface TipGroupElement {
|
|||||||
createGroup: null;
|
createGroup: null;
|
||||||
memberAdd?: {
|
memberAdd?: {
|
||||||
showType: MemberAddShowType;
|
showType: MemberAddShowType;
|
||||||
otherAdd: null;
|
otherAdd: NTGroupGrayMember;
|
||||||
otherAddByOtherQRCode: null;
|
otherAddByOtherQRCode: NTGroupGrayInviterAndInvite;
|
||||||
otherAddByYourQRCode: null;
|
otherAddByYourQRCode: NTGroupGrayMember;
|
||||||
youAddByOtherQRCode: null;
|
youAddByOtherQRCode: NTGroupGrayMember;
|
||||||
otherInviteOther: null;
|
otherInviteOther: NTGroupGrayInviterAndInvite;
|
||||||
otherInviteYou: null;
|
otherInviteYou: NTGroupGrayMember;
|
||||||
youInviteOther: null
|
youInviteOther: NTGroupGrayMember;
|
||||||
};
|
};
|
||||||
shutUp?: {
|
shutUp?: {
|
||||||
curTime: string;
|
curTime: string;
|
||||||
|
|||||||
@ -113,6 +113,7 @@ export class OneBotMsgApi {
|
|||||||
return {
|
return {
|
||||||
type: OB11MessageDataType.image,
|
type: OB11MessageDataType.image,
|
||||||
data: {
|
data: {
|
||||||
|
pic_type: element.picType,
|
||||||
summary: element.summary,
|
summary: element.summary,
|
||||||
file: encodedFileId,
|
file: encodedFileId,
|
||||||
sub_type: element.picSubType,
|
sub_type: element.picSubType,
|
||||||
|
|||||||
@ -176,67 +176,48 @@ export class NapCatOneBot11Adapter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async reloadNetwork(prev: OneBotConfig, now: OneBotConfig) {
|
private async reloadNetwork(prev: OneBotConfig, now: OneBotConfig): Promise<void> {
|
||||||
const prevLog = await this.creatOneBotLog(prev);
|
const prevLog = await this.creatOneBotLog(prev);
|
||||||
const newLog = await this.creatOneBotLog(now);
|
const newLog = await this.creatOneBotLog(now);
|
||||||
this.context.logger.log(`[Notice] [OneBot11] 配置变更前:\n${prevLog}`);
|
this.context.logger.log(`[Notice] [OneBot11] 配置变更前:\n${prevLog}`);
|
||||||
this.context.logger.log(`[Notice] [OneBot11] 配置变更后:\n${newLog}`);
|
this.context.logger.log(`[Notice] [OneBot11] 配置变更后:\n${newLog}`);
|
||||||
|
|
||||||
const { added: addedHttpServers, removed: removedHttpServers } = this.findDifference(prev.network.httpServers, now.network.httpServers);
|
await this.handleConfigChange(prev.network.httpServers, now.network.httpServers, OB11PassiveHttpAdapter);
|
||||||
const { added: addedHttpClients, removed: removedHttpClients } = this.findDifference(prev.network.httpClients, now.network.httpClients);
|
await this.handleConfigChange(prev.network.httpClients, now.network.httpClients, OB11ActiveHttpAdapter);
|
||||||
const { added: addedWebSocketServers, removed: removedWebSocketServers } = this.findDifference(prev.network.websocketServers, now.network.websocketServers);
|
await this.handleConfigChange(prev.network.websocketServers, now.network.websocketServers, OB11PassiveWebSocketAdapter);
|
||||||
const { added: addedWebSocketClients, removed: removedWebSocketClients } = this.findDifference(prev.network.websocketClients, now.network.websocketClients);
|
await this.handleConfigChange(prev.network.websocketClients, now.network.websocketClients, OB11ActiveWebSocketAdapter);
|
||||||
|
|
||||||
await this.handleRemovedAdapters(removedHttpServers);
|
|
||||||
await this.handleRemovedAdapters(removedHttpClients);
|
|
||||||
await this.handleRemovedAdapters(removedWebSocketServers);
|
|
||||||
await this.handleRemovedAdapters(removedWebSocketClients);
|
|
||||||
|
|
||||||
await this.handlerConfigChange(now.network.httpServers);
|
|
||||||
await this.handlerConfigChange(now.network.httpClients);
|
|
||||||
await this.handlerConfigChange(now.network.websocketServers);
|
|
||||||
await this.handlerConfigChange(now.network.websocketClients);
|
|
||||||
|
|
||||||
await this.handleAddedAdapters(addedHttpServers, OB11PassiveHttpAdapter);
|
|
||||||
await this.handleAddedAdapters(addedHttpClients, OB11ActiveHttpAdapter);
|
|
||||||
await this.handleAddedAdapters(addedWebSocketServers, OB11PassiveWebSocketAdapter);
|
|
||||||
await this.handleAddedAdapters(addedWebSocketClients, OB11ActiveWebSocketAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handlerConfigChange(adapters: Array<NetworkConfigAdapter>) {
|
private async handleConfigChange(
|
||||||
for (const adapterConfig of adapters) {
|
prevConfig: NetworkConfigAdapter[],
|
||||||
|
nowConfig: NetworkConfigAdapter[],
|
||||||
|
adapterClass: new (...args: any[]) => IOB11NetworkAdapter
|
||||||
|
): Promise<void> {
|
||||||
|
// 通知新配置重载 删除关闭的 加入新开的
|
||||||
|
for (const adapterConfig of nowConfig) {
|
||||||
const existingAdapter = this.networkManager.findSomeAdapter(adapterConfig.name);
|
const existingAdapter = this.networkManager.findSomeAdapter(adapterConfig.name);
|
||||||
if (existingAdapter) {
|
if (existingAdapter) {
|
||||||
const networkChange = await existingAdapter.reload(adapterConfig);
|
const networkChange = await existingAdapter.reload(adapterConfig);
|
||||||
if (networkChange === OB11NetworkReloadType.NetWorkClose) {
|
if (networkChange === OB11NetworkReloadType.NetWorkClose) {
|
||||||
this.networkManager.closeSomeAdapters([existingAdapter]);
|
await this.networkManager.closeSomeAdaterWhenOpen([existingAdapter]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const newAdapter = new adapterClass(adapterConfig.name, adapterConfig, this.core, this.actions);
|
||||||
|
await this.networkManager.registerAdapterAndOpen(newAdapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 比较旧的找不到的回收
|
||||||
|
for (const adapterConfig of prevConfig) {
|
||||||
|
const existingAdapter = nowConfig.find((e) => e.name === adapterConfig.name);
|
||||||
|
if (!existingAdapter) {
|
||||||
|
const existingAdapter = this.networkManager.findSomeAdapter(adapterConfig.name);
|
||||||
|
if (existingAdapter) {
|
||||||
|
await this.networkManager.closeSomeAdaterWhenOpen([existingAdapter]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleRemovedAdapters(adapters: Array<{ name: string }>): Promise<void> {
|
|
||||||
for (const adapter of adapters) {
|
|
||||||
await this.networkManager.closeAdapterByPredicate((existingAdapter) => existingAdapter.name === adapter.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleAddedAdapters<T extends new (...args: any[]) => IOB11NetworkAdapter>(addedAdapters: Array<NetworkConfigAdapter>, AdapterClass: T) {
|
|
||||||
for (const adapter of addedAdapters) {
|
|
||||||
if (adapter.enable) {
|
|
||||||
const newAdapter = new AdapterClass(adapter.name, adapter, this.core, this.actions);
|
|
||||||
await newAdapter.open();
|
|
||||||
this.networkManager.registerAdapter(newAdapter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private findDifference<T>(prev: T[], now: T[]): { added: T[]; removed: T[] } {
|
|
||||||
const added = now.filter((item) => !prev.includes(item));
|
|
||||||
const removed = prev.filter((item) => !now.includes(item));
|
|
||||||
return { added, removed };
|
|
||||||
}
|
|
||||||
|
|
||||||
private initMsgListener() {
|
private initMsgListener() {
|
||||||
const msgListener = new NodeIKernelMsgListener();
|
const msgListener = new NodeIKernelMsgListener();
|
||||||
msgListener.onRecvSysMsg = (msg) => {
|
msgListener.onRecvSysMsg = (msg) => {
|
||||||
|
|||||||
@ -68,6 +68,14 @@ export class OB11NetworkManager {
|
|||||||
await adapter.close();
|
await adapter.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async closeSomeAdaterWhenOpen(adaptersToClose: IOB11NetworkAdapter[]) {
|
||||||
|
for (const adapter of adaptersToClose) {
|
||||||
|
this.adapters.delete(adapter.name);
|
||||||
|
if(adapter.isEnable){
|
||||||
|
await adapter.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
findSomeAdapter(name: string) {
|
findSomeAdapter(name: string) {
|
||||||
return this.adapters.get(name);
|
return this.adapters.get(name);
|
||||||
|
|||||||
@ -31,13 +31,9 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
) {
|
) {
|
||||||
this.config = structuredClone(config);
|
this.config = structuredClone(config);
|
||||||
this.logger = core.context.logger;
|
this.logger = core.context.logger;
|
||||||
if (this.config.host === '0.0.0.0') {
|
|
||||||
//兼容配置同时处理0.0.0.0逻辑
|
|
||||||
this.config.host = '';
|
|
||||||
}
|
|
||||||
this.wsServer = new WebSocketServer({
|
this.wsServer = new WebSocketServer({
|
||||||
port: this.config.port,
|
port: this.config.port,
|
||||||
host: this.config.host,
|
host: this.config.host === '0.0.0.0' ? '' : this.config.host,
|
||||||
maxPayload: 1024 * 1024 * 1024,
|
maxPayload: 1024 * 1024 * 1024,
|
||||||
});
|
});
|
||||||
this.wsServer.on('connection', async (wsClient, wsReq) => {
|
this.wsServer.on('connection', async (wsClient, wsReq) => {
|
||||||
|
|||||||
@ -33,6 +33,7 @@ export const QQCheckLoginStatusHandler: RequestHandler = async (req, res) => {
|
|||||||
message: 'success',
|
message: 'success',
|
||||||
data: {
|
data: {
|
||||||
isLogin: await WebUiDataRuntime.getQQLoginStatus(),
|
isLogin: await WebUiDataRuntime.getQQLoginStatus(),
|
||||||
|
qrcodeurl: await WebUiDataRuntime.getQQLoginQrcodeURL()
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user