Compare commits

..

1 Commits

Author SHA1 Message Date
手瓜一十雪
beae21c4fd feat: 更新依赖项并移除速率限制中间件 2025-05-16 14:51:35 +08:00
64 changed files with 523 additions and 25465 deletions

View File

@@ -150,15 +150,3 @@ jobs:
NapCat.Shell.zip NapCat.Shell.zip
NapCat.Framework.Windows.Once.zip NapCat.Framework.Windows.Once.zip
draft: true draft: true
build-docker:
needs: release-napcat
runs-on: ubuntu-latest
steps:
- name: Dispatch Docker Build
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.NAPCAT_BUILD }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/NapNeko/NapCat-Docker/actions/workflows/docker-publish.yml/dispatches \
-d '{"ref": "main"}'

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
# Develop # Develop
node_modules/ node_modules/
package-lock.json
pnpm-lock.yaml pnpm-lock.yaml
out/ out/
dist/ dist/

View File

@@ -53,8 +53,6 @@ _Modern protocol-side framework implemented based on NTQQ._
+ [AstrBot](https://github.com/AstrBotDevs/AstrBot) 是完美适配本项目的LLM Bot框架 在此推荐一下 + [AstrBot](https://github.com/AstrBotDevs/AstrBot) 是完美适配本项目的LLM Bot框架 在此推荐一下
+ [MaiBot](https://github.com/MaiM-with-u/MaiBot) 一只赛博群友 麦麦 Bot框架 在此推荐一下
+ 不过最最重要的 还是需要感谢屏幕前的你哦~ + 不过最最重要的 还是需要感谢屏幕前的你哦~
--- ---

Binary file not shown.

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -92,9 +92,7 @@ const MusicInsert = () => {
className="w-96" className="w-96"
fullWidth fullWidth
selectedKey={mode} selectedKey={mode}
onSelectionChange={(key) => { onSelectionChange={setMode}
if (key !== null) setMode(key)
}}
> >
<Tab title="主流平台" key="default" className="flex flex-col gap-2"> <Tab title="主流平台" key="default" className="flex flex-col gap-2">
<Select <Select

View File

@@ -26,7 +26,7 @@ const itemVariants = {
opacity: 1, opacity: 1,
scale: 1, scale: 1,
y: 0, y: 0,
transition: { type: 'spring' as const, stiffness: 300, damping: 20 } transition: { type: 'spring', stiffness: 300, damping: 20 }
} }
} }

View File

