Compare commits

...

39 Commits

Author SHA1 Message Date
手瓜一十雪
68216415b6 release: 2.1.0 2024-08-21 08:16:59 +08:00
手瓜一十雪
ba53da18d1 release: 2.1.0 2024-08-21 08:13:53 +08:00
手瓜一十雪
9b76fa3582 chore: LLNC Deprecated 2024-08-21 08:12:52 +08:00
手瓜一十雪
13d8d10a7f Merge pull request #289 from NapNeko/extend
Support: 9.9.15-27254
2024-08-21 08:09:37 +08:00
手瓜一十雪
5c6c1bb09d Merge branch 'main' into extend 2024-08-20 20:36:53 +08:00
手瓜一十雪
12105d96ea release: v2.0.37 2024-08-20 20:33:06 +08:00
手瓜一十雪
4054756035 Merge branch 'main' into extend 2024-08-20 20:27:22 +08:00
手瓜一十雪
16769c7838 fix: 提高兼容性 2024-08-20 20:26:49 +08:00
手瓜一十雪
cd076c5959 fix 2024-08-20 20:22:44 +08:00
手瓜一十雪
f52e1aa131 Merge branch 'main' into extend 2024-08-20 20:07:02 +08:00
手瓜一十雪
fdc1ef7e9a fix: error 2024-08-20 20:03:41 +08:00
手瓜一十雪
9cccf2d47b Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-20 20:03:20 +08:00
手瓜一十雪
0796f27f2a fix: file ext and blank data 2024-08-20 20:03:02 +08:00
手瓜一十雪
6c84014e0d doc: 2024-08-20 19:36:13 +08:00
手瓜一十雪
cd496a22bf chore: 调整appid 2024-08-20 17:47:14 +08:00
手瓜一十雪
0200343780 chore: 调整appid 2024-08-20 17:12:07 +08:00
手瓜一十雪
47fb629d26 Merge branch 'main' into extend 2024-08-20 16:59:17 +08:00
Alen
71ae08706b release: v2.0.35 2024-08-20 16:49:16 +08:00
Alen
50dd798757 Merge pull request #285 from cnxysoft/upmain
refactor: 接口兼容
2024-08-20 16:39:51 +08:00
Alen
0c8cf73746 refactor: 接口兼容
调整SetGroupHeader接口为SetGroupPortrait,使其兼容gocq标准
2024-08-20 16:35:23 +08:00
Alen
1bee811312 refactor: 接口兼容
调整SetSelfProfile接口为SetQQProfile,使其兼容gocq标准
2024-08-20 16:05:23 +08:00
Alen
b4c0068637 Merge pull request #284 from cnxysoft/upmain
fix: 多处修复
2024-08-20 14:31:02 +08:00
Alen
f484c6e5fe fix: 多处修复
1.修复group_card事件上报
2.修复group_admin事件上报
2024-08-20 14:28:32 +08:00
Alen
7a08187c5f fix: Uid转Uin
修复Uid转Uin兜底逻辑
2024-08-20 07:28:25 +08:00
Alen
c4d7d5a0d4 fix: 多处修改
1.修改(疑似)旧设备回复消息验证失败的解决方案
2.修复Base64发送文件失败
3.修复群时间监听器未注册
2024-08-20 01:02:03 +08:00
手瓜一十雪
5b75e753a7 Util 2024-08-19 21:38:31 +08:00
手瓜一十雪
326e9b86ce Merge branch 'main' into extend 2024-08-19 20:44:49 +08:00
手瓜一十雪
d22f5d369c releas: v2.0.34 2024-08-19 20:27:04 +08:00
手瓜一十雪
d76503995c fix: ws心跳实现 2024-08-19 20:24:26 +08:00
手瓜一十雪
eab930c083 doc: 小tip 2024-08-19 19:33:58 +08:00
手瓜一十雪
e430cc54f2 Merge branch 'main' into extend 2024-08-19 19:03:31 +08:00
手瓜一十雪
fd26a9c698 fix 2024-08-19 18:53:47 +08:00
手瓜一十雪
e79b608f77 support: 27206 2024-08-19 18:47:12 +08:00
Wesley F. Young
42b23a6c9c docs: clarify version range 2024-08-19 16:17:33 +08:00
Alen
8d94f24c71 release: v2.0.33 2024-08-19 15:43:41 +08:00
Alen
6ac74c39d9 Merge pull request #279 from cnxysoft/upmain
fix: 多处修复
2024-08-19 15:41:10 +08:00
Alen
836eb7b708 fix: 多处修复
1.修复部分Uin转换失败导致的相关API错误
2.继续改进get_group_member_info效率
3.增加fetchUserDetailInfo容错(暂时性/待观察)
2024-08-19 15:39:34 +08:00
手瓜一十雪
03098ee024 chore: util 2024-08-17 15:21:47 +08:00
手瓜一十雪
a2bfdd003c fix: getNTUserDataInfoConfig 2024-08-17 15:18:33 +08:00
33 changed files with 270 additions and 227 deletions

View File

