Compare commits

...

73 Commits

Author SHA1 Message Date
手瓜一十雪
7330a05c78 fix
Some checks failed
Build Action / Build-LiteLoader (push) Has been cancelled
Build Action / Build-Shell (push) Has been cancelled
2025-07-11 19:28:01 +08:00
Mlikiowa
a39c932868 release: v4.8.93 2025-07-09 11:21:08 +00:00
dependabot[bot]
a2c24c9197 build(deps-dev): bump @eslint/js from 9.28.0 to 9.30.1 (#1110)
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.28.0 to 9.30.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.30.1/packages/js)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.30.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 19:11:09 +08:00
dependabot[bot]
5c3efc681f build(deps-dev): bump @eslint/compat from 1.3.0 to 1.3.1 (#1099)
Bumps [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/compat-v1.3.1/packages/compat)

---
updated-dependencies:
- dependency-name: "@eslint/compat"
  dependency-version: 1.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 19:10:54 +08:00
dependabot[bot]
e70d2bd708 build(deps-dev): bump typescript-eslint from 8.34.0 to 8.35.1 (#1112)
---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.35.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 19:10:14 +08:00
dependabot[bot]
cf75a961fb build(deps-dev): bump @rollup/plugin-typescript from 12.1.2 to 12.1.4 (#1098)
Bumps [@rollup/plugin-typescript](https://github.com/rollup/plugins/tree/HEAD/packages/typescript) from 12.1.2 to 12.1.4.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/typescript/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/typescript-v12.1.4/packages/typescript)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-typescript"
  dependency-version: 12.1.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 19:10:02 +08:00
手瓜一十雪
159f317071 Add support for 3.2.18-37051 and 9.9.20-37051 versions
Updated appid.json and offset.json to include new entries for versions 3.2.18-37051 and 9.9.20-37051, including their respective app IDs, QUA values, and offset mappings for x64 and arm64 architectures.
2025-07-09 19:06:47 +08:00
Mlikiowa
713eef592a release: v4.8.92 2025-07-07 12:47:57 +00:00
囧囧JOJO
cf03ad8fd9 feat: 向 /get_system_msg 添加可选参数 'count' (#1113)
* feat: Add the optional parameter "count" to /get_system_msg

* Refactor GetGroupSystemMsg to use TypeBox schema

Introduced TypeBox for payload validation in GetGroupSystemMsg, replacing manual count handling with a schema-based approach. Updated the handler to use the new payload type and schema, improving type safety and input validation.

---------

Co-authored-by: 手瓜一十雪 <nanaeonn@outlook.com>
2025-07-07 20:46:26 +08:00
Mlikiowa
0c0b27901a release: v4.8.91 2025-07-07 12:41:15 +00:00
手瓜一十雪
137fe3c8f2 feat: 37012 2025-07-07 20:40:36 +08:00
Mlikiowa
d96174076a release: v4.8.90 2025-06-29 02:01:31 +00:00
手瓜一十雪
6d5662d96e feat: Add new Linux native modules for arm64 and x64
Added MoeHoo.linux.arm64.new.node and MoeHoo.linux.x64.new.node binaries to support native packet functionality on both ARM64 and x64 Linux platforms.
2025-06-29 09:58:32 +08:00
Mlikiowa
57abd47d99 release: v4.7.85 2025-06-26 10:35:59 +00:00
手瓜一十雪
5092b3d791 fix: package 2025-06-26 18:35:12 +08:00
Mlikiowa
649409d1be release: v4.7.81 2025-06-26 10:32:56 +00:00
手瓜一十雪
8f549d896a feat: package 2025-06-26 18:32:31 +08:00
Mlikiowa
a1359ddbb5 release: v4.7.80 2025-06-26 10:30:32 +00:00
手瓜一十雪
304a0dda3e feat: 初步验证win 36580 2025-06-26 18:30:06 +08:00
手瓜一十雪
fff9c4a4d8 feat: 36580 2025-06-26 17:06:37 +08:00
Wang Zeng
2c76102fc4 ci: dispatch docker build workflow after release (#1078) 2025-06-15 23:26:17 +08:00
手瓜一十雪
f576cd9417 fix: type 2025-06-13 16:58:05 +08:00
时瑾
9cfd224b74 fix: 优化get_group_ignored_notifies接口返回值 2025-06-12 20:14:11 +08:00
时瑾
c12f8de8b4 feat: get_collection_list 2025-06-12 13:28:31 +08:00
时瑾
ed9a7c52e2 feat: get_group_ignore_add_request 2025-06-12 13:23:22 +08:00
Mlikiowa
38fcaaa28b release: v4.7.78 2025-06-12 04:30:05 +00:00
手瓜一十雪
5317a1c1a9 fix: 35951 2025-06-12 12:29:14 +08:00
时瑾
4bc5933ea2 fix: 修正部分接口的参数、返回值,提高兼容性 (#1072)
* fix: 修正`get_group_system_msg` `get_group_honor_info`接口返回值 提升兼容性

* fix: `create_group_file_folder` 接口兼容性提升
2025-06-11 12:37:29 +08:00
Mlikiowa
6a6bd33fe5 release: v4.7.77 2025-06-10 06:28:20 +00:00
手瓜一十雪
8256942a3d fix: #1051 2025-06-10 14:27:50 +08:00
手瓜一十雪
697632eee8 feat: 35951 2025-06-10 13:19:19 +08:00
手瓜一十雪
6bbf5b254d fix: #1049 2025-06-10 13:05:36 +08:00
手瓜一十雪
5831898c4a fix: #1058 2025-06-10 12:54:29 +08:00
dependabot[bot]
2cc413bec1 build(deps-dev): bump multer from 1.4.5-lts.2 to 2.0.1 (#1070)
Bumps [multer](https://github.com/expressjs/multer) from 1.4.5-lts.2 to 2.0.1.
- [Release notes](https://github.com/expressjs/multer/releases)
- [Changelog](https://github.com/expressjs/multer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/expressjs/multer/compare/v1.4.5-lts.2...v2.0.1)

---
updated-dependencies:
- dependency-name: multer
  dependency-version: 2.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-10 12:48:39 +08:00
时瑾
0af36e89d9 fix: 转发消息接口返回值兼容gocq (#1066) 2025-06-09 10:02:27 +08:00
837951602
b2c0f5d2e5 /get_group_system_msg description (#1064) 2025-06-08 10:38:32 +08:00
手瓜一十雪
80b74c7da9 Merge pull request #1054 from NapNeko/dependabot/npm_and_yarn/file-type-21.0.0
build(deps-dev): bump file-type from 20.5.0 to 21.0.0
2025-06-06 11:53:31 +08:00
手瓜一十雪
f14f13b158 build(deps-dev): bump esbuild from 0.25.4 to 0.25.5 (#1056)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.4 to 0.25.5.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.4...v0.25.5)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-06 11:53:06 +08:00
lzw
9dda00b6fa chore: add host in listen log
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2025-06-05 12:59:01 +08:00
Lan Zongwei
a29debb738 fix: fix missing host in onebot http-server listen 2025-06-05 12:59:01 +08:00
dependabot[bot]
b990fc43df build(deps-dev): bump esbuild from 0.25.4 to 0.25.5
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.4 to 0.25.5.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.4...v0.25.5)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 09:14:28 +00:00
dependabot[bot]
915e9552ee build(deps-dev): bump file-type from 20.5.0 to 21.0.0
Bumps [file-type](https://github.com/sindresorhus/file-type) from 20.5.0 to 21.0.0.
- [Release notes](https://github.com/sindresorhus/file-type/releases)
- [Commits](https://github.com/sindresorhus/file-type/compare/v20.5.0...v21.0.0)

---
updated-dependencies:
- dependency-name: file-type
  dependency-version: 21.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 09:09:48 +00:00
Mlikiowa
c522e0a386 release: v4.7.76 2025-05-29 13:58:02 +00:00
手瓜一十雪
c9cc08a9ba fix: #1048 2025-05-29 21:15:07 +08:00
手瓜一十雪
66e1b1662f fix: 支持registerCallback 2025-05-29 20:45:35 +08:00
手瓜一十雪
9372e83bd8 feat: nativeLoader功能预备 2025-05-29 14:39:09 +08:00
Mlikiowa
b38a240dbb release: v4.7.75 2025-05-26 12:19:44 +00:00
手瓜一十雪
76b9506395 fix: #1043 2025-05-26 19:58:50 +08:00
手瓜一十雪
f1cf636aa2 Merge pull request #1041 from Neboer/main
允许使用环境变量指定napcat工作路径。
2025-05-26 14:41:01 +08:00
Mlikiowa
312dcd0e13 release: v4.7.74 2025-05-26 05:57:44 +00:00
手瓜一十雪
42c2419613 Revert "fix: #1038"
This reverts commit 4e7c96634c.
2025-05-26 13:56:48 +08:00
手瓜一十雪
8f7f748e82 Revert "fix: #1039"
This reverts commit 1eda3f2e33.
2025-05-26 13:56:14 +08:00
Neboer
7ad3bad1be 修改环境变量名字NAPCAT_WRITEPATH为NAPCAT_WORKDIR 2025-05-26 05:36:07 +00:00
Neboer
5cd682e69f 允许使用NAPCAT_WRITEPATH环境变量指定napcat工作路径。 2025-05-26 04:59:20 +00:00
Mlikiowa
5d57780e84 release: v4.7.73 2025-05-26 03:52:01 +00:00
手瓜一十雪
f399955204 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-05-26 11:51:35 +08:00
手瓜一十雪
770652fe6b fix: remove debug 2025-05-26 11:51:25 +08:00
Mlikiowa
9ed5fa8c67 release: v4.7.72 2025-05-26 03:51:12 +00:00
手瓜一十雪
5a4ad29727 fix: #1040 2025-05-26 11:50:45 +08:00
手瓜一十雪
1eda3f2e33 fix: #1039 2025-05-26 10:58:01 +08:00
Mlikiowa
95cb95ef96 release: v4.7.70 2025-05-25 08:56:20 +00:00
手瓜一十雪
4e7c96634c fix: #1038 2025-05-25 16:55:32 +08:00
手瓜一十雪
58587b8aea fix 2025-05-25 16:30:15 +08:00
手瓜一十雪
3fbf6239db fix: #1031 2025-05-25 16:18:50 +08:00
手瓜一十雪
faec53d497 feat: #1031 2025-05-25 16:09:06 +08:00
手瓜一十雪
482dcc534e feat: kill-update 2025-05-24 10:33:14 +08:00
手瓜一十雪
854f61dda6 feat: createGrayTip 2025-05-23 17:22:07 +08:00
Mlikiowa
fca38713a1 release: v4.7.68 2025-05-22 03:48:00 +00:00
手瓜一十雪
5dd3bade53 fix: #1029 2025-05-22 11:47:31 +08:00
手瓜一十雪
665360f48d fix: #1027 2025-05-22 11:33:23 +08:00
Mlikiowa
65719cb56a release: v4.7.67 2025-05-21 04:59:16 +00:00
手瓜一十雪
bdb76d4639 fix: make ts happy 2025-05-21 12:58:53 +08:00
Mlikiowa
15634412ef release: v4.7.66 2025-05-21 04:53:55 +00:00
49 changed files with 25319 additions and 57 deletions

View File

@@ -150,3 +150,15 @@ jobs:
NapCat.Shell.zip
NapCat.Framework.Windows.Once.zip
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,6 +1,5 @@
# Develop
node_modules/
package-lock.json
pnpm-lock.yaml
out/
dist/

Binary file not shown.

View File

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

15995
napcat.webui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

8301
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.7.65",
"version": "4.8.93",
"scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
@@ -18,14 +18,14 @@
},
"devDependencies": {
"@babel/preset-typescript": "^7.24.7",
"@eslint/compat": "^1.2.2",
"@eslint/compat": "^1.3.1",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.14.0",
"@eslint/js": "^9.30.1",
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
"@log4js-node/log4js-api": "^1.0.2",
"@napneko/nap-proto-core": "^0.0.4",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-typescript": "^12.1.2",
"@rollup/plugin-typescript": "^12.1.4",
"@sinclair/typebox": "^0.34.9",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
@@ -42,18 +42,18 @@
"async-mutex": "^0.5.0",
"commander": "^13.0.0",
"cors": "^2.8.5",
"esbuild": "0.25.4",
"esbuild": "0.25.5",
"eslint": "^9.14.0",
"eslint-import-resolver-typescript": "^4.0.0",
"eslint-plugin-import": "^2.29.1",
"express-rate-limit": "^7.5.0",
"fast-xml-parser": "^4.3.6",
"file-type": "^20.0.0",
"file-type": "^21.0.0",
"globals": "^16.0.0",
"json5": "^2.2.3",
"multer": "^1.4.5-lts.1",
"multer": "^2.0.1",
"typescript": "^5.3.3",
"typescript-eslint": "^8.13.0",
"typescript-eslint": "^8.35.1",
"vite": "^6.0.1",
"vite-plugin-cp": "^6.0.0",
"vite-tsconfig-paths": "^5.1.0",

View File

@@ -10,10 +10,14 @@ import { LogWrapper } from './log';
const downloadOri = "https://github.com/NapNeko/ffmpeg-build/releases/download/v1.0.0/ffmpeg-7.1.1-win64.zip"
const urls = [
"https://github.moeyy.xyz/" + downloadOri,
"https://ghp.ci/" + downloadOri,
"https://gh.api.99988866.xyz/" + downloadOri,
"https://gh.api.99988866.xyz/" + downloadOri,
"https://j.1win.ggff.net/" + downloadOri,
"https://git.yylx.win/" + downloadOri,
"https://ghfile.geekertao.top/" + downloadOri,
"https://gh-proxy.net/" + downloadOri,
"https://ghm.078465.xyz/" + downloadOri,
"https://gitproxy.127731.xyz/" + downloadOri,
"https://jiashu.1win.eu.org/" + downloadOri,
"https://github.tbedu.top/" + downloadOri,
downloadOri
];

View File

@@ -13,11 +13,15 @@ export class NapCatPathWrapper {
constructor(mainPath: string = dirname(fileURLToPath(import.meta.url))) {
this.binaryPath = mainPath;
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');
} else {
writePath = this.binaryPath;
}
this.logsPath = path.join(writePath, 'logs');
this.configPath = path.join(writePath, 'config');
this.cachePath = path.join(writePath, 'cache');

View File

@@ -1 +1 @@
export const napCatVersion = '4.7.65';
export const napCatVersion = '4.8.93';

View File

@@ -10,11 +10,14 @@ import {
GroupNotify,
GroupInfoSource,
ShutUpGroupMember,
Peer,
ChatType,
} from '@/core';
import { isNumeric, solveAsyncProblem } from '@/common/helper';
import { LimitedHashTable } from '@/common/message-unique';
import { NTEventWrapper } from '@/common/event';
import { CancelableTask, TaskExecutor } from '@/common/cancel-task';
import { createGroupDetailInfoV2Param, createGroupExtFilter, createGroupExtInfo } from '../data';
export class NTQQGroupApi {
context: InstanceContext;
@@ -47,6 +50,22 @@ export class NTQQGroupApi {
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() {
for (const group of await this.getGroups(true)) {
this.refreshGroupMemberCache(group.groupCode, false).then().catch(e => this.context.logger.logError(e));
@@ -95,6 +114,58 @@ export class NTQQGroupApi {
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) {
const [, , groupList] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getGroupList',

View File

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

245
src/core/data/group.ts Normal file
View File

@@ -0,0 +1,245 @@
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,
}
};

1
src/core/data/index.ts Normal file
View File

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

View File

@@ -302,5 +302,41 @@
"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"
},
"3.2.18-37051": {
"appid": 537304158,
"qua": "V1_LNX_NQ_3.2.18_37051_GW_B"
},
"9.9.20-37051": {
"appid": 537304122,
"qua": "V1_WIN_NQ_9.9.20_37051_GW_B"
}
}

View File

@@ -380,8 +380,12 @@
"recv": "3BEA210"
},
"9.9.19-35341-x64": {
"send": "3BE5A10",
"recv": "3BEA210"
"send": "3BF1D50",
"recv": "3BF6550"
},
"9.9.19-35469-x64": {
"send": "3BF1D50",
"recv": "3BF6550"
},
"3.2.17-35341-x64": {
"send": "AE2F700",
@@ -390,5 +394,49 @@
"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"
},
"3.2.18-37051-x64": {
"send": "B20F960",
"recv": "B2133E0"
},
"3.2.18-37051-arm64": {
"send": "7A19E00",
"recv": "7A1D790"
},
"9.9.20-37051-x64": {
"send": "30CC958",
"recv": "30D00FC"
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,97 @@
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 {
optFlag: number;
@@ -7,6 +100,185 @@ export interface KickMemberInfo {
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 {
KUNSPECIFIED,

View File

@@ -48,6 +48,12 @@ 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 loginListener = new NodeIKernelLoginListener();
loginListener.onQRCodeLoginSucceed = async (loginResult) => {

View File

@@ -0,0 +1,26 @@
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
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,28 @@
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

@@ -0,0 +1,23 @@
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

@@ -0,0 +1,27 @@
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

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

View File

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

View File

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

View File

@@ -116,10 +116,22 @@ import { CleanCache } from './system/CleanCache';
import SetFriendRemark from './user/SetFriendRemark';
import { SetDoubtFriendsAddRequest } from './new/SetDoubtFriendsAddRequest';
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) {
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 GetDoubtFriendsAddRequest(obContext, core),
new SetFriendRemark(obContext, core),
@@ -247,6 +259,8 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new GetPrivateFileUrl(obContext, core),
new GetUnidirectionalFriendList(obContext, core),
new CleanCache(obContext, core),
new GetGroupAddRequest(obContext, core),
new GetCollectionList(obContext, core),
];
type HandlerUnion = typeof actionHandlers[number];

View File

@@ -19,6 +19,7 @@ import { rawMsgWithSendMsg } from '@/core/packet/message/converter';
export interface ReturnDataType {
message_id: number;
res_id?: string;
forward_id?: string;
}
export enum ContextMode {
@@ -147,7 +148,10 @@ export class SendMsgBase extends OneBotAction<OB11PostSendMsg, ReturnDataType> {
peerUid: peer.peerUid,
chatType: peer.chatType,
}, (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) {
throw Error(`发送转发消息res_id${returnMsgAndResId.res_id} 失败`);
}

View File

@@ -10,6 +10,10 @@ export interface InvalidCheckResult {
}
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类别
GetDoubtFriendsAddRequest: 'get_doubt_friends_add_request',
SetDoubtFriendsAddRequest: 'set_doubt_friends_add_request',
@@ -59,7 +63,7 @@ export const ActionName = {
GetStatus: 'get_status',
GetVersionInfo: 'get_version_info',
// Reboot : 'set_restart',
CleanCache : 'clean_cache',
CleanCache: 'clean_cache',
Exit: 'bot_exit',
// go-cqhttp
SetQQProfile: 'set_qq_profile',
@@ -128,6 +132,7 @@ export const ActionName = {
FetchEmojiLike: 'fetch_emoji_like',
SetInputStatus: 'set_input_status',
GetGroupInfoEx: 'get_group_info_ex',
GetGroupDetailInfo: 'get_group_detail_info',
GetGroupIgnoreAddRequest: 'get_group_ignore_add_request',
DelGroupNotice: '_del_group_notice',
FriendPoke: 'friend_poke',

View File

@@ -2,18 +2,27 @@ import { GroupNotifyMsgStatus } from '@/core';
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Notify } from '@/onebot/types';
import { Static, Type } from '@sinclair/typebox';
interface RetData {
invited_requests: Notify[];
InvitedRequest: Notify[];
join_requests: Notify[];
}
export class GetGroupSystemMsg extends OneBotAction<void, RetData> {
override actionName = ActionName.GetGroupSystemMsg;
const SchemaData = Type.Object({
count: Type.Union([Type.Number(), Type.String()], { default: 50 }),
});
async _handle(): Promise<RetData> {
const SingleScreenNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(false, 50);
const retData: RetData = { InvitedRequest: [], join_requests: [] };
type Payload = Static<typeof SchemaData>;
export class GetGroupSystemMsg extends OneBotAction<Payload, RetData> {
override actionName = ActionName.GetGroupSystemMsg;
override payloadSchema = SchemaData;
async _handle(params: Payload): Promise<RetData> {
const SingleScreenNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(false, +params.count);
const retData: RetData = { invited_requests: [], InvitedRequest: [], join_requests: [] };
const notifyPromises = SingleScreenNotifies.map(async (SSNotify) => {
const invitorUin = SSNotify.user1?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1.uid) : 0;
@@ -39,6 +48,7 @@ export class GetGroupSystemMsg extends OneBotAction<void, RetData> {
await Promise.all(notifyPromises);
retData.invited_requests = retData.InvitedRequest;
return retData;
}
}

View File

@@ -1209,7 +1209,6 @@ export class OneBotMsgApi {
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 isAdminOrOwner = groupRole === 3 || groupRole === 4;
if (isAdminOrOwner && !operatorUid) {
let dataNotify: GroupNotify | undefined;
await this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onGroupNotifiesUpdated',
@@ -1239,7 +1238,7 @@ export class OneBotMsgApi {
const operatorUid = await this.waitGroupNotify(
groupChange.groupUin.toString(),
groupChange.memberUid,
groupChange.operatorInfo ? Buffer.from(groupChange.operatorInfo).toString() : ''
groupChange.operatorInfo ? new TextDecoder('utf-8').decode(groupChange.operatorInfo) : undefined
);
return new OB11GroupIncreaseEvent(
this.core,
@@ -1251,13 +1250,42 @@ export class OneBotMsgApi {
} else if (SysMessage.contentHead.type == 34 && 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(
groupChange.groupUin.toString(),
groupChange.memberUid,
groupChange.decreaseType === 3 && groupChange.operatorInfo ?
new NapProtoMsg(GroupChangeInfo).decode(groupChange.operatorInfo).operator?.operatorUid :
groupChange.operatorInfo?.toString()
operator_uid_parse
);
if (groupChange.memberUid === this.core.selfInfo.uid) {
setTimeout(() => {

View File

@@ -270,7 +270,6 @@ export class NapCatOneBot11Adapter {
);
}
};
msgListener.onAddSendMsg = async (msg) => {
try {
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) {
@@ -282,7 +281,8 @@ export class NapCatOneBot11Adapter {
}, 1, 10 * 60 * 1000);
// 10分钟 超时
const updatemsg = updatemsgs.find((e) => e.msgId === msg.msgId);
if (updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS || updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS_NOSEQ) {
// updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS_NOSEQ NOSEQ一般是服务器未下发SEQ 这意味着这条消息不应该推送network
if (updatemsg?.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS) {
updatemsg.id = MessageUnique.createUniqueMsgId(
{
chatType: updatemsg.chatType,
@@ -304,8 +304,18 @@ export class NapCatOneBot11Adapter {
peerUid: uid,
guildId: ''
};
const msg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, msgSeq)).msgList.find(e => e.msgType == NTMsgType.KMSGTYPEGRAYTIPS);
let 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);
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) {
const recallEvent = await this.emitRecallMsg(msg, element);
try {
@@ -316,6 +326,7 @@ export class NapCatOneBot11Adapter {
this.context.logger.logError('处理消息撤回失败', e);
}
}
};
msgListener.onKickedOffLine = async (kick) => {
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) => {
await this.handleRequest(req, res);
});
this.server.listen(this.config.port, () => {
this.core.context.logger.log(`[OneBot] [HTTP Server Adapter] Start On Port ${this.config.port}`);
this.server.listen(this.config.port, this.config.host, () => {
this.core.context.logger.log(`[OneBot] [HTTP Server Adapter] Start On ${this.config.host}:${this.config.port}`);
});
}

View File

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

View File

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

View File

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

View File

@@ -53,6 +53,7 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
{ src: './napcat.webui/dist/', dest: 'dist/static/', flatten: false },
{ src: './src/framework/liteloader.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/renderer.js', dest: 'dist' },
{ src: './package.json', dest: 'dist' },