@@ -24,7 +24,9 @@ const oneBotHttpApiGroup = {
}, },
'/get_group_system_msg': { '/get_group_system_msg': {
description: '获取群系统消息', description: '获取群系统消息',
request: z.object({}), request: z.object({
group_id: z.union([z.string(), z.number()]).describe('群号')
}),
response: baseResponseSchema.extend({ response: baseResponseSchema.extend({
data: z.object({ data: z.object({
InvitedRequest: z InvitedRequest: z
@@ -35,7 +37,6 @@ const oneBotHttpApiGroup = {
invitor_uin: z.string().describe('邀请人 QQ 号'), invitor_uin: z.string().describe('邀请人 QQ 号'),
invitor_nick: z.string().describe('邀请人昵称'), invitor_nick: z.string().describe('邀请人昵称'),
group_id: z.string().describe('群号'), group_id: z.string().describe('群号'),
message: z.string().describe('入群回答'),
group_name: z.string().describe('群名称'), group_name: z.string().describe('群名称'),
checked: z.boolean().describe('是否已处理'), checked: z.boolean().describe('是否已处理'),
actor: z.string().describe('处理人 QQ 号') actor: z.string().describe('处理人 QQ 号')
@@ -49,7 +50,6 @@ const oneBotHttpApiGroup = {
requester_uin: z.string().describe('请求人 QQ 号'), requester_uin: z.string().describe('请求人 QQ 号'),
requester_nick: z.string().describe('请求人昵称'), requester_nick: z.string().describe('请求人昵称'),
group_id: z.string().describe('群号'), group_id: z.string().describe('群号'),
message: z.string().describe('入群回答'),
group_name: z.string().describe('群名称'), group_name: z.string().describe('群名称'),
checked: z.boolean().describe('是否已处理'), checked: z.boolean().describe('是否已处理'),
actor: z.string().describe('处理人 QQ 号') actor: z.string().describe('处理人 QQ 号')
@@ -604,7 +604,7 @@ const oneBotHttpApiGroup = {
response: baseResponseSchema.extend({ response: baseResponseSchema.extend({
data: z data: z
.object({ .object({
group_id: z.number().describe('群号'), group_id: z.string().describe('群号'),
current_talkative: z current_talkative: z
.object({ .object({
user_id: z.number().describe('QQ 号'), user_id: z.number().describe('QQ 号'),

View File

@@ -56,9 +56,9 @@ export default function TerminalPage() {
setTabs((prev) => [...prev, newTab]) setTabs((prev) => [...prev, newTab])
setSelectedTab(id) setSelectedTab(id)
} catch (error: unknown) { } catch (error) {
console.error('Failed to create terminal:', error) console.error('Failed to create terminal:', error)
toast.error((error as Error).message) toast.error('创建终端失败')
} }
} }

8363
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "4.8.90", "version": "4.7.62",
"scripts": { "scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1",
@@ -41,29 +41,31 @@
"ajv": "^8.13.0", "ajv": "^8.13.0",
"async-mutex": "^0.5.0", "async-mutex": "^0.5.0",
"commander": "^13.0.0", "commander": "^13.0.0",
"compressing": "^1.10.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"esbuild": "0.25.5", "esbuild": "0.25.4",
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-import-resolver-typescript": "^4.0.0", "eslint-import-resolver-typescript": "^4.0.0",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
"express-rate-limit": "^7.5.0", "express": "npm:@karinjs/express@1.0.3",
"fast-xml-parser": "^4.3.6", "fast-xml-parser": "^4.3.6",
"file-type": "^21.0.0", "file-type": "^20.0.0",
"globals": "^16.0.0", "globals": "^16.0.0",
"json5": "^2.2.3", "json5": "^2.2.3",
"multer": "^2.0.1", "multer": "^1.4.5-lts.1",
"napcat.protobuf": "^1.1.4",
"on-finished": "^2.4.1",
"silk-wasm": "^3.6.1",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"typescript-eslint": "^8.13.0", "typescript-eslint": "^8.13.0",
"vite": "^6.0.1", "vite": "^6.0.1",
"vite-plugin-cp": "^6.0.0", "vite-plugin-cp": "^6.0.0",
"vite-plugin-wasm": "^3.4.1",
"vite-tsconfig-paths": "^5.1.0", "vite-tsconfig-paths": "^5.1.0",
"napcat.protobuf": "^1.1.4",
"winston": "^3.17.0", "winston": "^3.17.0",
"compressing": "^1.10.1" "ws": "npm:@karinjs/ws@1.2.0"
}, },
"dependencies": { "dependencies": {
"express": "^5.0.0", "type-is": "^2.0.1"
"silk-wasm": "^3.6.1",
"ws": "^8.18.0"
} }
} }

View File

@@ -13,15 +13,11 @@ export class NapCatPathWrapper {
constructor(mainPath: string = dirname(fileURLToPath(import.meta.url))) { constructor(mainPath: string = dirname(fileURLToPath(import.meta.url))) {
this.binaryPath = mainPath; this.binaryPath = mainPath;
let writePath: string; let writePath: string;
if (os.platform() === 'darwin') {
if (process.env['NAPCAT_WORKDIR']) {
writePath = process.env['NAPCAT_WORKDIR'];
} else if (os.platform() === 'darwin') {
writePath = path.join(os.homedir(), 'Library', 'Application Support', 'QQ', 'NapCat'); writePath = path.join(os.homedir(), 'Library', 'Application Support', 'QQ', 'NapCat');
} else { } else {
writePath = this.binaryPath; writePath = this.binaryPath;
} }
this.logsPath = path.join(writePath, 'logs'); this.logsPath = path.join(writePath, 'logs');
this.configPath = path.join(writePath, 'config'); this.configPath = path.join(writePath, 'config');
this.cachePath = path.join(writePath, 'cache'); this.cachePath = path.join(writePath, 'cache');

View File

@@ -1 +1 @@
export const napCatVersion = '4.8.90'; export const napCatVersion = '4.7.62';

View File

@@ -10,14 +10,11 @@ import {
GroupNotify, GroupNotify,
GroupInfoSource, GroupInfoSource,
ShutUpGroupMember, ShutUpGroupMember,
Peer,
ChatType,
} from '@/core'; } from '@/core';
import { isNumeric, solveAsyncProblem } from '@/common/helper'; import { isNumeric, solveAsyncProblem } from '@/common/helper';
import { LimitedHashTable } from '@/common/message-unique'; import { LimitedHashTable } from '@/common/message-unique';
import { NTEventWrapper } from '@/common/event'; import { NTEventWrapper } from '@/common/event';
import { CancelableTask, TaskExecutor } from '@/common/cancel-task'; import { CancelableTask, TaskExecutor } from '@/common/cancel-task';
import { createGroupDetailInfoV2Param, createGroupExtFilter, createGroupExtInfo } from '../data';
export class NTQQGroupApi { export class NTQQGroupApi {
context: InstanceContext; context: InstanceContext;
@@ -50,22 +47,6 @@ export class NTQQGroupApi {
this.initCache().then().catch(e => this.context.logger.logError(e)); this.initCache().then().catch(e => this.context.logger.logError(e));
} }
async createGrayTip(groupCode: string, tip: string) {
return this.context.session.getMsgService().addLocalJsonGrayTipMsg(
{
chatType: ChatType.KCHATTYPEGROUP,
peerUid: groupCode,
} as Peer,
{
busiId: 2201,
jsonStr: JSON.stringify({ "align": "center", "items": [{ "txt": tip, "type": "nor" }] }),
recentAbstract: tip,
isServer: false
},
true,
true
)
}
async initCache() { async initCache() {
for (const group of await this.getGroups(true)) { for (const group of await this.getGroups(true)) {
this.refreshGroupMemberCache(group.groupCode, false).then().catch(e => this.context.logger.logError(e)); this.refreshGroupMemberCache(group.groupCode, false).then().catch(e => this.context.logger.logError(e));
@@ -114,58 +95,6 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().setHeader(groupCode, filePath); return this.context.session.getGroupService().setHeader(groupCode, filePath);
} }
// 0 0 无需管理员审核
// 0 2 需要管理员审核
// 1 2 禁止Bot入群( 最好只传一个1 )
async setGroupRobotAddOption(groupCode: string, robotMemberSwitch?: number, robotMemberExamine?: number) {
let extInfo = createGroupExtInfo(groupCode);
let groupExtFilter = createGroupExtFilter();
if (robotMemberSwitch !== undefined) {
extInfo.extInfo.inviteRobotMemberSwitch = robotMemberSwitch;
groupExtFilter.inviteRobotMemberSwitch = 1;
}
if (robotMemberExamine !== undefined) {
extInfo.extInfo.inviteRobotMemberExamine = robotMemberExamine;
groupExtFilter.inviteRobotMemberExamine = 1;
}
return this.context.session.getGroupService().modifyGroupExtInfoV2(extInfo, groupExtFilter);
}
async setGroupAddOption(groupCode: string, option: {
addOption: number;
groupQuestion?: string;
groupAnswer?: string;
}) {
let param = createGroupDetailInfoV2Param(groupCode);
// 设置要修改的目标
param.filter.addOption = 1;
if (option.addOption == 4 || option.addOption == 5) {
// 4 问题进入答案 5 问题管理员批准
param.filter.groupQuestion = 1;
param.filter.groupAnswer = option.addOption == 4 ? 1 : 0;
param.modifyInfo.groupQuestion = option.groupQuestion || '';
param.modifyInfo.groupAnswer = option.addOption == 4 ? option.groupAnswer || '' : '';
}
param.modifyInfo.addOption = option.addOption;
return this.context.session.getGroupService().modifyGroupDetailInfoV2(param, 0);
}
async setGroupSearch(groupCode: string, option: {
noCodeFingerOpenFlag?: number;
noFingerOpenFlag?: number;
}) {
let param = createGroupDetailInfoV2Param(groupCode);
if (option.noCodeFingerOpenFlag) {
param.filter.noCodeFingerOpenFlag = 1;
param.modifyInfo.noCodeFingerOpenFlag = option.noCodeFingerOpenFlag;
}
if (option.noFingerOpenFlag) {
param.filter.noFingerOpenFlag = 1;
param.modifyInfo.noFingerOpenFlag = option.noFingerOpenFlag;
}
return this.context.session.getGroupService().modifyGroupDetailInfoV2(param, 0);
}
async getGroups(forced: boolean = false) { async getGroups(forced: boolean = false) {
const [, , groupList] = await this.core.eventWrapper.callNormalEventV2( const [, , groupList] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getGroupList', 'NodeIKernelGroupService/getGroupList',

View File

@@ -264,7 +264,7 @@ export class NTQQWebApi {
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) { async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com'); const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
let HonorInfo = { let HonorInfo = {
group_id: Number(groupCode), group_id: groupCode,
current_talkative: {}, current_talkative: {},
talkative_list: [], talkative_list: [],
performer_list: [], performer_list: [],

View File

@@ -1,245 +0,0 @@
import { GroupDetailInfoV2Param, GroupExtInfo, GroupExtFilter } from "../types";
export function createGroupDetailInfoV2Param(group_code: string): GroupDetailInfoV2Param {
return {
groupCode: group_code,
filter:
{
noCodeFingerOpenFlag: 0,
noFingerOpenFlag: 0,
groupName: 0,
classExt: 0,
classText: 0,
fingerMemo: 0,
richFingerMemo: 0,
tagRecord: 0,
groupGeoInfo:
{
ownerUid: 0,
setTime: 0,
cityId: 0,
longitude: 0,
latitude: 0,
geoContent: 0,
poiId: 0
},
groupExtAdminNum: 0,
flag: 0,
groupMemo: 0,
groupAioSkinUrl: 0,
groupBoardSkinUrl: 0,
groupCoverSkinUrl: 0,
groupGrade: 0,
activeMemberNum: 0,
certificationType: 0,
certificationText: 0,
groupNewGuideLines:
{
enabled: 0,
content: 0
},
groupFace: 0,
addOption: 0,
shutUpTime: 0,
groupTypeFlag: 0,
appPrivilegeFlag: 0,
appPrivilegeMask: 0,
groupExtOnly:
{
tribeId: 0,
moneyForAddGroup: 0
}, groupSecLevel: 0,
groupSecLevelInfo: 0,
subscriptionUin: 0,
subscriptionUid: "",
allowMemberInvite: 0,
groupQuestion: 0,
groupAnswer: 0,
groupFlagExt3: 0,
groupFlagExt3Mask: 0,
groupOpenAppid: 0,
rootId: 0,
msgLimitFrequency: 0,
hlGuildAppid: 0,
hlGuildSubType: 0,
hlGuildOrgId: 0,
groupFlagExt4: 0,
groupFlagExt4Mask: 0,
groupSchoolInfo: {
location: 0,
grade: 0,
school: 0
},
groupCardPrefix:
{
introduction: 0,
rptPrefix: 0
}, allianceId: 0,
groupFlagPro1: 0,
groupFlagPro1Mask: 0
},
modifyInfo: {
noCodeFingerOpenFlag: 0,
noFingerOpenFlag: 0,
groupName: "",
classExt: 0,
classText: "",
fingerMemo: "",
richFingerMemo: "",
tagRecord: [],
groupGeoInfo: {
ownerUid: "",
SetTime: 0,
CityId: 0,
Longitude: "",
Latitude: "",
GeoContent: "",
poiId: ""
},
groupExtAdminNum: 0,
flag: 0,
groupMemo: "",
groupAioSkinUrl: "",
groupBoardSkinUrl: "",
groupCoverSkinUrl: "",
groupGrade: 0,
activeMemberNum: 0,
certificationType: 0,
certificationText: "",
groupNewGuideLines: {
enabled: false,
content: ""
}, groupFace: 0,
addOption: 0,
shutUpTime: 0,
groupTypeFlag: 0,
appPrivilegeFlag: 0,
appPrivilegeMask: 0,
groupExtOnly: {
tribeId: 0,
moneyForAddGroup: 0
},
groupSecLevel: 0,
groupSecLevelInfo: 0,
subscriptionUin: "",
subscriptionUid: "",
allowMemberInvite: 0,
groupQuestion: "",
groupAnswer: "",
groupFlagExt3: 0,
groupFlagExt3Mask: 0,
groupOpenAppid: 0,
rootId: "",
msgLimitFrequency: 0,
hlGuildAppid: 0,
hlGuildSubType: 0,
hlGuildOrgId: 0,
groupFlagExt4: 0,
groupFlagExt4Mask: 0,
groupSchoolInfo: {
location: "",
grade: 0,
school: ""
},
groupCardPrefix:
{
introduction: "",
rptPrefix: []
},
allianceId: "",
groupFlagPro1: 0,
groupFlagPro1Mask: 0
}
}
}
export function createGroupExtInfo(group_code: string): GroupExtInfo {
return {
groupCode: group_code,
resultCode: 0,
extInfo: {
groupInfoExtSeq: 0,
reserve: 0,
luckyWordId: '',
lightCharNum: 0,
luckyWord: '',
starId: 0,
essentialMsgSwitch: 0,
todoSeq: 0,
blacklistExpireTime: 0,
isLimitGroupRtc: 0,
companyId: 0,
hasGroupCustomPortrait: 0,
bindGuildId: '',
groupOwnerId: {
memberUin: '',
memberUid: '',
memberQid: '',
},
essentialMsgPrivilege: 0,
msgEventSeq: '',
inviteRobotSwitch: 0,
gangUpId: '',
qqMusicMedalSwitch: 0,
showPlayTogetherSwitch: 0,
groupFlagPro1: '',
groupBindGuildIds: {
guildIds: [],
},
viewedMsgDisappearTime: '',
groupExtFlameData: {
switchState: 0,
state: 0,
dayNums: [],
version: 0,
updateTime: '',
isDisplayDayNum: false,
},
groupBindGuildSwitch: 0,
groupAioBindGuildId: '',
groupExcludeGuildIds: {
guildIds: [],
},
fullGroupExpansionSwitch: 0,
fullGroupExpansionSeq: '',
inviteRobotMemberSwitch: 0,
inviteRobotMemberExamine: 0,
groupSquareSwitch: 0,
}
}
}
export function createGroupExtFilter(): GroupExtFilter {
return {
groupInfoExtSeq: 0,
reserve: 0,
luckyWordId: 0,
lightCharNum: 0,
luckyWord: 0,
starId: 0,
essentialMsgSwitch: 0,
todoSeq: 0,
blacklistExpireTime: 0,
isLimitGroupRtc: 0,
companyId: 0,
hasGroupCustomPortrait: 0,
bindGuildId: 0,
groupOwnerId: 0,
essentialMsgPrivilege: 0,
msgEventSeq: 0,
inviteRobotSwitch: 0,
gangUpId: 0,
qqMusicMedalSwitch: 0,
showPlayTogetherSwitch: 0,
groupFlagPro1: 0,
groupBindGuildIds: 0,
viewedMsgDisappearTime: 0,
groupExtFlameData: 0,
groupBindGuildSwitch: 0,
groupAioBindGuildId: 0,
groupExcludeGuildIds: 0,
fullGroupExpansionSwitch: 0,
fullGroupExpansionSeq: 0,
inviteRobotMemberSwitch: 0,
inviteRobotMemberExamine: 0,
groupSquareSwitch: 0,
}
};

View File

@@ -1 +0,0 @@
export * from "./group";

View File

@@ -286,49 +286,5 @@
"9.9.19-34958": { "9.9.19-34958": {
"appid": 537290742, "appid": 537290742,
"qua": "V1_WIN_NQ_9.9.19_34958_GW_B" "qua": "V1_WIN_NQ_9.9.19_34958_GW_B"
},
"3.2.17-35184": {
"appid": 537291084,
"qua": "V1_LNX_NQ_3.2.17_35184_GW_B"
},
"9.9.19-35184": {
"appid": 537291048,
"qua": "V1_WIN_NQ_9.9.19_35184_GW_B"
},
"3.2.17-35341": {
"appid": 537291383,
"qua": "V1_LNX_NQ_3.2.17_35341_GW_B"
},
"9.9.19-35341": {
"appid": 537291347,
"qua": "V1_WIN_NQ_9.9.19_35341_GW_B"
},
"9.9.19-35469": {
"appid": 537291398,
"qua": "V1_WIN_NQ_9.9.19_35469_GW_B"
},
"3.2.18-35951": {
"appid": 537296013,
"qua": "V1_LNX_NQ_3.2.18_35951_GW_B"
},
"9.9.20-35951": {
"appid": 537295977,
"qua": "V1_WIN_NQ_9.9.20_35951_GW_B"
},
"3.2.18-36580": {
"appid": 537298509,
"qua": "V1_LNX_NQ_3.2.18_36580_GW_B"
},
"9.9.20-36580": {
"appid": 537298473,
"qua": "V1_WIN_NQ_9.9.20_36580_GW_B"
},
"9.9.20-37012": {
"appid": 537304071,
"qua": "V1_WIN_NQ_9.9.20_37012_GW_B"
},
"3.2.18-37012": {
"appid": 537304107,
"qua": "V1_LNX_NQ_3.2.18_37012_GW_B"
} }
} }

View File

@@ -366,65 +366,5 @@
"9.9.19-34958-x64": { "9.9.19-34958-x64": {
"send": "3BDD8D0", "send": "3BDD8D0",
"recv": "3BE20D0" "recv": "3BE20D0"
},
"3.2.17-35184-x64": {
"send": "AE0DDE0",
"recv": "AE11800"
},
"3.2.17-35184-arm64": {
"send": "7776028",
"recv": "7779958"
},
"9.9.19-35184-x64": {
"send": "3BE5A10",
"recv": "3BEA210"
},
"9.9.19-35341-x64": {
"send": "3BF1D50",
"recv": "3BF6550"
},
"9.9.19-35469-x64": {
"send": "3BF1D50",
"recv": "3BF6550"
},
"3.2.17-35341-x64": {
"send": "AE2F700",
"recv": "AE33120"
},
"3.2.17-35341-arm64": {
"send": "778D840",
"recv": "7791170"
},
"9.9.20-35951-x64": {
"send": "3034BAC",
"recv": "3038354"
},
"3.2.18-35951-x64": {
"send": "AFBBB00",
"recv": "AFBF520"
},
"9.9.20-36580-x64": {
"send": "30824B8",
"recv": "3085C5C"
},
"3.2.18-36580-x64": {
"send": "B0853E0",
"recv": "B088E60"
},
"3.2.18-36580-arm64": {
"send": "793DAC8",
"recv": "7941458"
},
"3.2.18-37012-x64": {
"send": "B20F960",
"recv": "B2133E0"
},
"3.2.18-37012-arm64": {
"send": "7A19E00",
"recv": "7A1D790"
},
"9.9.20-37012-x64": {
"send": "30CC958",
"recv": "30D00FC"
} }
} }

View File

@@ -40,8 +40,7 @@ export class NativePacketClient extends IPacketClient {
async init(_pid: number, recv: string, send: string): Promise<void> { async init(_pid: number, recv: string, send: string): Promise<void> {
const platform = process.platform + '.' + process.arch; const platform = process.platform + '.' + process.arch;
const isNewQQ = this.napcore.basicInfo.requireMinNTQQBuild("36580"); const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node');
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + (isNewQQ ? '.new' : '') + '.node');
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY); process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
this.MoeHooExport.exports.InitHook?.(send, recv, (type: number, _uin: string, cmd: string, seq: number, hex_data: string) => { this.MoeHooExport.exports.InitHook?.(send, recv, (type: number, _uin: string, cmd: string, seq: number, hex_data: string) => {

View File

@@ -1,7 +1,6 @@
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
export interface NapCoreCompatBasicInfo { export interface NapCoreCompatBasicInfo {
readonly requireMinNTQQBuild: (buildVer: string) => boolean;
readonly uin: number; readonly uin: number;
readonly uid: string; readonly uid: string;
readonly uin2uid: (uin: number) => Promise<string>; readonly uin2uid: (uin: number) => Promise<string>;
@@ -22,7 +21,6 @@ export class NapCoreContext {
get basicInfo() { get basicInfo() {
return { return {
requireMinNTQQBuild: (buildVer: string) => this.core.context.basicInfoWrapper.requireMinNTQQBuild(buildVer),
uin: +this.core.selfInfo.uin, uin: +this.core.selfInfo.uin,
uid: this.core.selfInfo.uid, uid: this.core.selfInfo.uid,
uin2uid: (uin: number) => this.core.apis.UserApi.getUidByUinV2(String(uin)).then(res => res ?? ''), uin2uid: (uin: number) => this.core.apis.UserApi.getUidByUinV2(String(uin)).then(res => res ?? ''),

View File

@@ -30,8 +30,13 @@ export class PacketOperationContext {
return await this.context.client.sendOidbPacket(pkt, rsp); return await this.context.client.sendOidbPacket(pkt, rsp);
} }
async SendPoke(is_group: boolean, peer: number, target?: number) { async GroupPoke(groupUin: number, uin: number) {
const req = trans.SendPoke.build(is_group, peer, target ?? peer); const req = trans.SendPoke.build(uin, groupUin);
await this.context.client.sendOidbPacket(req);
}
async FriendPoke(uin: number) {
const req = trans.SendPoke.build(uin);
await this.context.client.sendOidbPacket(req); await this.context.client.sendOidbPacket(req);
} }

View File

@@ -8,13 +8,13 @@ class SendPoke extends PacketTransformer<typeof proto.OidbSvcTrpcTcpBase> {
super(); super();
} }
build(is_group: boolean, peer: number, target: number): OidbPacket { build(peer: number, group?: number): OidbPacket {
const payload = { const data = new NapProtoMsg(proto.OidbSvcTrpcTcp0XED3_1).encode({
uin: target, uin: peer,
ext: 0, groupUin: group,
...(is_group ? { groupUin: peer } : { friendUin: peer }) friendUin: group ?? peer,
}; ext: 0
const data = new NapProtoMsg(proto.OidbSvcTrpcTcp0XED3_1).encode(payload); });
return OidbBase.build(0xED3, 1, data); return OidbBase.build(0xED3, 1, data);
} }

View File

@@ -7,7 +7,7 @@ class OidbBase extends PacketTransformer<typeof proto.OidbSvcTrpcTcpBase> {
super(); super();
} }
build(cmd: number, subCmd: number, body: Uint8Array, isUid: boolean = true, _isLafter: boolean = false): OidbPacket { build(cmd: number, subCmd: number, body: Uint8Array, isUid: boolean = true, isLafter: boolean = false): OidbPacket {
const data = new NapProtoMsg(proto.OidbSvcTrpcTcpBase).encode({ const data = new NapProtoMsg(proto.OidbSvcTrpcTcpBase).encode({
command: cmd, command: cmd,
subCommand: subCmd, subCommand: subCmd,

View File

@@ -8,22 +8,10 @@ import {
GroupNotifyMsgType, GroupNotifyMsgType,
NTGroupRequestOperateTypes, NTGroupRequestOperateTypes,
KickMemberV2Req, KickMemberV2Req,
GroupDetailInfoV2Param,
GroupExtInfo,
GroupExtFilter,
} from '@/core/types'; } from '@/core/types';
import { GeneralCallResult } from '@/core/services/common'; import { GeneralCallResult } from '@/core/services/common';
export interface NodeIKernelGroupService { export interface NodeIKernelGroupService {
modifyGroupExtInfoV2(groupExtInfo: GroupExtInfo, groupExtFilter: GroupExtFilter): Promise<GeneralCallResult &
{
result: {
groupCode: string,
result: number
}
}>;
// ---> // --->
// 待启用 For Next Version 3.2.0 // 待启用 For Next Version 3.2.0
// isTroopMember ? 0 : 111 // isTroopMember ? 0 : 111
@@ -181,9 +169,6 @@ export interface NodeIKernelGroupService {
modifyGroupDetailInfo(groupCode: string, arg: unknown): void; modifyGroupDetailInfo(groupCode: string, arg: unknown): void;
// 第二个参数在大多数情况为0 设置群成员权限 例如上传群文件权限和群成员付费/加入邀请加入时为8
modifyGroupDetailInfoV2(param: GroupDetailInfoV2Param, arg: number): Promise<GeneralCallResult>;
setGroupMsgMask(groupCode: string, arg: unknown): void; setGroupMsgMask(groupCode: string, arg: unknown): void;
changeGroupShieldSettingTemp(groupCode: string, arg: unknown): void; changeGroupShieldSettingTemp(groupCode: string, arg: unknown): void;

View File

@@ -58,7 +58,6 @@ export interface GrayTipRovokeElement {
operatorUid: string; operatorUid: string;
operatorNick: string; operatorNick: string;
operatorRemark: string; operatorRemark: string;
isSelfOperate: boolean; // 是否是自己撤回的
operatorMemRemark?: string; operatorMemRemark?: string;
wording: string; // 自定义的撤回提示语 wording: string; // 自定义的撤回提示语
} }

View File

@@ -1,97 +1,4 @@
import { QQLevel, NTSex } from './user'; import { QQLevel, NTSex } from './user';
export interface GroupExtInfo {
groupCode: string;
resultCode: number;
extInfo: EXTInfo;
}
export interface GroupExtFilter {
groupInfoExtSeq: number;
reserve: number;
luckyWordId: number;
lightCharNum: number;
luckyWord: number;
starId: number;
essentialMsgSwitch: number;
todoSeq: number;
blacklistExpireTime: number;
isLimitGroupRtc: number;
companyId: number;
hasGroupCustomPortrait: number;
bindGuildId: number;
groupOwnerId: number;
essentialMsgPrivilege: number;
msgEventSeq: number;
inviteRobotSwitch: number;
gangUpId: number;
qqMusicMedalSwitch: number;
showPlayTogetherSwitch: number;
groupFlagPro1: number;
groupBindGuildIds: number;
viewedMsgDisappearTime: number;
groupExtFlameData: number;
groupBindGuildSwitch: number;
groupAioBindGuildId: number;
groupExcludeGuildIds: number;
fullGroupExpansionSwitch: number;
fullGroupExpansionSeq: number;
inviteRobotMemberSwitch: number;
inviteRobotMemberExamine: number;
groupSquareSwitch: number;
};
export interface EXTInfo {
groupInfoExtSeq: number;
reserve: number;
luckyWordId: string;
lightCharNum: number;
luckyWord: string;
starId: number;
essentialMsgSwitch: number;
todoSeq: number;
blacklistExpireTime: number;
isLimitGroupRtc: number;
companyId: number;
hasGroupCustomPortrait: number;
bindGuildId: string;
groupOwnerId: GroupOwnerID;
essentialMsgPrivilege: number;
msgEventSeq: string;
inviteRobotSwitch: number;
gangUpId: string;
qqMusicMedalSwitch: number;
showPlayTogetherSwitch: number;
groupFlagPro1: string;
groupBindGuildIds: GroupGuildIDS;
viewedMsgDisappearTime: string;
groupExtFlameData: GroupEXTFlameData;
groupBindGuildSwitch: number;
groupAioBindGuildId: string;
groupExcludeGuildIds: GroupGuildIDS;
fullGroupExpansionSwitch: number;
fullGroupExpansionSeq: string;
inviteRobotMemberSwitch: number;
inviteRobotMemberExamine: number;
groupSquareSwitch: number;
}
export interface GroupGuildIDS {
guildIds: any[];
}
export interface GroupEXTFlameData {
switchState: number;
state: number;
dayNums: any[];
version: number;
updateTime: string;
isDisplayDayNum: boolean;
}
export interface GroupOwnerID {
memberUin: string;
memberUid: string;
memberQid: string;
}
export interface KickMemberInfo { export interface KickMemberInfo {
optFlag: number; optFlag: number;
@@ -100,185 +7,6 @@ export interface KickMemberInfo {
optBytesMsg: string; optBytesMsg: string;
} }
export interface GroupDetailInfoV2Param {
groupCode: string;
filter: Filter;
modifyInfo: ModifyInfo;
}
export interface Filter {
noCodeFingerOpenFlag: number;
noFingerOpenFlag: number;
groupName: number;
classExt: number;
classText: number;
fingerMemo: number;
richFingerMemo: number;
tagRecord: number;
groupGeoInfo: FilterGroupGeoInfo;
groupExtAdminNum: number;
flag: number;
groupMemo: number;
groupAioSkinUrl: number;
groupBoardSkinUrl: number;
groupCoverSkinUrl: number;
groupGrade: number;
activeMemberNum: number;
certificationType: number;
certificationText: number;
groupNewGuideLines: FilterGroupNewGuideLines;
groupFace: number;
addOption: number;
shutUpTime: number;
groupTypeFlag: number;
appPrivilegeFlag: number;
appPrivilegeMask: number;
groupExtOnly: GroupEXTOnly;
groupSecLevel: number;
groupSecLevelInfo: number;
subscriptionUin: number;
subscriptionUid: string;
allowMemberInvite: number;
groupQuestion: number;
groupAnswer: number;
groupFlagExt3: number;
groupFlagExt3Mask: number;
groupOpenAppid: number;
rootId: number;
msgLimitFrequency: number;
hlGuildAppid: number;
hlGuildSubType: number;
hlGuildOrgId: number;
groupFlagExt4: number;
groupFlagExt4Mask: number;
groupSchoolInfo: FilterGroupSchoolInfo;
groupCardPrefix: FilterGroupCardPrefix;
allianceId: number;
groupFlagPro1: number;
groupFlagPro1Mask: number;
}
export interface FilterGroupCardPrefix {
introduction: number;
rptPrefix: number;
}
export interface GroupEXTOnly {
tribeId: number;
moneyForAddGroup: number;
}
export interface FilterGroupGeoInfo {
ownerUid: number;
setTime: number;
cityId: number;
longitude: number;
latitude: number;
geoContent: number;
poiId: number;
}
export interface FilterGroupNewGuideLines {
enabled: number;
content: number;
}
export interface FilterGroupSchoolInfo {
location: number;
grade: number;
school: number;
}
export interface ModifyInfo {
noCodeFingerOpenFlag: number;
noFingerOpenFlag: number;
groupName: string;
classExt: number;
classText: string;
fingerMemo: string;
richFingerMemo: string;
tagRecord: any[];
groupGeoInfo: ModifyInfoGroupGeoInfo;
groupExtAdminNum: number;
flag: number;
groupMemo: string;
groupAioSkinUrl: string;
groupBoardSkinUrl: string;
groupCoverSkinUrl: string;
groupGrade: number;
activeMemberNum: number;
certificationType: number;
certificationText: string;
groupNewGuideLines: ModifyInfoGroupNewGuideLines;
groupFace: number;
addOption: number;// 0 空设置 1 任何人都可以进入 2 需要管理员批准 3 不允许任何人入群 4 问题进入答案 5 问题管理员批准
shutUpTime: number;
groupTypeFlag: number;
appPrivilegeFlag: number;
// 需要管理员审核
// 0000 0000 0000 0000 0000 0000 0000
// 无需审核入群
// 0000 0001 0000 0000 0000 0000 0000
// 成员数100内无审核
// 0100 0000 0000 0000 0000 0000 0000
// 禁用 群成员邀请好友
// 0100 0000 0000 0000 0000 0000 0000
appPrivilegeMask: number;
// 0110 0001 0000 0000 0000 0000 0000
// 101711872
groupExtOnly: GroupEXTOnly;
groupSecLevel: number;
groupSecLevelInfo: number;
subscriptionUin: string;
subscriptionUid: string;
allowMemberInvite: number;
groupQuestion: string;
groupAnswer: string;
groupFlagExt3: number;
groupFlagExt3Mask: number;
groupOpenAppid: number;
rootId: string;
msgLimitFrequency: number;
hlGuildAppid: number;
hlGuildSubType: number;
hlGuildOrgId: number;
groupFlagExt4: number;
groupFlagExt4Mask: number;
groupSchoolInfo: ModifyInfoGroupSchoolInfo;
groupCardPrefix: ModifyInfoGroupCardPrefix;
allianceId: string;
groupFlagPro1: number;
groupFlagPro1Mask: number;
}
export interface ModifyInfoGroupCardPrefix {
introduction: string;
rptPrefix: any[];
}
export interface ModifyInfoGroupGeoInfo {
ownerUid: string;
SetTime: number;
CityId: number;
Longitude: string;
Latitude: string;
GeoContent: string;
poiId: string;
}
export interface ModifyInfoGroupNewGuideLines {
enabled: boolean;
content: string;
}
export interface ModifyInfoGroupSchoolInfo {
location: string;
grade: number;
school: string;
}
// 获取群详细信息的来源类型 // 获取群详细信息的来源类型
export enum GroupInfoSource { export enum GroupInfoSource {
KUNSPECIFIED, KUNSPECIFIED,

View File

@@ -48,12 +48,6 @@ export async function NCoreInitFramework(
}); });
} }
//直到登录成功后,执行下一步 //直到登录成功后,执行下一步
// const selfInfo = {
// uid: 'u_FUSS0_x06S_9Tf4na_WpUg',
// uin: '3684714082',
// nick: '',
// online: true
// }
const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => { const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
const loginListener = new NodeIKernelLoginListener(); const loginListener = new NodeIKernelLoginListener();
loginListener.onQRCodeLoginSucceed = async (loginResult) => { loginListener.onQRCodeLoginSucceed = async (loginResult) => {

View File

@@ -1,26 +0,0 @@
const fs = require('fs');
const path = require('path');
async function initializeNapCat(session, loginService, registerCallback) {
//const logFile = path.join(currentPath, 'napcat.log');
console.log('[NapCat] [Info] 开始初始化NapCat');
//fs.writeFileSync(logFile, '', { flag: 'w' });
//fs.writeFileSync(logFile, '[NapCat] [Info] NapCat 初始化成功\n', { flag: 'a' });
try {
const currentPath = path.dirname(__filename);
const { NCoreInitFramework } = await import('file://' + path.join(currentPath, './napcat.mjs'));
await NCoreInitFramework(session, loginService, (callback) => { registerCallback(callback) });
} catch (error) {
console.log('[NapCat] [Error] 初始化NapCat', error);
//fs.writeFileSync(logFile, `[NapCat] [Error] 初始化NapCat失败: ${error.message}\n`, { flag: 'a' });
}
}
module.exports = {
initializeNapCat: initializeNapCat
};

372
src/on-finished/index.ts Normal file
View File

@@ -0,0 +1,372 @@
/*!
* on-finished
* Copyright(c) 2013 Jonathan Ong
* Copyright(c) 2014 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* 定义消息对象接口
*/
interface Message {
socket?: Socket;
finished?: boolean;
complete?: boolean;
readable?: boolean;
upgrade?: boolean;
__onFinished?: Listener;
on?: (event: string, callback: (socket: Socket) => void) => void;
removeListener?: (event: string, callback: Function) => void;
assignSocket?: (socket: Socket) => void;
}
/**
* 定义Socket接口
*/
interface Socket {
writable: boolean;
readable: boolean;
on?: (event: string, callback: Function) => void;
removeListener?: (event: string, callback: Function) => void;
}
/**
* 事件处理器类型
*/
type EventHandler = (err?: Error) => void;
/**
* 事件元组: [对象, ...事件名称]
*/
type EventTuple = [unknown, ...string[]];
/**
* 取消函数类型
*/
interface CancelableListener {
cancel: () => void;
}
/**
* 监听器函数类型
*/
type ListenerCallback = (err: Error | null, msg: Message) => void;
/**
* 修改监听器接口,使其参数类型更通用
*/
interface Listener {
(err: Error | null | undefined): void;
queue: ListenerCallback[] | null;
}
/**
* 模块导出
* @public
*/
export { onFinished as default, isFinished };
/**
* 模块依赖
* @private
*/
const asyncHooks = tryRequireAsyncHooks();
/**
* 变量
* @private
*/
const defer = function(fn: ListenerCallback, err: Error | null, msg: Message): void {
setImmediate(fn, err, msg);
};
/**
* 实现 ee-first 功能:监听多个事件,当第一个事件触发时执行回调并移除所有监听器
* @param {Array<EventTuple>} stuff - 事件元组数组
* @param {EventHandler} done - 完成时的回调
* @returns {CancelableListener} 可取消的监听器对象
* @private
*/
function first(stuff: EventTuple[], done: EventHandler): CancelableListener {
if (!Array.isArray(stuff)) {
throw new TypeError('参数 "stuff" 必须是数组');
}
if (typeof done !== 'function') {
throw new TypeError('参数 "done" 必须是函数');
}
const cleanups: Function[] = [];
let finished = false;
// 处理所有传入的对象和事件
for (const tuple of stuff) {
if (!Array.isArray(tuple) || tuple.length < 2) {
throw new TypeError('每个事件元组必须是长度大于等于2的数组');
}
const obj = tuple[0];
if (!obj || typeof obj !== 'object') {
throw new TypeError('监听目标必须是对象');
}
// 使用类型断言确保obj有on和removeListener方法
const target = obj as {
on: (event: string, callback: Function) => void;
removeListener: (event: string, callback: Function) => void;
};
if (typeof target.on !== 'function' || typeof target.removeListener !== 'function') {
throw new TypeError('监听目标必须支持 on/removeListener 方法');
}
const events = tuple.slice(1);
// 为每个事件创建一个监听器
for (const event of events) {
if (typeof event !== 'string') {
throw new TypeError('事件名称必须是字符串');
}
const listener = createEventListener(target, event, cleanup);
cleanups.push(listener.cleanup);
target.on(event, listener.callback);
}
}
// 创建可取消的监听器对象
return {
cancel: cleanup
};
// 清理函数
function cleanup(): void {
if (finished) return;
finished = true;
// 执行所有清理函数
for (const cleanup of cleanups) {
try {
cleanup();
} catch (err) {
console.error('清理事件监听器时出错:', err);
}
}
}
// 创建单个事件监听器
function createEventListener(
obj: { on: Function; removeListener: Function },
event: string,
cleanup: Function
): {
callback: (arg?: unknown) => void;
cleanup: () => void;
} {
const callback = (arg?: unknown): void => {
cleanup();
done(arg instanceof Error ? arg : undefined);
};
return {
callback,
cleanup: () => {
obj.removeListener(event, callback);
}
};
}
}
/**
* 当响应结束时调用回调,用于资源清理
*
* @param {Message} msg
* @param {ListenerCallback} listener
* @return {Message}
* @public
*/
function onFinished(msg: Message, listener: ListenerCallback): Message {
if (!msg || typeof msg !== 'object') {
throw new TypeError('参数 "msg" 必须是对象');
}
if (typeof listener !== 'function') {
throw new TypeError('参数 "listener" 必须是函数');
}
if (isFinished(msg) !== false) {
defer(listener, null, msg);
return msg;
}
// 将监听器附加到消息
attachListener(msg, wrap(listener));
return msg;
}
/**
* 确定消息是否已完成
*
* @param {Message} msg
* @return {boolean | undefined}
* @public
*/
function isFinished(msg: Message): boolean | undefined {
if (!msg || typeof msg !== 'object') {
throw new TypeError('参数 "msg" 必须是对象');
}
const socket = msg.socket;
if (typeof msg.finished === 'boolean') {
// OutgoingMessage
return Boolean(msg.finished || (socket && !socket.writable));
}
if (typeof msg.complete === 'boolean') {
// IncomingMessage
return Boolean(msg.upgrade || !socket || !socket.readable || (msg.complete && !msg.readable));
}
// 不确定状态
return undefined;
}
/**
* 将完成监听器附加到消息
*
* @param {Message} msg
* @param {Function} callback
* @private
*/
function attachFinishedListener(msg: Message, callback: (error?: Error) => void): void {
let eeMsg: CancelableListener;
let eeSocket: CancelableListener;
let finished = false;
function onFinish(error?: Error): void {
if (!eeMsg || !eeSocket) {
return; // 防御性检查
}
eeMsg.cancel();
eeSocket.cancel();
finished = true;
callback(error);
}
// 在第一个消息事件上完成
eeMsg = eeSocket = first([[msg, 'end', 'finish']], onFinish);
function onSocket(socket: Socket): void {
// 移除监听器
if (msg.removeListener && typeof msg.removeListener === 'function') {
msg.removeListener('socket', onSocket);
}
if (finished) return;
if (eeMsg !== eeSocket) return;
if (!socket || typeof socket !== 'object') {
return; // 防御性检查
}
// 在第一个socket事件上完成
eeSocket = first([[socket, 'error', 'close']], onFinish);
}
if (msg.socket) {
// socket已分配
onSocket(msg.socket);
return;
}
// 等待socket分配
if (msg.on && typeof msg.on === 'function') {
msg.on('socket', onSocket);
}
}
/**
* 将监听器附加到消息
*
* @param {Message} msg
* @param {ListenerCallback} listener
* @private
*/
function attachListener(msg: Message, listener: ListenerCallback): void {
let attached = msg.__onFinished;
// 创建私有单一监听器和队列
if (!attached || !attached.queue) {
attached = msg.__onFinished = createListener(msg);
attachFinishedListener(msg, attached);
}
if (attached.queue) {
attached.queue.push(listener);
}
}
/**
* 在消息上创建监听器
*
* @param {Message} msg
* @return {Listener}
* @private
*/
function createListener(msg: Message): Listener {
const listener: Listener = function listener(err: Error | null | undefined): void {
if (msg.__onFinished === listener) msg.__onFinished = undefined;
if (!listener.queue) return;
const queue = listener.queue;
listener.queue = null;
for (let i = 0; i < queue.length; i++) {
queue[i]?.(err || null, msg);
}
};
listener.queue = [];
return listener;
}
/**
* 尝试引入async_hooks
* @private
*/
function tryRequireAsyncHooks(): { AsyncResource?: typeof import('async_hooks').AsyncResource } {
try {
return require('async_hooks');
} catch (e) {
return {};
}
}
/**
* 如果可能用async resource包装函数
* @private
*/
function wrap(fn: ListenerCallback): ListenerCallback {
if (typeof fn !== 'function') {
throw new TypeError('参数必须是函数');
}
// Node.js 16+ 总是有 AsyncResource
if (!asyncHooks.AsyncResource) {
return fn;
}
// 创建匿名资源
const res = new asyncHooks.AsyncResource(fn.name || 'bound-anonymous-fn');
// 返回绑定函数
return (res.runInAsyncScope.bind(res, fn, null) as ListenerCallback);
}

View File

@@ -1,28 +0,0 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
add_type: Type.Number(),
group_question: Type.Optional(Type.String()),
group_answer: Type.Optional(Type.String()),
});
type Payload = Static<typeof SchemaData>;
export default class SetGroupAddOption extends OneBotAction<Payload, null> {
override actionName = ActionName.SetGroupAddOption;
override payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> {
let ret = await this.core.apis.GroupApi.setGroupAddOption(payload.group_id, {
addOption: payload.add_type,
groupQuestion: payload.group_question,
groupAnswer: payload.group_answer,
});
if (ret.result != 0) {
throw new Error(`设置群添加选项失败, ${ret.result}:${ret.errMsg}`);
}
return null;
}
}

View File

@@ -1,23 +0,0 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
user_id: Type.Array(Type.String()),
reject_add_request: Type.Optional(Type.Union([Type.Boolean(), Type.String()])),
});
type Payload = Static<typeof SchemaData>;
export default class SetGroupKickMembers extends OneBotAction<Payload, null> {
override actionName = ActionName.SetGroupKickMembers;
override payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> {
const rejectReq = payload.reject_add_request?.toString() == 'true';
const uids: string[] = await Promise.all(payload.user_id.map(async uin => await this.core.apis.UserApi.getUidByUinV2(uin)));
await this.core.apis.GroupApi.kickMember(payload.group_id.toString(), uids.filter(uid => !!uid), rejectReq);
return null;
}
}

View File

@@ -1,27 +0,0 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
robot_member_switch: Type.Optional(Type.Number()),
robot_member_examine: Type.Optional(Type.Number()),
});
type Payload = Static<typeof SchemaData>;
export default class SetGroupRobotAddOption extends OneBotAction<Payload, null> {
override actionName = ActionName.SetGroupRobotAddOption;
override payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> {
let ret = await this.core.apis.GroupApi.setGroupRobotAddOption(
payload.group_id,
payload.robot_member_switch,
payload.robot_member_examine,
);
if (ret.result != 0) {
throw new Error(`设置群机器人添加选项失败, ${ret.result}:${ret.errMsg}`);
}
return null;
}
}

View File

@@ -1,26 +0,0 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
no_code_finger_open: Type.Optional(Type.Number()),
no_finger_open: Type.Optional(Type.Number()),
});
type Payload = Static<typeof SchemaData>;
export default class SetGroupSearch extends OneBotAction<Payload, null> {
override actionName = ActionName.SetGroupSearch;
override payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> {
let ret = await this.core.apis.GroupApi.setGroupSearch(payload.group_id, {
noCodeFingerOpenFlag: payload.no_code_finger_open,
noFingerOpenFlag: payload.no_finger_open,
});
if (ret.result != 0) {
throw new Error(`设置群搜索失败, ${ret.result}:${ret.errMsg}`);
}
return null;
}
}

View File

@@ -4,10 +4,7 @@ import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]), group_id: Type.Union([Type.Number(), Type.String()]),
// 兼容gocq 与name二选一 folder_name: Type.String(),
folder_name: Type.Optional(Type.String()),
// 兼容gocq 与folder_name二选一
name: Type.Optional(Type.String()),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
@@ -19,7 +16,6 @@ export class CreateGroupFileFolder extends OneBotAction<Payload, ResponseType>
override actionName = ActionName.GoCQHTTP_CreateGroupFileFolder; override actionName = ActionName.GoCQHTTP_CreateGroupFileFolder;
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
const folderName = payload.folder_name || payload.name; return (await this.core.apis.GroupApi.creatGroupFileFolder(payload.group_id.toString(), payload.folder_name)).resultWithGroupItem;
return (await this.core.apis.GroupApi.creatGroupFileFolder(payload.group_id.toString(), folderName!)).resultWithGroupItem;
} }
} }

View File

@@ -1,4 +1,4 @@
import { ContextMode, normalize, ReturnDataType, SendMsgBase } from '@/onebot/action/msg/SendMsg'; import { normalize, SendMsgBase } from '@/onebot/action/msg/SendMsg';
import { OB11PostSendMsg } from '@/onebot/types'; import { OB11PostSendMsg } from '@/onebot/types';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
@@ -19,14 +19,8 @@ export class GoCQHTTPSendForwardMsg extends GoCQHTTPSendForwardMsgBase {
} }
export class GoCQHTTPSendPrivateForwardMsg extends GoCQHTTPSendForwardMsgBase { export class GoCQHTTPSendPrivateForwardMsg extends GoCQHTTPSendForwardMsgBase {
override actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg; override actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg;
override async _handle(payload: OB11PostSendMsg): Promise<ReturnDataType> {
return this.base_handle(payload, ContextMode.Private);
}
} }
export class GoCQHTTPSendGroupForwardMsg extends GoCQHTTPSendForwardMsgBase { export class GoCQHTTPSendGroupForwardMsg extends GoCQHTTPSendForwardMsgBase {
override actionName = ActionName.GoCQHTTP_SendGroupForwardMsg; override actionName = ActionName.GoCQHTTP_SendGroupForwardMsg;
override async _handle(payload: OB11PostSendMsg): Promise<ReturnDataType> {
return this.base_handle(payload, ContextMode.Group);
}
} }

View File

@@ -1,27 +0,0 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]),
});
type Payload = Static<typeof SchemaData>;
export class GetGroupDetailInfo extends OneBotAction<Payload, unknown> {
override actionName = ActionName.GetGroupDetailInfo;
override payloadSchema = SchemaData;
async _handle(payload: Payload) {
const data = await this.core.apis.GroupApi.fetchGroupDetail(payload.group_id.toString());
return {
...data,
group_all_shut: data.shutUpAllTimestamp > 0 ? -1 : 0,
group_remark: '',
group_id: +payload.group_id,
group_name: data.groupName,
member_count: data.memberNum,
max_member_count: data.maxMemberNum,
};
}
}

View File

@@ -4,7 +4,6 @@ import { ActionName } from '@/onebot/action/router';
import { Notify } from '@/onebot/types'; import { Notify } from '@/onebot/types';
interface RetData { interface RetData {
invited_requests: Notify[];
InvitedRequest: Notify[]; InvitedRequest: Notify[];
join_requests: Notify[]; join_requests: Notify[];
} }
@@ -14,7 +13,7 @@ export class GetGroupIgnoredNotifies extends OneBotAction<void, RetData> {
async _handle(): Promise<RetData> { async _handle(): Promise<RetData> {
const SingleScreenNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(false, 50); const SingleScreenNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(false, 50);
const retData: RetData = { invited_requests: [], InvitedRequest: [], join_requests: [] }; const retData: RetData = { InvitedRequest: [], join_requests: [] };
const notifyPromises = SingleScreenNotifies.map(async (SSNotify) => { const notifyPromises = SingleScreenNotifies.map(async (SSNotify) => {
const invitorUin = SSNotify.user1?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1.uid) : 0; const invitorUin = SSNotify.user1?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1.uid) : 0;
@@ -39,7 +38,7 @@ export class GetGroupIgnoredNotifies extends OneBotAction<void, RetData> {
}); });
await Promise.all(notifyPromises); await Promise.all(notifyPromises);
retData.invited_requests = retData.InvitedRequest;
return retData; return retData;
} }
} }

View File

@@ -8,16 +8,10 @@ interface GroupNotice {
notice_id: string; notice_id: string;
message: { message: {
text: string text: string
// 保持一段时间兼容性 防止以往版本出现问题 后续版本可考虑移除
image: Array<{ image: Array<{
height: string height: string
width: string width: string
id: string id: string
}>,
images: Array<{
height: string
width: string
id: string
}> }>
}; };
} }
@@ -46,18 +40,15 @@ export class GetGroupNotice extends OneBotAction<Payload, GroupNotice[]> {
continue; continue;
} }
const retApiNotice: WebApiGroupNoticeFeed = ret.feeds[key]; const retApiNotice: WebApiGroupNoticeFeed = ret.feeds[key];
const image = retApiNotice.msg.pics?.map((pic) => {
return { id: pic.id, height: pic.h, width: pic.w };
}) || [];
const retNotice: GroupNotice = { const retNotice: GroupNotice = {
notice_id: retApiNotice.fid, notice_id: retApiNotice.fid,
sender_id: retApiNotice.u, sender_id: retApiNotice.u,
publish_time: retApiNotice.pubt, publish_time: retApiNotice.pubt,
message: { message: {
text: retApiNotice.msg.text, text: retApiNotice.msg.text,
image, image: retApiNotice.msg.pics?.map((pic) => {
images: image, return { id: pic.id, height: pic.h, width: pic.w };
}) || [],
}, },
}; };
retNotices.push(retNotice); retNotices.push(retNotice);

View File

@@ -0,0 +1,19 @@
import { ActionName } from '@/onebot/action/router';
import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]),
user_id: Type.Union([Type.Number(), Type.String()]),
});
type Payload = Static<typeof SchemaData>;
export class GroupPoke extends GetPacketStatusDepends<Payload, void> {
override actionName = ActionName.GroupPoke;
override payloadSchema = SchemaData;
async _handle(payload: Payload) {
await this.core.apis.PacketApi.pkt.operation.GroupPoke(+payload.group_id, +payload.user_id);
}
}

View File

@@ -1,19 +1,17 @@
import { ContextMode, ReturnDataType, SendMsgBase } from '@/onebot/action/msg/SendMsg'; import { ContextMode, SendMsgBase } from '@/onebot/action/msg/SendMsg';
import { ActionName, BaseCheckResult } from '@/onebot/action/router'; import { ActionName, BaseCheckResult } from '@/onebot/action/router';
import { OB11PostSendMsg } from '@/onebot/types'; import { OB11PostSendMsg } from '@/onebot/types';
// 未检测参数 // 未检测参数
class SendGroupMsg extends SendMsgBase { class SendGroupMsg extends SendMsgBase {
override actionName = ActionName.SendGroupMsg; override actionName = ActionName.SendGroupMsg;
override contextMode: ContextMode = ContextMode.Group;
protected override async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> { protected override async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
delete payload.user_id; delete payload.user_id;
payload.message_type = 'group'; payload.message_type = 'group';
return super.check(payload); return super.check(payload);
} }
override async _handle(payload: OB11PostSendMsg): Promise<ReturnDataType> {
return this.base_handle(payload, ContextMode.Group);
}
} }
export default SendGroupMsg; export default SendGroupMsg;

View File

@@ -78,6 +78,7 @@ import { GetGroupFileSystemInfo } from '@/onebot/action/go-cqhttp/GetGroupFileSy
import { GetGroupRootFiles } from '@/onebot/action/go-cqhttp/GetGroupRootFiles'; import { GetGroupRootFiles } from '@/onebot/action/go-cqhttp/GetGroupRootFiles';
import { GetGroupFilesByFolder } from '@/onebot/action/go-cqhttp/GetGroupFilesByFolder'; import { GetGroupFilesByFolder } from '@/onebot/action/go-cqhttp/GetGroupFilesByFolder';
import { GetGroupSystemMsg } from './system/GetSystemMsg'; import { GetGroupSystemMsg } from './system/GetSystemMsg';
import { GroupPoke } from './group/GroupPoke';
import { GetUserStatus } from './extends/GetUserStatus'; import { GetUserStatus } from './extends/GetUserStatus';
import { GetRkey } from './extends/GetRkey'; import { GetRkey } from './extends/GetRkey';
import { SetSpecialTitle } from './extends/SetSpecialTitle'; import { SetSpecialTitle } from './extends/SetSpecialTitle';
@@ -85,6 +86,7 @@ import { GetGroupShutList } from './group/GetGroupShutList';
import { GetGroupMemberList } from './group/GetGroupMemberList'; import { GetGroupMemberList } from './group/GetGroupMemberList';
import { GetGroupFileUrl } from '@/onebot/action/file/GetGroupFileUrl'; import { GetGroupFileUrl } from '@/onebot/action/file/GetGroupFileUrl';
import { GetPacketStatus } from '@/onebot/action/packet/GetPacketStatus'; import { GetPacketStatus } from '@/onebot/action/packet/GetPacketStatus';
import { FriendPoke } from '@/onebot/action/user/FriendPoke';
import { GetCredentials } from './system/GetCredentials'; import { GetCredentials } from './system/GetCredentials';
import { SendGroupSign, SetGroupSign } from './extends/SetGroupSign'; import { SendGroupSign, SetGroupSign } from './extends/SetGroupSign';
import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain'; import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain';
@@ -100,7 +102,7 @@ import { GetGuildList } from './guild/GetGuildList';
import { GetGuildProfile } from './guild/GetGuildProfile'; import { GetGuildProfile } from './guild/GetGuildProfile';
import { GetClientkey } from './extends/GetClientkey'; import { GetClientkey } from './extends/GetClientkey';
import { SendPacket } from './extends/SendPacket'; import { SendPacket } from './extends/SendPacket';
import { FriendPoke, GroupPoke, SendPoke } from '@/onebot/action/packet/SendPoke'; import { SendPoke } from '@/onebot/action/packet/SendPoke';
import { SetDiyOnlineStatus } from './extends/SetDiyOnlineStatus'; import { SetDiyOnlineStatus } from './extends/SetDiyOnlineStatus';
import { BotExit } from './extends/BotExit'; import { BotExit } from './extends/BotExit';
import { ClickInlineKeyboardButton } from './extends/ClickInlineKeyboardButton'; import { ClickInlineKeyboardButton } from './extends/ClickInlineKeyboardButton';
@@ -116,22 +118,10 @@ import { CleanCache } from './system/CleanCache';
import SetFriendRemark from './user/SetFriendRemark'; import SetFriendRemark from './user/SetFriendRemark';
import { SetDoubtFriendsAddRequest } from './new/SetDoubtFriendsAddRequest'; import { SetDoubtFriendsAddRequest } from './new/SetDoubtFriendsAddRequest';
import { GetDoubtFriendsAddRequest } from './new/GetDoubtFriendsAddRequest'; import { GetDoubtFriendsAddRequest } from './new/GetDoubtFriendsAddRequest';
import SetGroupAddOption from './extends/SetGroupAddOption';
import SetGroupSearch from './extends/SetGroupSearch';
import SetGroupRobotAddOption from './extends/SetGroupRobotAddOption';
import SetGroupKickMembers from './extends/SetGroupKickMembers';
import { GetGroupDetailInfo } from './group/GetGroupDetailInfo';
import GetGroupAddRequest from './extends/GetGroupAddRequest';
import { GetCollectionList } from './extends/GetCollectionList';
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) { export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
const actionHandlers = [ const actionHandlers = [
new GetGroupDetailInfo(obContext, core),
new SetGroupKickMembers(obContext, core),
new SetGroupAddOption(obContext, core),
new SetGroupRobotAddOption(obContext, core),
new SetGroupSearch(obContext, core),
new SetDoubtFriendsAddRequest(obContext, core), new SetDoubtFriendsAddRequest(obContext, core),
new GetDoubtFriendsAddRequest(obContext, core), new GetDoubtFriendsAddRequest(obContext, core),
new SetFriendRemark(obContext, core), new SetFriendRemark(obContext, core),
@@ -259,8 +249,6 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new GetPrivateFileUrl(obContext, core), new GetPrivateFileUrl(obContext, core),
new GetUnidirectionalFriendList(obContext, core), new GetUnidirectionalFriendList(obContext, core),
new CleanCache(obContext, core), new CleanCache(obContext, core),
new GetGroupAddRequest(obContext, core),
new GetCollectionList(obContext, core),
]; ];
type HandlerUnion = typeof actionHandlers[number]; type HandlerUnion = typeof actionHandlers[number];

View File

@@ -19,7 +19,6 @@ import { rawMsgWithSendMsg } from '@/core/packet/message/converter';
export interface ReturnDataType { export interface ReturnDataType {
message_id: number; message_id: number;
res_id?: string; res_id?: string;
forward_id?: string;
} }
export enum ContextMode { export enum ContextMode {
@@ -105,6 +104,8 @@ function getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType
} }
export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> { export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
contextMode = ContextMode.Normal;
protected override async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> { protected override async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
const messages = normalize(payload.message); const messages = normalize(payload.message);
const nodeElementLength = getSpecialMsgNum(payload, OB11MessageDataType.node); const nodeElementLength = getSpecialMsgNum(payload, OB11MessageDataType.node);
@@ -116,13 +117,12 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
} }
return { valid: true }; return { valid: true };
} }
async _handle(payload: OB11PostSendMsg): Promise<ReturnDataType> { async _handle(payload: OB11PostSendMsg): Promise<ReturnDataType> {
return this.base_handle(payload); this.contextMode = ContextMode.Normal;
} if (payload.message_type === 'group') this.contextMode = ContextMode.Group;
async base_handle(payload: OB11PostSendMsg, contextMode: ContextMode = ContextMode.Normal): Promise<ReturnDataType> { if (payload.message_type === 'private') this.contextMode = ContextMode.Private;
if (payload.message_type === 'group') contextMode = ContextMode.Group; const peer = await createContext(this.core, payload, this.contextMode);
if (payload.message_type === 'private') contextMode = ContextMode.Private;
const peer = await createContext(this.core, payload, contextMode);
const messages = normalize( const messages = normalize(
payload.message, payload.message,
@@ -148,10 +148,7 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
peerUid: peer.peerUid, peerUid: peer.peerUid,
chatType: peer.chatType, chatType: peer.chatType,
}, (returnMsgAndResId.message).msgId); }, (returnMsgAndResId.message).msgId);
return { message_id: msgShortId!, res_id: returnMsgAndResId.res_id! };
// 对gocq的forward_id进行兼容
const resId = returnMsgAndResId.res_id!;
return { message_id: msgShortId!, res_id: resId, forward_id: resId };
} else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) { } else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) {
throw Error(`发送转发消息res_id${returnMsgAndResId.res_id} 失败`); throw Error(`发送转发消息res_id${returnMsgAndResId.res_id} 失败`);
} }

View File

@@ -1,18 +1,16 @@
import { ContextMode, ReturnDataType, SendMsgBase } from './SendMsg'; import { ContextMode, SendMsgBase } from './SendMsg';
import { ActionName, BaseCheckResult } from '@/onebot/action/router'; import { ActionName, BaseCheckResult } from '@/onebot/action/router';
import { OB11PostSendMsg } from '@/onebot/types'; import { OB11PostSendMsg } from '@/onebot/types';
// 未检测参数 // 未检测参数
class SendPrivateMsg extends SendMsgBase { class SendPrivateMsg extends SendMsgBase {
override actionName = ActionName.SendPrivateMsg; override actionName = ActionName.SendPrivateMsg;
override contextMode: ContextMode = ContextMode.Private;
protected override async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> { protected override async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
payload.message_type = 'private'; payload.message_type = 'private';
return super.check(payload); return super.check(payload);
} }
override async _handle(payload: OB11PostSendMsg): Promise<ReturnDataType> {
return this.base_handle(payload, ContextMode.Private);
}
} }
export default SendPrivateMsg; export default SendPrivateMsg;

View File

@@ -3,36 +3,21 @@ import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
group_id: Type.Optional(Type.String()), group_id: Type.Optional(Type.Union([Type.Number(), Type.String()])),
user_id: Type.Optional(Type.String()), user_id: Type.Union([Type.Number(), Type.String()]),
target_id: Type.Optional(Type.String()),
}); });
type Payload = Static<typeof SchemaData>; type Payload = Static<typeof SchemaData>;
export class SendPokeBase extends GetPacketStatusDepends<Payload, void> {
export class SendPoke extends GetPacketStatusDepends<Payload, void> {
override actionName = ActionName.SendPoke;
override payloadSchema = SchemaData; override payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
// 这里的 !! 可以传入空字符串 忽略这些数据有利用接口统一接口 if (payload.group_id) {
const target_id = !!payload.target_id ? payload.target_id : payload.user_id; await this.core.apis.PacketApi.pkt.operation.GroupPoke(+payload.group_id, +payload.user_id);
const peer_id = !!payload.group_id ? payload.group_id : payload.user_id; } else {
await this.core.apis.PacketApi.pkt.operation.FriendPoke(+payload.user_id);
const is_group = !!payload.group_id;
if (!target_id || !peer_id) {
throw new Error('请检查参数,缺少 user_id 或 group_id');
} }
await this.core.apis.PacketApi.pkt.operation.SendPoke(is_group, +peer_id, +target_id);
} }
} }
export class SendPoke extends SendPokeBase {
override actionName = ActionName.SendPoke;
}
export class GroupPoke extends SendPokeBase {
override actionName = ActionName.GroupPoke;
}
export class FriendPoke extends SendPokeBase {
override actionName = ActionName.FriendPoke;
}

View File

@@ -10,10 +10,6 @@ export interface InvalidCheckResult {
} }
export const ActionName = { export const ActionName = {
SetGroupKickMembers: 'set_group_kick_members',
SetGroupRobotAddOption: 'set_group_robot_add_option',
SetGroupAddOption: 'set_group_add_option',
SetGroupSearch: 'set_group_search',
// new extends 完全差异OneBot类别 // new extends 完全差异OneBot类别
GetDoubtFriendsAddRequest: 'get_doubt_friends_add_request', GetDoubtFriendsAddRequest: 'get_doubt_friends_add_request',
SetDoubtFriendsAddRequest: 'set_doubt_friends_add_request', SetDoubtFriendsAddRequest: 'set_doubt_friends_add_request',
@@ -63,7 +59,7 @@ export const ActionName = {
GetStatus: 'get_status', GetStatus: 'get_status',
GetVersionInfo: 'get_version_info', GetVersionInfo: 'get_version_info',
// Reboot : 'set_restart', // Reboot : 'set_restart',
CleanCache: 'clean_cache', CleanCache : 'clean_cache',
Exit: 'bot_exit', Exit: 'bot_exit',
// go-cqhttp // go-cqhttp
SetQQProfile: 'set_qq_profile', SetQQProfile: 'set_qq_profile',
@@ -132,7 +128,6 @@ export const ActionName = {
FetchEmojiLike: 'fetch_emoji_like', FetchEmojiLike: 'fetch_emoji_like',
SetInputStatus: 'set_input_status', SetInputStatus: 'set_input_status',
GetGroupInfoEx: 'get_group_info_ex', GetGroupInfoEx: 'get_group_info_ex',
GetGroupDetailInfo: 'get_group_detail_info',
GetGroupIgnoreAddRequest: 'get_group_ignore_add_request', GetGroupIgnoreAddRequest: 'get_group_ignore_add_request',
DelGroupNotice: '_del_group_notice', DelGroupNotice: '_del_group_notice',
FriendPoke: 'friend_poke', FriendPoke: 'friend_poke',

View File

@@ -4,7 +4,6 @@ import { ActionName } from '@/onebot/action/router';
import { Notify } from '@/onebot/types'; import { Notify } from '@/onebot/types';
interface RetData { interface RetData {
invited_requests: Notify[];
InvitedRequest: Notify[]; InvitedRequest: Notify[];
join_requests: Notify[]; join_requests: Notify[];
} }
@@ -14,7 +13,7 @@ export class GetGroupSystemMsg extends OneBotAction<void, RetData> {
async _handle(): Promise<RetData> { async _handle(): Promise<RetData> {
const SingleScreenNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(false, 50); const SingleScreenNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(false, 50);
const retData: RetData = { invited_requests: [], InvitedRequest: [], join_requests: [] }; const retData: RetData = { InvitedRequest: [], join_requests: [] };
const notifyPromises = SingleScreenNotifies.map(async (SSNotify) => { const notifyPromises = SingleScreenNotifies.map(async (SSNotify) => {
const invitorUin = SSNotify.user1?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1.uid) : 0; const invitorUin = SSNotify.user1?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1.uid) : 0;
@@ -40,7 +39,6 @@ export class GetGroupSystemMsg extends OneBotAction<void, RetData> {
await Promise.all(notifyPromises); await Promise.all(notifyPromises);
retData.invited_requests = retData.InvitedRequest;
return retData; return retData;
} }
} }

View File

@@ -0,0 +1,18 @@
import { ActionName } from '@/onebot/action/router';
import { GetPacketStatusDepends } from '@/onebot/action/packet/GetPacketStatus';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
user_id: Type.Union([Type.Number(), Type.String()])
});
type Payload = Static<typeof SchemaData>;
export class FriendPoke extends GetPacketStatusDepends<Payload, void> {
override actionName = ActionName.FriendPoke;
override payloadSchema = SchemaData;
async _handle(payload: Payload) {
await this.core.apis.PacketApi.pkt.operation.FriendPoke(+payload.user_id);
}
}

View File

@@ -1209,6 +1209,7 @@ export class OneBotMsgApi {
async waitGroupNotify(groupUin: string, memberUid?: string, operatorUid?: string) { async waitGroupNotify(groupUin: string, memberUid?: string, operatorUid?: string) {
const groupRole = this.core.apis.GroupApi.groupMemberCache.get(groupUin)?.get(this.core.selfInfo.uid.toString())?.role; const groupRole = this.core.apis.GroupApi.groupMemberCache.get(groupUin)?.get(this.core.selfInfo.uid.toString())?.role;
const isAdminOrOwner = groupRole === 3 || groupRole === 4; const isAdminOrOwner = groupRole === 3 || groupRole === 4;
if (isAdminOrOwner && !operatorUid) { if (isAdminOrOwner && !operatorUid) {
let dataNotify: GroupNotify | undefined; let dataNotify: GroupNotify | undefined;
await this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onGroupNotifiesUpdated', await this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onGroupNotifiesUpdated',
@@ -1238,7 +1239,7 @@ export class OneBotMsgApi {
const operatorUid = await this.waitGroupNotify( const operatorUid = await this.waitGroupNotify(
groupChange.groupUin.toString(), groupChange.groupUin.toString(),
groupChange.memberUid, groupChange.memberUid,
groupChange.operatorInfo ? new TextDecoder('utf-8').decode(groupChange.operatorInfo) : undefined groupChange.operatorInfo ? Buffer.from(groupChange.operatorInfo).toString() : ''
); );
return new OB11GroupIncreaseEvent( return new OB11GroupIncreaseEvent(
this.core, this.core,
@@ -1250,42 +1251,13 @@ export class OneBotMsgApi {
} else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) { } else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) {
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
// 自身被踢出时operatorInfo会是一个protobuf 否则大多数情况为一个string
let operator_uid_parse: string | undefined = undefined;
if (groupChange.operatorInfo) {
// 先判断是否可能是protobuf自身被踢出或以0a开头
if (groupChange.decreaseType === 3 || Buffer.from(groupChange.operatorInfo).toString('hex').startsWith('0a')) {
// 可能是protobuf尝试解析
try {
operator_uid_parse = new NapProtoMsg(GroupChangeInfo).decode(groupChange.operatorInfo).operator?.operatorUid;
} catch (error) {
// protobuf解析失败fallback到字符串解析
try {
const decoded = new TextDecoder('utf-8').decode(groupChange.operatorInfo);
// 检查是否包含非ASCII字符如果包含则丢弃
const isAsciiOnly = [...decoded].every(char => char.charCodeAt(0) >= 32 && char.charCodeAt(0) <= 126);
operator_uid_parse = isAsciiOnly ? decoded : '';
} catch (e2) {
operator_uid_parse = '';
}
}
} else {
// 直接进行字符串解析
try {
const decoded = new TextDecoder('utf-8').decode(groupChange.operatorInfo);
// 检查是否包含非ASCII字符如果包含则丢弃
const isAsciiOnly = [...decoded].every(char => char.charCodeAt(0) >= 32 && char.charCodeAt(0) <= 126);
operator_uid_parse = isAsciiOnly ? decoded : '';
} catch (e) {
operator_uid_parse = '';
}
}
}
const operatorUid = await this.waitGroupNotify( const operatorUid = await this.waitGroupNotify(
groupChange.groupUin.toString(), groupChange.groupUin.toString(),
groupChange.memberUid, groupChange.memberUid,
operator_uid_parse groupChange.decreaseType === 3 && groupChange.operatorInfo ?
new NapProtoMsg(GroupChangeInfo).decode(groupChange.operatorInfo).operator?.operatorUid :
groupChange.operatorInfo?.toString()
); );
if (groupChange.memberUid === this.core.selfInfo.uid) { if (groupChange.memberUid === this.core.selfInfo.uid) {
setTimeout(() => { setTimeout(() => {

View File

@@ -2,5 +2,4 @@ import { EventType, OneBotEvent } from '@/onebot/event/OneBotEvent';
export abstract class OB11BaseNoticeEvent extends OneBotEvent { export abstract class OB11BaseNoticeEvent extends OneBotEvent {
post_type = EventType.NOTICE; post_type = EventType.NOTICE;
abstract notice_type: string; }
}

View File

@@ -1,6 +0,0 @@
import { EventType, OneBotEvent } from '@/onebot/event/OneBotEvent';
export abstract class OB11BaseRequestEvent extends OneBotEvent {
readonly post_type = EventType.REQUEST;
abstract request_type: string;
}

View File

@@ -1,8 +1,10 @@
import { OB11BaseNoticeEvent } from '@/onebot/event/notice/OB11BaseNoticeEvent';
import { EventType } from '@/onebot/event/OneBotEvent';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
import { OB11BaseRequestEvent } from './OB11BaseRequestEvent';
export class OB11FriendRequestEvent extends OB11BaseRequestEvent { export class OB11FriendRequestEvent extends OB11BaseNoticeEvent {
override request_type = 'friend'; override post_type = EventType.REQUEST;
request_type = 'friend';
user_id: number; user_id: number;
comment: string; comment: string;

View File

@@ -1,18 +1,18 @@
import { OB11GroupNoticeEvent } from '@/onebot/event/notice/OB11GroupNoticeEvent';
import { EventType } from '@/onebot/event/OneBotEvent';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
import { OB11BaseRequestEvent } from './OB11BaseRequestEvent';
export class OB11GroupRequestEvent extends OB11BaseRequestEvent { export class OB11GroupRequestEvent extends OB11GroupNoticeEvent {
override readonly request_type = 'group' as const; override post_type = EventType.REQUEST;
request_type = 'group';
group_id: number; override user_id: number;
user_id: number;
comment: string; comment: string;
flag: string; flag: string;
sub_type: string; sub_type: string;
constructor(core: NapCatCore, groupId: number, userId: number, sub_type: string, comment: string, flag: string) { constructor(core: NapCatCore, groupId: number, userId: number, sub_type: string, comment: string, flag: string) {
super(core); super(core, groupId, userId);
this.group_id = groupId;
this.user_id = userId; this.user_id = userId;
this.sub_type = sub_type; this.sub_type = sub_type;
this.comment = comment; this.comment = comment;

View File

@@ -270,6 +270,7 @@ export class NapCatOneBot11Adapter {
); );
} }
}; };
msgListener.onAddSendMsg = async (msg) => { msgListener.onAddSendMsg = async (msg) => {
try { try {
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) {
@@ -281,8 +282,7 @@ export class NapCatOneBot11Adapter {
}, 1, 10 * 60 * 1000); }, 1, 10 * 60 * 1000);
// 10分钟 超时 // 10分钟 超时
const updatemsg = updatemsgs.find((e) => e.msgId === msg.msgId); const updatemsg = updatemsgs.find((e) => e.msgId === msg.msgId);
// updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS_NOSEQ NOSEQ一般是服务器未下发SEQ 这意味着这条消息不应该推送network if (updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS || updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS_NOSEQ) {
if (updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS) {
updatemsg.id = MessageUnique.createUniqueMsgId( updatemsg.id = MessageUnique.createUniqueMsgId(
{ {
chatType: updatemsg.chatType, chatType: updatemsg.chatType,
@@ -304,18 +304,8 @@ export class NapCatOneBot11Adapter {
peerUid: uid, peerUid: uid,
guildId: '' guildId: ''
}; };
let msg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, msgSeq)).msgList.find(e => e.msgType == NTMsgType.KMSGTYPEGRAYTIPS); const msg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, msgSeq)).msgList.find(e => e.msgType == NTMsgType.KMSGTYPEGRAYTIPS);
const element = msg?.elements.find(e => !!e.grayTipElement?.revokeElement); const element = msg?.elements.find(e => !!e.grayTipElement?.revokeElement);
if (element?.grayTipElement?.revokeElement.isSelfOperate && msg) {
await this.core.eventWrapper.registerListen('NodeIKernelMsgListener/onMsgRecall',
(chatType: ChatType, uid: string, msgSeq: string) => {
return chatType === msg?.chatType && uid === msg?.peerUid && msgSeq === msg?.msgSeq;
}
).catch(() => {
msg = undefined;
this.context.logger.logDebug('自操作消息撤回事件');
});
}
if (msg && element) { if (msg && element) {
const recallEvent = await this.emitRecallMsg(msg, element); const recallEvent = await this.emitRecallMsg(msg, element);
try { try {
@@ -326,7 +316,6 @@ export class NapCatOneBot11Adapter {
this.context.logger.logError('处理消息撤回失败', e); this.context.logger.logError('处理消息撤回失败', e);
} }
} }
}; };
msgListener.onKickedOffLine = async (kick) => { msgListener.onKickedOffLine = async (kick) => {
const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc); const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc);

View File

@@ -87,8 +87,8 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter<HttpServerConfig>
this.app.use(async (req, res) => { this.app.use(async (req, res) => {
await this.handleRequest(req, res); await this.handleRequest(req, res);
}); });
this.server.listen(this.config.port, this.config.host, () => { this.server.listen(this.config.port, () => {
this.core.context.logger.log(`[OneBot] [HTTP Server Adapter] Start On ${this.config.host}:${this.config.port}`); this.core.context.logger.log(`[OneBot] [HTTP Server Adapter] Start On Port ${this.config.port}`);
}); });
} }

View File

@@ -47,9 +47,6 @@ export const CreateTerminalHandler: RequestHandler = async (req, res) => {
if (isMacOS) { if (isMacOS) {
return sendError(res, 'MacOS不支持终端'); return sendError(res, 'MacOS不支持终端');
} }
if ((await WebUiConfig.GetWebUIConfig()).token === 'napcat') {
return sendError(res, '默认密码禁止创建终端');
}
try { try {
const { cols, rows } = req.body; const { cols, rows } = req.body;
const { id } = terminalManager.createTerminal(cols, rows); const { id } = terminalManager.createTerminal(cols, rows);

View File

@@ -1,5 +1,4 @@
import { Router } from 'express'; import { Router } from 'express';
import rateLimit from 'express-rate-limit';
import { import {
ListFilesHandler, ListFilesHandler,
CreateDirHandler, CreateDirHandler,
@@ -20,15 +19,15 @@ import {
const router = Router(); const router = Router();
const apiLimiter = rateLimit({ // const apiLimiter = rateLimit({
windowMs: 1 * 60 * 1000, // 1分钟内 // windowMs: 1 * 60 * 1000, // 1分钟内
max: 60, // 最大60个请求 // max: 60, // 最大60个请求
validate: { // validate: {
xForwardedForHeader: false, // xForwardedForHeader: false,
}, // },
}); // });
router.use(apiLimiter); // router.use(apiLimiter);
router.get('/list', ListFilesHandler); router.get('/list', ListFilesHandler);
router.post('/mkdir', CreateDirHandler); router.post('/mkdir', CreateDirHandler);

View File

@@ -9,7 +9,7 @@ Object.defineProperty(global, '__dirname', {
// 注意:堆栈格式可能不同,请根据实际环境调整索引及正则表达式 // 注意:堆栈格式可能不同,请根据实际环境调整索引及正则表达式
for (const line of stack) { for (const line of stack) {
const match = line.match(/\((.*):\d+:\d+\)/); const match = line.match(/\((.*):\d+:\d+\)/);
if (match?.[1]) { if (match) {
callerFile = match[1]; callerFile = match[1];
if (!callerFile.includes('init-dynamic-dirname.ts')) { if (!callerFile.includes('init-dynamic-dirname.ts')) {
break; break;

View File

@@ -1,13 +1,13 @@
import multer from 'multer'; import multer from 'multer';
import { WebUiConfigWrapper } from '../helper/config';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import { WebUiConfig } from '@/webui';
export const webUIFontStorage = multer.diskStorage({ export const webUIFontStorage = multer.diskStorage({
destination: (_, __, cb) => { destination: (_, __, cb) => {
try { try {
const fontsPath = path.dirname(WebUiConfig.GetWebUIFontPath()); const fontsPath = path.dirname(WebUiConfigWrapper.GetWebUIFontPath());
// 确保字体目录存在 // 确保字体目录存在
fs.mkdirSync(fontsPath, { recursive: true }); fs.mkdirSync(fontsPath, { recursive: true });
cb(null, fontsPath); cb(null, fontsPath);

View File

@@ -3,6 +3,7 @@ import { defineConfig, PluginOption, UserConfig } from 'vite';
import { resolve } from 'path'; import { resolve } from 'path';
import nodeResolve from '@rollup/plugin-node-resolve'; import nodeResolve from '@rollup/plugin-node-resolve';
import { builtinModules } from 'module'; import { builtinModules } from 'module';
import wasm from 'vite-plugin-wasm';
//依赖排除 //依赖排除
const external = [ const external = [
'silk-wasm', 'silk-wasm',
@@ -21,6 +22,7 @@ if (process.env.NAPCAT_BUILDSYS == 'linux') {
} }
const UniversalBaseConfigPlugin: PluginOption[] = [ const UniversalBaseConfigPlugin: PluginOption[] = [
wasm(),
cp({ cp({
targets: [ targets: [
{ src: './manifest.json', dest: 'dist' }, { src: './manifest.json', dest: 'dist' },
@@ -44,6 +46,7 @@ const UniversalBaseConfigPlugin: PluginOption[] = [
]; ];
const FrameworkBaseConfigPlugin: PluginOption[] = [ const FrameworkBaseConfigPlugin: PluginOption[] = [
wasm(),
cp({ cp({
targets: [ targets: [
{ src: './manifest.json', dest: 'dist' }, { src: './manifest.json', dest: 'dist' },
@@ -53,7 +56,6 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
{ src: './napcat.webui/dist/', dest: 'dist/static/', flatten: false }, { src: './napcat.webui/dist/', dest: 'dist/static/', flatten: false },
{ src: './src/framework/liteloader.cjs', dest: 'dist' }, { src: './src/framework/liteloader.cjs', dest: 'dist' },
{ src: './src/framework/napcat.cjs', dest: 'dist' }, { src: './src/framework/napcat.cjs', dest: 'dist' },
{ src: './src/framework/nativeLoader.cjs', dest: 'dist' },
{ src: './src/framework/preload.cjs', dest: 'dist' }, { src: './src/framework/preload.cjs', dest: 'dist' },
{ src: './src/framework/renderer.js', dest: 'dist' }, { src: './src/framework/renderer.js', dest: 'dist' },
{ src: './package.json', dest: 'dist' }, { src: './package.json', dest: 'dist' },
@@ -64,6 +66,7 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
]; ];
const ShellBaseConfigPlugin: PluginOption[] = [ const ShellBaseConfigPlugin: PluginOption[] = [
wasm(),
cp({ cp({
targets: [ targets: [
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false }, { src: './src/native/packet', dest: 'dist/moehoo', flatten: false },