@@ -30,34 +30,34 @@ jobs:
ls ls
node ./script/checkVersion.cjs node ./script/checkVersion.cjs
sh ./checkVersion.sh sh ./checkVersion.sh
Build-LiteLoader: # Build-LiteLoader:
needs: [check-version] # needs: [check-version]
runs-on: ubuntu-latest # runs-on: ubuntu-latest
steps: # steps:
- name: Clone Main Repository # - name: Clone Main Repository
uses: actions/checkout@v4 # uses: actions/checkout@v4
with: # with:
repository: 'NapNeko/NapCatQQ' # repository: 'NapNeko/NapCatQQ'
submodules: true # submodules: true
ref: main # ref: main
token: ${{ secrets.NAPCAT_BUILD }} # token: ${{ secrets.NAPCAT_BUILD }}
- name: Use Node.js 20.X # - name: Use Node.js 20.X
uses: actions/setup-node@v4 # uses: actions/setup-node@v4
with: # with:
node-version: 20.x # node-version: 20.x
- name: Build NuCat Framework # - name: Build NuCat Framework
run: | # run: |
npm i # npm i
npm run build:framework # npm run build:framework
cd dist # cd dist
npm i --omit=dev # npm i --omit=dev
cd .. # cd ..
- name: Upload Artifact # - name: Upload Artifact
uses: actions/upload-artifact@v4 # uses: actions/upload-artifact@v4
with: # with:
name: NapCat.Framework # name: NapCat.Framework
path: dist # path: dist
Build-Shell: Build-Shell:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [check-version] needs: [check-version]
@@ -90,24 +90,31 @@ jobs:
path: dist path: dist
release-napcat: release-napcat:
needs: [Build-LiteLoader,Build-Shell] needs: [Build-Shell]
# needs: [Build-LiteLoader,Build-Shell]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Download All Artifact - name: Download All Artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
- name: Compress subdirectories - name: Compress subdirectories
run: | run: |
cd ./NapCat.Shell/ cd ./NapCat.Shell/
zip -q -r NapCat.Shell.zip * zip -q -r NapCat.Shell.zip *
cd .. cd ..
cd ./NapCat.Framework/
zip -q -r NapCat.Framework.zip *
cd ..
rm ./NapCat.Shell.zip -rf rm ./NapCat.Shell.zip -rf
rm ./NapCat.Framework.zip -rf
mv ./NapCat.Shell/NapCat.Shell.zip ./ mv ./NapCat.Shell/NapCat.Shell.zip ./
mv ./NapCat.Framework/NapCat.Framework.zip ./ # - name: Compress subdirectories
# run: |
# cd ./NapCat.Shell/
# zip -q -r NapCat.Shell.zip *
# cd ..
# cd ./NapCat.Framework/
# zip -q -r NapCat.Framework.zip *
# cd ..
# rm ./NapCat.Shell.zip -rf
# rm ./NapCat.Framework.zip -rf
# mv ./NapCat.Shell/NapCat.Shell.zip ./
# mv ./NapCat.Framework/NapCat.Framework.zip ./
- name: Extract version from tag - name: Extract version from tag
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
@@ -121,6 +128,5 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
body_path: CHANGELOG.md body_path: CHANGELOG.md
files: | files: |
NapCat.Framework.zip
NapCat.Shell.zip NapCat.Shell.zip
draft: true draft: true

View File

@@ -4,7 +4,9 @@
--- ---
## To Be Continued ## To Be Continued
当前版本请使用低于27187 (不包含) 高于26702 (包含) 的QQ版本运行 当前版本请使用内核构建版本(版本号最后的五位数)为 26702 至 26909 的 PC NTQQ 运行
高版本QQ NapCat已完成兼容 暂时不发布 直至版本2.0.x结束。
## 项目介绍 ## 项目介绍
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。 NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
@@ -12,6 +14,7 @@ NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
## 项目优势 ## 项目优势
- [x] **多种启动方式**支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动 - [x] **多种启动方式**支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动
- [x] **低占用**:无头模式占用资源极低,适合在服务器上运行 - [x] **低占用**:无头模式占用资源极低,适合在服务器上运行
- [x] **超多接口**在实现大部分Onebot接口上扩展了一套私有API
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷 - [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
## 如何使用 ## 如何使用

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "2.0.32", "version": "2.1.0",
"icon": "./logo.png", "icon": "./logo.png",
"authors": [ "authors": [
{ {

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "2.0.32", "version": "2.1.0",
"scripts": { "scripts": {
"build:framework": "vite build --mode framework", "build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell", "build:shell": "vite build --mode shell",

View File

@@ -16,16 +16,13 @@ export interface ListenerIBase {
} }
export class LegacyNTEventWrapper { export class LegacyNTEventWrapper {
private listenerMapping: Record<string, ListenerIBase>; //ListenerName-Unique -> Listener构造函数
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
private listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例 private listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例
private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func} private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
constructor( constructor(
listenerMapping: Record<string, ListenerIBase>, wrapperSession: NodeIQQNTWrapperSession
wrapperSession: NodeIQQNTWrapperSession,
) { ) {
this.listenerMapping = listenerMapping;
this.WrapperSession = wrapperSession; this.WrapperSession = wrapperSession;
} }
@@ -72,18 +69,17 @@ export class LegacyNTEventWrapper {
} }
createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T { createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
const ListenerType = this.listenerMapping![listenerMainName]; let existListener = this.listenerManager.get(listenerMainName + uniqueCode);
let Listener = this.listenerManager.get(listenerMainName + uniqueCode); if (!existListener) {
if (!Listener && ListenerType) { let Listener = this.createProxyDispatch(listenerMainName);
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1]; const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener'; const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
const addfunc = this.createEventFunction<(listener: T) => number>(Service); const addfunc = this.createEventFunction<(listener: T) => number>(Service);
addfunc!(Listener as T); addfunc!(Listener as T);
//console.log(addfunc!(Listener as T));
this.listenerManager.set(listenerMainName + uniqueCode, Listener); this.listenerManager.set(listenerMainName + uniqueCode, Listener);
return Listener as T;
} }
return Listener as T; return existListener as T;
} }
//统一回调清理事件 //统一回调清理事件

View File

@@ -2,7 +2,7 @@ import path, { dirname } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import fs from 'fs'; import fs from 'fs';
export const napcat_version = '2.0.32'; export const napcat_version = '2.1.0';
export class NapCatPathWrapper { export class NapCatPathWrapper {
binaryPath: string; binaryPath: string;

View File

@@ -74,7 +74,7 @@ export class QQBasicInfoWrapper {
this.context.logger.log( this.context.logger.log(
`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`, `[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`,
); );
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: this.getQUAInternal() }; return { appid: systemPlatform === 'linux' ? '537240645' : '537240610', qua: this.getQUAInternal() };
} }
} }

View File

@@ -259,7 +259,7 @@ export async function uri2local(dir: string, uri: string, filename: string | und
if (success) { if (success) {
filePath = fileTypePath; filePath = fileTypePath;
fileExt = ext; fileExt = ext;
filename = path.basename(filePath, fileExt); filename = filename + '.' + ext;
} }
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true }; return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
} }

View File

@@ -97,19 +97,19 @@ export function isEqual(obj1: any, obj2: any) {
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType { export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
if (os.platform() === 'linux') { if (os.platform() === 'linux') {
return { return {
baseVersion: '3.2.12-26702', baseVersion: '3.2.12-27206',
curVersion: '3.2.12-26702', curVersion: '3.2.12-27206',
prevVersion: '', prevVersion: '',
onErrorVersions: [], onErrorVersions: [],
buildId: '26702', buildId: '27206',
}; };
} }
return { return {
baseVersion: '9.9.15-26702', baseVersion: '9.9.15-27206',
curVersion: '9.9.15-26702', curVersion: '9.9.15-27206',
prevVersion: '', prevVersion: '',
onErrorVersions: [], onErrorVersions: [],
buildId: '26702', buildId: '27206',
}; };
} }

View File

@@ -261,7 +261,7 @@ export class NTQQGroupApi {
( (
'NodeIKernelGroupListener/onMemberInfoChange', 'NodeIKernelGroupListener/onMemberInfoChange',
1, 1,
forced ? 5000 : 500, forced ? 5000 : 250,
(params) => { (params) => {
return params === GroupCode; return params === GroupCode;
}, },
@@ -269,7 +269,7 @@ export class NTQQGroupApi {
const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo'); const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo');
const retData = await EventFunc!(GroupCode, [uid], forced); const retData = await EventFunc!(GroupCode, [uid], forced);
if (retData.result !== 0) { if (retData.result !== 0) {
throw new Error(`获取群成员信息失败: ${retData.errMsg}`); throw new Error(`${retData.errMsg}`);
} }
const result = await Listener as unknown; const result = await Listener as unknown;
let member: GroupMember | undefined; let member: GroupMember | undefined;

View File

@@ -120,20 +120,23 @@ export class NTQQUserApi {
...profile.simpleInfo.vasInfo, ...profile.simpleInfo.vasInfo,
...profile.commonExt, ...profile.commonExt,
...profile.simpleInfo.baseInfo, ...profile.simpleInfo.baseInfo,
qqLevel: profile.commonExt.qqLevel, qqLevel: profile.commonExt?.qqLevel,
age: profile.simpleInfo.baseInfo.age, age: profile.simpleInfo.baseInfo.age,
pendantId: '', pendantId: '',
}; };
return RetUser; return RetUser;
} }
async getUserDetailInfo(uid: string) { async getUserDetailInfo(uid: string): Promise<User> {
const ret = await this.fetchUserDetailInfo(uid, UserDetailSource.KDB); try {
if (ret.uin === '0') { let retUser = await this.fetchUserDetailInfo(uid, UserDetailSource.KDB);
console.log('[NapCat] [Mark] getUserDetailInfo Mode1 Failed.') if (retUser.uin !== '0') {
return await this.fetchUserDetailInfo(uid, UserDetailSource.KSERVER); return retUser;
}
} catch (e) {
} }
return ret; this.context.logger.logDebug('[NapCat] [Mark] getUserDetailInfo Mode1 Failed.');
return this.fetchUserDetailInfo(uid, UserDetailSource.KSERVER);
} }
async modifySelfProfile(param: ModifyProfileParams) { async modifySelfProfile(param: ModifyProfileParams) {
@@ -207,9 +210,9 @@ export class NTQQUserApi {
//后期改成流水线处理 //后期改成流水线处理
async getUinByUidV2(Uid: string) { async getUinByUidV2(Uid: string) {
let uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid); let uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
if (uin) return uin; if (uin) return uin;
uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid); uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
if (uin) return uin; if (uin) return uin;
uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid); uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid);
if (uin) return uin; if (uin) return uin;

View File

@@ -44,8 +44,8 @@ export class NapCatCore {
constructor(context: InstanceContext, selfInfo: SelfInfo) { constructor(context: InstanceContext, selfInfo: SelfInfo) {
this.selfInfo = selfInfo; this.selfInfo = selfInfo;
this.context = context; this.context = context;
this.util = new this.context.wrapper.NodeQQNTWrapperUtil(); this.util = this.context.wrapper.NodeQQNTWrapperUtil;
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session); this.eventWrapper = new LegacyNTEventWrapper(context.session);
this.apis = { this.apis = {
FileApi: new NTQQFileApi(this.context, this), FileApi: new NTQQFileApi(this.context, this),
SystemApi: new NTQQSystemApi(this.context, this), SystemApi: new NTQQSystemApi(this.context, this),
@@ -79,7 +79,7 @@ export class NapCatCore {
} }
get dataPath(): string { get dataPath(): string {
let result = this.util.getNTUserDataInfoConfig(); let result = this.context.wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
if (!result) { if (!result) {
result = path.resolve(os.homedir(), './.config/QQ'); result = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(result, { recursive: true }); fs.mkdirSync(result, { recursive: true });
@@ -98,7 +98,7 @@ export class NapCatCore {
}; };
//await sleep(2500); //await sleep(2500);
this.context.session.getMsgService().addKernelMsgListener( this.context.session.getMsgService().addKernelMsgListener(
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)), proxiedListenerOf(msgListener, this.context.logger) as any
); );
const profileListener = new ProfileListener(); const profileListener = new ProfileListener();
@@ -113,7 +113,7 @@ export class NapCatCore {
// } // }
}; };
this.context.session.getProfileService().addKernelProfileListener( this.context.session.getProfileService().addKernelProfileListener(
new this.context.wrapper.NodeIKernelProfileListener(proxiedListenerOf(profileListener, this.context.logger)), proxiedListenerOf(profileListener, this.context.logger),
); );
// 群相关 // 群相关
@@ -196,6 +196,9 @@ export class NapCatCore {
this.apis.GroupApi.groupMemberCache.set(groupCode, members); this.apis.GroupApi.groupMemberCache.set(groupCode, members);
} }
}; };
this.context.session.getGroupService().addKernelGroupListener(
proxiedListenerOf(profileListener, this.context.logger) as any
);
} }
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean { checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
if (memberNew.role !== memberOld?.role) { if (memberNew.role !== memberOld?.role) {

View File

@@ -1,58 +1,26 @@
{ {
"3.1.2-13107": { "3.2.12-27187": {
"appid": 537146866, "appid": 537240645,
"qua": "V1_LNX_NQ_3.1.2-13107_RDM_B" "qua": "V1_LNX_NQ_3.2.12_27187_GW_B"
}, },
"3.2.10-25765": { "3.2.12-27206": {
"appid": 537234773, "appid": 537240645,
"qua": "V1_LNX_NQ_3.2.10_25765_GW_B" "qua": "V1_LNX_NQ_3.2.12_27206_GW_B"
}, },
"3.2.12-26702": { "3.2.12-27254":{
"appid": 537237950, "appid": 537240795,
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B" "qua": "V1_LNX_NQ_3.2.12_27254_GW_B"
}, },
"3.2.12-26740": { "9.9.15-27187":{
"appid": 537237950, "appid": 537240610,
"qua": "V1_WIN_NQ_9.9.15_26740_GW_B" "qua": "V1_WIN_NQ_9.9.15_27187_GW_B"
}, },
"3.2.12-26909": { "9.9.15-27206":{
"appid": 537237923, "appid": 537240610,
"qua": "V1_LNX_NQ_3.2.12_26909_GW_B" "qua": "V1_WIN_NQ_9.9.15_27206_GW_B"
}, },
"9.9.11-24815": { "9.9.15-27254":{
"appid": 537226656, "appid": 537240709,
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B" "qua": "V1_WIN_NQ_9.9.15_27254_GW_B"
},
"9.9.12-25493": {
"appid": 537231759,
"qua": "V1_WIN_NQ_9.9.12_25493_GW_B"
},
"9.9.12-25765": {
"appid": 537234702,
"qua": "V1_WIN_NQ_9.9.12_25765_GW_B"
},
"9.9.12-26299": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26299_GW_B"
},
"9.9.12-26339": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26339_GW_B"
},
"9.9.12-26466": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26466_GW_B"
},
"9.9.15-26702": {
"appid": 537237765,
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
},
"9.9.15-26740": {
"appid": 537237765,
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
},
"9.9.15-26909": {
"appid": 537237802,
"qua": "V1_WIN_NQ_9.9.15_26909_GW_B"
} }
} }

View File

@@ -1,6 +1,6 @@
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities'; import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
interface IGroupListener { export interface IGroupListener {
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void; onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
onGroupExtListUpdate(...args: unknown[]): void; onGroupExtListUpdate(...args: unknown[]): void;

View File

@@ -1,4 +1,4 @@
import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener'; import { IGroupListener, NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import { import {
GroupExtParam, GroupExtParam,
GroupMember, GroupMember,
@@ -104,7 +104,7 @@ export interface NodeIKernelGroupService {
setHeader(uid: string, path: string): unknown; setHeader(uid: string, path: string): unknown;
addKernelGroupListener(listener: NodeIKernelGroupListener): number; addKernelGroupListener(listener: IGroupListener): number;
removeKernelGroupListener(listenerId: unknown): void; removeKernelGroupListener(listenerId: unknown): void;

View File

@@ -1,6 +1,6 @@
import { AnyCnameRecord } from 'node:dns'; import { AnyCnameRecord } from 'node:dns';
import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities'; import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
import { NodeIKernelProfileListener } from '../listeners'; import { NodeIKernelProfileListener, ProfileListener } from '../listeners';
import { GeneralCallResult } from '@/core/services/common'; import { GeneralCallResult } from '@/core/services/common';
export enum UserDetailSource { export enum UserDetailSource {
@@ -35,7 +35,7 @@ export interface NodeIKernelProfileService {
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>; fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
addKernelProfileListener(listener: NodeIKernelProfileListener): number; addKernelProfileListener(listener: ProfileListener): number;
removeKernelProfileListener(listenerId: number): void; removeKernelProfileListener(listenerId: number): void;

View File

@@ -35,6 +35,7 @@ import { NodeIkernelTestPerformanceService } from '../services/NodeIkernelTestPe
import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService'; import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService';
export interface NodeQQNTWrapperUtil { export interface NodeQQNTWrapperUtil {
get(): unknown;
// eslint-disable-next-line @typescript-eslint/no-misused-new // eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeQQNTWrapperUtil; new(): NodeQQNTWrapperUtil;

View File

@@ -41,8 +41,7 @@ export async function NCoreInitFramework(
online: true, online: true,
}); });
}; };
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener( loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
proxiedListenerOf(loginListener, logger)));
}); });
// 过早进入会导致addKernelMsgListener等Listener添加失败 // 过早进入会导致addKernelMsgListener等Listener添加失败
// await sleep(2500); // await sleep(2500);

View File

@@ -1,32 +0,0 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
nick: { type: 'string' },
longNick: { type: 'string' },
sex: { type: ['number', 'string'] },//传Sex值建议传0
},
required: ['nick', 'longNick', 'sex'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class SetSelfProfile extends BaseAction<Payload, any | null> {
actionName = ActionName.SetSelfProfile;
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const ret = await NTQQUserApi.modifySelfProfile({
nick: payload.nick,
longNick: payload.longNick,
sex: parseInt(payload.sex.toString()),
birthday: { birthday_year: '', birthday_month: '', birthday_day: '' },
location: undefined,
});
return ret;
}
}

View File

@@ -7,18 +7,18 @@ import { checkFileReceived, uri2local } from '@/common/utils/file';
interface Payload { interface Payload {
file: string, file: string,
groupCode: string group_id: number
} }
export default class SetGroupHeader extends BaseAction<Payload, any> { export default class SetGroupPortrait extends BaseAction<Payload, any> {
actionName = ActionName.SetGroupHeader; actionName = ActionName.SetGroupPortrait;
// 用不着复杂检测 // 用不着复杂检测
protected async check(payload: Payload): Promise<BaseCheckResult> { protected async check(payload: Payload): Promise<BaseCheckResult> {
if (!payload.file || typeof payload.file != 'string' || !payload.groupCode || typeof payload.groupCode != 'string') { if (!payload.file || typeof payload.file != 'string' || !payload.group_id || typeof payload.group_id != 'number') {
return { return {
valid: false, valid: false,
message: 'file和groupCode字段不能为空或者类型错误', message: 'file和group_id字段不能为空或者类型错误',
}; };
} }
return { return {
@@ -34,7 +34,7 @@ export default class SetGroupHeader extends BaseAction<Payload, any> {
} }
if (path) { if (path) {
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断 await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断
const ret = await NTQQGroupApi.setGroupAvatar(payload.groupCode, path); const ret = await NTQQGroupApi.setGroupAvatar(payload.group_id.toString(), path) as any;
if (!isLocal) { if (!isLocal) {
fs.unlink(path, () => { fs.unlink(path, () => {
}); });
@@ -43,11 +43,11 @@ export default class SetGroupHeader extends BaseAction<Payload, any> {
throw `头像${payload.file}设置失败,api无返回`; throw `头像${payload.file}设置失败,api无返回`;
} }
// log(`头像设置返回:${JSON.stringify(ret)}`) // log(`头像设置返回:${JSON.stringify(ret)}`)
// if (ret['result'] == 1004022) { if (ret['result'] == 1004022) {
// throw `头像${payload.file}设置失败,文件可能不是图片格式`; throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`;
// } else if (ret['result'] != 0) { } else if (ret['result'] != 0) {
// throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`; throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
// } }
return ret; return ret;
} else { } else {
if (!isLocal) { if (!isLocal) {

View File

@@ -0,0 +1,34 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
nickname: { type: 'string' },
personal_note: { type: 'string' },
sex: { type: ['number', 'string'] },//传Sex值建议传0
},
required: ['nickname'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class SetQQProfile extends BaseAction<Payload, any | null> {
actionName = ActionName.SetQQProfile;
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const self = this.CoreContext.selfInfo;
const OldProfile = await NTQQUserApi.getUserDetailInfo(self.uid);
const ret = await NTQQUserApi.modifySelfProfile({
nick: payload.nickname,
longNick: payload?.personal_note ?? OldProfile?.longNick!,
sex: parseInt(payload?.sex ? payload?.sex.toString() : OldProfile?.sex!.toString()),
birthday: { birthday_year: OldProfile?.birthday_year!.toString(), birthday_month: OldProfile?.birthday_month!.toString(), birthday_day: OldProfile?.birthday_day!.toString() },
location: undefined,
});
return ret;
}
}

View File

@@ -3,6 +3,7 @@ import { OB11Constructor } from '../../helper/data';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { GroupMember } from '@/core';
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
@@ -23,23 +24,25 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi; const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQGroupApi = this.CoreContext.apis.GroupApi; const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQWebApi = this.CoreContext.apis.WebApi;
const isNocache = typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache; const isNocache = typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache;
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString()); const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
if (!uid) throw (`Uin2Uid Error ${payload.user_id}不存在`); if (!uid) throw (`Uin2Uid Error ${payload.user_id}不存在`);
const member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache); const [member, info] = await Promise.allSettled([
if (!member) throw (`群(${payload.group_id})成员${payload.user_id}不存在`); NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache),
try { NTQQUserApi.getUserDetailInfo(uid),
const info = (await NTQQUserApi.getUserDetailInfo(member.uid)); ]);
this.CoreContext.context.logger.logDebug('群成员详细信息结果', info); if (member.status !== 'fulfilled') throw (`群(${payload.group_id})成员${payload.user_id}不存在 ${member.reason}`);
Object.assign(member, info); if (info.status === 'fulfilled') {
} catch (e) { this.CoreContext.context.logger.logDebug("群成员详细信息结果", info.value);
this.CoreContext.context.logger.logDebug('获取群成员详细信息失败, 只能返回基础信息', e); Object.assign(member, info.value);
} else {
this.CoreContext.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息 ${info.reason}`);
} }
const date = Math.round(Date.now() / 1000); const date = Math.round(Date.now() / 1000);
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member); const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member.value as GroupMember);
retMember.last_sent_time = parseInt((await this.CoreContext.apis.GroupApi.getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString()); const Member = await this.CoreContext.apis.GroupApi.getGroupMember(payload.group_id.toString(), retMember.user_id);
retMember.join_time = parseInt((await this.CoreContext.apis.GroupApi.getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString()); retMember.last_sent_time = parseInt(Member?.lastSpeakTime || date.toString());
retMember.join_time = parseInt(Member?.joinTime || date.toString());
return retMember; return retMember;
} }
} }

View File

@@ -61,7 +61,7 @@ import { TranslateEnWordToZn } from './extends/TranslateEnWordToZn';
import { SetGroupFileFolder } from './file/SetGroupFileFolder'; import { SetGroupFileFolder } from './file/SetGroupFileFolder';
import { DelGroupFile } from './file/DelGroupFile'; import { DelGroupFile } from './file/DelGroupFile';
import { DelGroupFileFolder } from './file/DelGroupFileFolder'; import { DelGroupFileFolder } from './file/DelGroupFileFolder';
import { SetSelfProfile } from './extends/SetSelfProfile'; import { SetQQProfile } from './go-cqhttp/SetQQProfile'
import { ShareGroupEx, SharePeer } from './extends/ShareContact'; import { ShareGroupEx, SharePeer } from './extends/ShareContact';
import { CreateCollection } from './extends/CreateCollection'; import { CreateCollection } from './extends/CreateCollection';
import { SetLongNick } from './extends/SetLongNick'; import { SetLongNick } from './extends/SetLongNick';
@@ -69,7 +69,7 @@ import DelEssenceMsg from './group/DelEssenceMsg';
import SetEssenceMsg from './group/SetEssenceMsg'; import SetEssenceMsg from './group/SetEssenceMsg';
import GetRecentContact from './user/GetRecentContact'; import GetRecentContact from './user/GetRecentContact';
import { GetProfileLike } from './extends/GetProfileLike'; import { GetProfileLike } from './extends/GetProfileLike';
import SetGroupHeader from './extends/SetGroupHeader'; import SetGroupPortrait from './go-cqhttp/SetGroupPortrait';
import { FetchCustomFace } from './extends/FetchCustomFace'; import { FetchCustomFace } from './extends/FetchCustomFace';
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile'; import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile';
import { FetchEmojiLike } from './extends/FetchEmojiLike'; import { FetchEmojiLike } from './extends/FetchEmojiLike';
@@ -79,6 +79,7 @@ import { NapCatOneBot11Adapter } from '@/onebot';
import GetGuildProfile from './guild/GetGuildProfile'; import GetGuildProfile from './guild/GetGuildProfile';
import SetModelShow from './go-cqhttp/SetModelShow'; import SetModelShow from './go-cqhttp/SetModelShow';
import { SetInputStatus } from './extends/SetInputStatus'; import { SetInputStatus } from './extends/SetInputStatus';
import { GetCSRF } from './system/GetCSRF';
export type ActionMap = Map<string, BaseAction<any, any>>; export type ActionMap = Map<string, BaseAction<any, any>>;
@@ -86,7 +87,7 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
const actionHandlers = [ const actionHandlers = [
new FetchEmojiLike(onebotContext, coreContext), new FetchEmojiLike(onebotContext, coreContext),
new GetFile(onebotContext, coreContext), new GetFile(onebotContext, coreContext),
new SetSelfProfile(onebotContext, coreContext), new SetQQProfile(onebotContext, coreContext),
new ShareGroupEx(onebotContext, coreContext), new ShareGroupEx(onebotContext, coreContext),
new SharePeer(onebotContext, coreContext), new SharePeer(onebotContext, coreContext),
new CreateCollection(onebotContext, coreContext), new CreateCollection(onebotContext, coreContext),
@@ -161,12 +162,13 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
new GetRecentContact(onebotContext, coreContext), new GetRecentContact(onebotContext, coreContext),
new MarkAllMsgAsRead(onebotContext, coreContext), new MarkAllMsgAsRead(onebotContext, coreContext),
new GetProfileLike(onebotContext, coreContext), new GetProfileLike(onebotContext, coreContext),
new SetGroupHeader(onebotContext, coreContext), new SetGroupPortrait(onebotContext, coreContext),
new FetchCustomFace(onebotContext, coreContext), new FetchCustomFace(onebotContext, coreContext),
new GoCQHTTPUploadPrivateFile(onebotContext, coreContext), new GoCQHTTPUploadPrivateFile(onebotContext, coreContext),
new GetGuildProfile(onebotContext, coreContext), new GetGuildProfile(onebotContext, coreContext),
new SetModelShow(onebotContext, coreContext), new SetModelShow(onebotContext, coreContext),
new SetInputStatus(onebotContext, coreContext), new SetInputStatus(onebotContext, coreContext),
new GetCSRF(onebotContext, coreContext),
]; ];
const actionMap = new Map(); const actionMap = new Map();
for (const action of actionHandlers) { for (const action of actionHandlers) {

View File

@@ -56,7 +56,7 @@ const _handlers: {
if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员'); if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员');
// then the qq is a group member // then the qq is a group member
// Mlikiowa V2.0.32 Refactor Todo // Mlikiowa V2.1.0 Refactor Todo
const uid = await coreContext.apis.UserApi.getUidByUinV2(`${atQQ}`); const uid = await coreContext.apis.UserApi.getUidByUinV2(`${atQQ}`);
if (!uid) throw new Error('Get Uid Error'); if (!uid) throw new Error('Get Uid Error');
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, ''); return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, '');
@@ -161,7 +161,7 @@ const _handlers: {
} else { } else {
postData = data; postData = data;
} }
// Mlikiowa V2.0.32 Refactor Todo // Mlikiowa V2.1.0 Refactor Todo
const signUrl = obContext.configLoader.configData.musicSignUrl; const signUrl = obContext.configLoader.configData.musicSignUrl;
if (!signUrl) { if (!signUrl) {
if (data.type === 'qq') { if (data.type === 'qq') {

View File

@@ -0,0 +1,12 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
export class GetCSRF extends BaseAction<any, any> {
actionName = ActionName.GetCSRF;
async _handle(payload: any) {
return {
token: "",
};
}
}

View File

@@ -91,7 +91,7 @@ export enum ActionName {
GetOnlineClient = 'get_online_clients', GetOnlineClient = 'get_online_clients',
OCRImage = 'ocr_image', OCRImage = 'ocr_image',
IOCRImage = '.ocr_image', IOCRImage = '.ocr_image',
SetSelfProfile = 'set_self_profile', SetQQProfile = 'set_qq_profile',
CreateCollection = 'create_collection', CreateCollection = 'create_collection',
GetCollectionList = 'get_collection_list', GetCollectionList = 'get_collection_list',
SetLongNick = 'set_self_longnick', SetLongNick = 'set_self_longnick',
@@ -100,12 +100,13 @@ export enum ActionName {
GetRecentContact = 'get_recent_contact', GetRecentContact = 'get_recent_contact',
_MarkAllMsgAsRead = '_mark_all_as_read', _MarkAllMsgAsRead = '_mark_all_as_read',
GetProfileLike = 'get_profile_like', GetProfileLike = 'get_profile_like',
SetGroupHeader = 'set_group_head', SetGroupPortrait = 'set_group_portrait',
FetchCustomFace = 'fetch_custom_face', FetchCustomFace = 'fetch_custom_face',
GOCQHTTP_UploadPrivateFile = 'upload_private_file', GOCQHTTP_UploadPrivateFile = 'upload_private_file',
TestApi01 = 'test_api_01', TestApi01 = 'test_api_01',
FetchEmojiLike = 'fetch_emoji_like', FetchEmojiLike = 'fetch_emoji_like',
GetGuildProfile = "get_guild_service_profile", GetGuildProfile = "get_guild_service_profile",
SetModelShow = "_set_model_show", SetModelShow = "_set_model_show",
SetInputStatus = "set_input_status" SetInputStatus = "set_input_status",
GetCSRF = "get_csrf_token",
} }

View File

@@ -30,6 +30,7 @@ import { EventType } from '../event/OB11BaseEvent';
import { encodeCQCode } from './cqcode'; import { encodeCQCode } from './cqcode';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent'; import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { OB11GroupBanEvent } from '../event/notice/OB11GroupBanEvent'; import { OB11GroupBanEvent } from '../event/notice/OB11GroupBanEvent';
import { OB11GroupCardEvent } from '../event/notice/OB11GroupCardEvent';
import { OB11GroupUploadNoticeEvent } from '../event/notice/OB11GroupUploadNoticeEvent'; import { OB11GroupUploadNoticeEvent } from '../event/notice/OB11GroupUploadNoticeEvent';
import { OB11GroupNoticeEvent } from '../event/notice/OB11GroupNoticeEvent'; import { OB11GroupNoticeEvent } from '../event/notice/OB11GroupNoticeEvent';
import { calcQQLevel, sleep, UUIDConverter } from '@/common/utils/helper'; import { calcQQLevel, sleep, UUIDConverter } from '@/common/utils/helper';
@@ -149,7 +150,6 @@ export class OB11Constructor {
message_data['type'] = OB11MessageDataType.reply; message_data['type'] = OB11MessageDataType.reply;
//log("收到回复消息", element.replyElement); //log("收到回复消息", element.replyElement);
try { try {
let oldMsgFlag = false;
const records = msg.records.find(msgRecord => msgRecord.msgId === element?.replyElement?.sourceMsgIdInRecords); const records = msg.records.find(msgRecord => msgRecord.msgId === element?.replyElement?.sourceMsgIdInRecords);
const peer = { const peer = {
chatType: msg.chatType, chatType: msg.chatType,
@@ -164,14 +164,17 @@ export class OB11Constructor {
chatType: msg.chatType, chatType: msg.chatType,
}, element.replyElement.replayMsgSeq, 1, true, true)).msgList.find(msg => msg.msgRandom === records.msgRandom); }, element.replyElement.replayMsgSeq, 1, true, true)).msgList.find(msg => msg.msgRandom === records.msgRandom);
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) { if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
if (!replyMsg && records.msgRandom === '0') oldMsgFlag = true;
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0]; replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0];
} }
if (msg.peerUin == '284840486') { if (msg.peerUin == '284840486') {
//合并消息内侧 消息具体定位不到 //合并消息内侧 消息具体定位不到
} }
if ((!replyMsg || (records.msgRandom !== replyMsg.msgRandom && !oldMsgFlag || (oldMsgFlag && records.msgSeq !== replyMsg.msgSeq))) && msg.peerUin !== '284840486') { if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
throw new Error('回复消息消息验证失败'); const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList;
if (replyMsgList.length < 1) {
throw new Error('回复消息消息验证失败');
}
replyMsg = replyMsgList.filter(e => e.msgSeq == records.msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
} }
message_data['data']['id'] = MessageUnique.createMsg({ message_data['data']['id'] = MessageUnique.createMsg({
peerUid: msg.peerUid, peerUid: msg.peerUid,
@@ -419,16 +422,15 @@ export class OB11Constructor {
return; return;
} }
//log("group msg", msg); //log("group msg", msg);
// Mlikiowa V2.0.32 Refactor Todo if (msg.senderUin && msg.senderUin !== '0') {
// if (msg.senderUin && msg.senderUin !== '0') { const member = await NTQQGroupApi.getGroupMember(msg.peerUid, msg.senderUin);
// const member = await getGroupMember(msg.peerUid, msg.senderUin); if (member && member.cardName !== msg.sendMemberName) {
// if (member && member.cardName !== msg.sendMemberName) { const newCardName = msg.sendMemberName || '';
// const newCardName = msg.sendMemberName || ''; const event = new OB11GroupCardEvent(core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
// const event = new OB11GroupCardEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName); member.cardName = newCardName;
// member.cardName = newCardName; return event;
// return event; }
// } }
// }
for (const element of msg.elements) { for (const element of msg.elements) {
const grayTipElement = element.grayTipElement; const grayTipElement = element.grayTipElement;

View File

@@ -9,6 +9,7 @@ import {
NapCatCore, NapCatCore,
RawMessage, RawMessage,
SendStatusType, SendStatusType,
GroupMemberRole,
} from '@/core'; } from '@/core';
import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config'; import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config';
import { OneBotApiContextType } from '@/onebot/types'; import { OneBotApiContextType } from '@/onebot/types';
@@ -274,7 +275,7 @@ export class NapCatOneBot11Adapter {
}; };
this.context.session.getMsgService().addKernelMsgListener( this.context.session.getMsgService().addKernelMsgListener(
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)), proxiedListenerOf(msgListener, this.context.logger) as any
); );
} }
@@ -301,7 +302,7 @@ export class NapCatOneBot11Adapter {
}; };
this.context.session.getBuddyService().addKernelBuddyListener( this.context.session.getBuddyService().addKernelBuddyListener(
new this.context.wrapper.NodeIKernelBuddyListener(proxiedListenerOf(buddyListener, this.context.logger)), proxiedListenerOf(buddyListener, this.context.logger) as any
); );
} }
@@ -413,8 +414,31 @@ export class NapCatOneBot11Adapter {
} }
}; };
groupListener.onMemberInfoChange = async (groupCode, changeType, members) => {
//this.context.logger.logDebug('收到群成员信息变动通知', groupCode, changeType);
if (changeType === 0) {
const existMembers = this.core.apis.GroupApi.groupMemberCache.get(groupCode);
if (!existMembers) return;
members.forEach((member) => {
const existMember = existMembers.get(member.uid);
if (!existMember?.isChangeRole) return;
this.context.logger.logDebug('变动管理员获取成功');
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
this.core,
parseInt(groupCode),
parseInt(member.uin),
member.role === GroupMemberRole.admin ? 'set' : 'unset',
);
this.networkManager.emitEvent(groupAdminNoticeEvent)
.catch(e => this.context.logger.logError('处理群管理员变动失败', e));
existMember.isChangeRole = false;
this.context.logger.logDebug('群管理员变动处理完毕');
});
}
};
this.context.session.getGroupService().addKernelGroupListener( this.context.session.getGroupService().addKernelGroupListener(
new this.context.wrapper.NodeIKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger)), proxiedListenerOf(groupListener, this.context.logger)
); );
} }

View File

@@ -71,6 +71,8 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
this.connection = new WebSocket(this.url, { this.connection = new WebSocket(this.url, {
maxPayload: 1024 * 1024 * 1024, maxPayload: 1024 * 1024 * 1024,
handshakeTimeout: 2000,
perMessageDeflate: false,
headers: { headers: {
'X-Self-ID': this.coreContext.selfInfo.uin, 'X-Self-ID': this.coreContext.selfInfo.uin,
'Authorization': `Bearer ${this.token}`, 'Authorization': `Bearer ${this.token}`,
@@ -79,6 +81,12 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
}, },
}); });
this.connection.on('ping', () => {
this.connection?.pong();
});
this.connection.on('pong', () => {
//this.logger.logDebug('[OneBot] [WebSocket Client] 收到pong');
});
this.connection.on('open', () => { this.connection.on('open', () => {
try { try {
this.connectEvent(this.coreContext); this.connectEvent(this.coreContext);
@@ -128,6 +136,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
this.logger.logDebug('[OneBot] [WebSocket Client] 收到正向Websocket消息', receiveData); this.logger.logDebug('[OneBot] [WebSocket Client] 收到正向Websocket消息', receiveData);
} catch (e) { } catch (e) {
this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo)); this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo));
return;
} }
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
const retdata = await this.actions.get(receiveData.action) const retdata = await this.actions.get(receiveData.action)

View File

@@ -36,7 +36,11 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
this.logger = coreContext.context.logger; this.logger = coreContext.context.logger;
this.heartbeatInterval = heartbeatInterval; this.heartbeatInterval = heartbeatInterval;
this.wsServer = new WebSocketServer({ port: port, host: ip, maxPayload: 1024 * 1024 * 1024, }); this.wsServer = new WebSocketServer({
port: port,
host: ip,
maxPayload: 1024 * 1024 * 1024,
});
const core = coreContext; const core = coreContext;
this.wsServer.on('connection', async (wsClient, wsReq) => { this.wsServer.on('connection', async (wsClient, wsReq) => {
if (!this.isOpen) { if (!this.isOpen) {
@@ -50,6 +54,12 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
wsClient.on('message', (message) => { wsClient.on('message', (message) => {
this.handleMessage(wsClient, message).then().catch(this.logger.logError); this.handleMessage(wsClient, message).then().catch(this.logger.logError);
}); });
wsClient.on('ping', () => {
wsClient.pong();
});
wsClient.on('pong', () => {
//this.logger.logDebug('[OneBot] [WebSocket Server] Pong received');
});
wsClient.once('close', () => { wsClient.once('close', () => {
this.wsClientsMutex.runExclusive(async () => { this.wsClientsMutex.runExclusive(async () => {
const index = this.wsClients.indexOf(wsClient); const index = this.wsClients.indexOf(wsClient);
@@ -145,6 +155,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
//this.logger.logDebug('收到正向Websocket消息', receiveData); //this.logger.logDebug('收到正向Websocket消息', receiveData);
} catch (e) { } catch (e) {
this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient); this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient);
return;
} }
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
const retdata = await this.actions.get(receiveData.action)?.websocketHandle(receiveData.params, echo || ''); const retdata = await this.actions.get(receiveData.action)?.websocketHandle(receiveData.params, echo || '');

View File

@@ -43,12 +43,12 @@ export async function NCoreInitShell() {
// from constructor // from constructor
const engine = new wrapper.NodeIQQNTWrapperEngine(); const engine = new wrapper.NodeIQQNTWrapperEngine();
const util = new wrapper.NodeQQNTWrapperUtil(); //const util = wrapper.NodeQQNTWrapperUtil.get();
const loginService = new wrapper.NodeIKernelLoginService(); const loginService = new wrapper.NodeIKernelLoginService();
const session = new wrapper.NodeIQQNTWrapperSession(); const session = new wrapper.NodeIQQNTWrapperSession();
// from get dataPath // from get dataPath
let dataPath = util.getNTUserDataInfoConfig(); let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
if (!dataPath) { if (!dataPath) {
dataPath = path.resolve(os.homedir(), './.config/QQ'); dataPath = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(dataPath, { recursive: true }); fs.mkdirSync(dataPath, { recursive: true });
@@ -70,7 +70,7 @@ export async function NCoreInitShell() {
}, },
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 }, thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },
}, },
new wrapper.NodeIGlobalAdapter(new GlobalAdapter()), new GlobalAdapter() as any,
); );
loginService.initConfig({ loginService.initConfig({
machineId: '', machineId: '',
@@ -140,8 +140,7 @@ export async function NCoreInitShell() {
logger.logError('[Core] [Login] Login Error , ErrInfo: ', args); logger.logError('[Core] [Login] Login Error , ErrInfo: ', args);
}; };
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener( loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
proxiedListenerOf(loginListener, logger)));
// 实现WebUi快速登录 // 实现WebUi快速登录
loginService.getLoginList().then((res) => { loginService.getLoginList().then((res) => {
@@ -188,11 +187,10 @@ export async function NCoreInitShell() {
} else { } else {
logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式'); logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式');
if (historyLoginList.length > 0) { if (historyLoginList.length > 0) {
logger.log(`可用于快速登录的 QQ\n${ logger.log(`可用于快速登录的 QQ\n${historyLoginList
historyLoginList .map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`) .join('\n')
.join('\n') }`);
}`);
} }
loginService.getQRCodePicture(); loginService.getQRCodePicture();
} }
@@ -220,9 +218,9 @@ export async function NCoreInitShell() {
}; };
session.init( session.init(
sessionConfig, sessionConfig,
new wrapper.NodeIDependsAdapter(new DependsAdapter()), new DependsAdapter() as any,
new wrapper.NodeIDispatcherAdapter(new DispatcherAdapter()), new DispatcherAdapter() as any,
new wrapper.NodeIKernelSessionListener(sessionListener), sessionListener as any,
); );
try { try {
session.startNT(0); session.startNT(0);

View File

@@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) {
SettingItem( SettingItem(
'<span id="napcat-update-title">Napcat</span>', '<span id="napcat-update-title">Napcat</span>',
undefined, undefined,
SettingButton('V2.0.32', 'napcat-update-button', 'secondary'), SettingButton('V2.1.0', 'napcat-update-button', 'secondary'),
), ),
]), ]),
SettingList([ SettingList([

View File

@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
SettingItem( SettingItem(
'<span id="napcat-update-title">Napcat</span>', '<span id="napcat-update-title">Napcat</span>',
void 0, void 0,
SettingButton("V2.0.32", "napcat-update-button", "secondary") SettingButton("V2.1.0", "napcat-update-button", "secondary")
) )
]), ]),
SettingList([ SettingList([