mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c54a58d6e4 | ||
![]() |
34cb1ea3fd | ||
![]() |
f640b0ca91 | ||
![]() |
60e16da42e | ||
![]() |
0cdceb95d6 | ||
![]() |
70bd22d925 | ||
![]() |
82462dd647 | ||
![]() |
c0466e943d | ||
![]() |
b187b4695d | ||
![]() |
b1956d2a37 | ||
![]() |
590b622e5f | ||
![]() |
3d8174396a | ||
![]() |
b8dc6e9bd9 | ||
![]() |
8ee99109dc | ||
![]() |
902041d4ee | ||
![]() |
cc34aef47e | ||
![]() |
0afbbe7c7a | ||
![]() |
8004553ba7 | ||
![]() |
0023b2846a | ||
![]() |
34775c1816 | ||
![]() |
e0759e704b | ||
![]() |
0aa225ca78 | ||
![]() |
b43b4ee5c0 | ||
![]() |
0cdb8cecbf | ||
![]() |
fd6a306742 | ||
![]() |
7f3b3d2277 | ||
![]() |
8be5b977bf | ||
![]() |
d7ddb15f9c | ||
![]() |
9a6a1798d0 | ||
![]() |
14196fd349 | ||
![]() |
941b89a523 | ||
![]() |
a5f9e5f8c0 | ||
![]() |
80c3356c8f |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -14,4 +14,5 @@ dist/
|
|||||||
|
|
||||||
# Build
|
# Build
|
||||||
*.db
|
*.db
|
||||||
checkVersion.sh
|
checkVersion.sh
|
||||||
|
bun.lockb
|
||||||
|
17
docs/changelogs/CHANGELOG.v1.6.6.md
Normal file
17
docs/changelogs/CHANGELOG.v1.6.6.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# v1.6.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 修复了一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
没有哦
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
@@ -1,2 +0,0 @@
|
|||||||
# 开始
|
|
||||||
jadx 跳转于 `com.tencent.qqnt.kernel.*`
|
|
@@ -1,42 +0,0 @@
|
|||||||
# Android
|
|
||||||
```java
|
|
||||||
GroupMemberExtReq groupMemberExtReq = new GroupMemberExtReq();
|
|
||||||
groupMemberExtReq.sourceType = MemberExtSourceType.TITLETYPE.ordinal();
|
|
||||||
groupMemberExtReq.groupCode = longOrNull.longValue();
|
|
||||||
groupMemberExtReq.beginUin = "0";
|
|
||||||
groupMemberExtReq.dataTime = "0";
|
|
||||||
Long[] lArr = new Long[1];
|
|
||||||
AppInterface a2 = dVar.a();
|
|
||||||
lArr[0] = Long.valueOf(a2 != null ? a2.getLongAccountUin() : 0L);
|
|
||||||
arrayListOf = CollectionsKt__CollectionsKt.arrayListOf(lArr);
|
|
||||||
groupMemberExtReq.uinList = arrayListOf;
|
|
||||||
MemberExtInfoFilter memberExtInfoFilter = new MemberExtInfoFilter();
|
|
||||||
memberExtInfoFilter.memberLevelInfoUin = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoPoint = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoActiveDay = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoLevel = 1;
|
|
||||||
memberExtInfoFilter.levelName = 1;
|
|
||||||
memberExtInfoFilter.dataTime = 1;
|
|
||||||
memberExtInfoFilter.sysShowFlag = 1;
|
|
||||||
memberExtInfoFilter.userShowFlag = 1;
|
|
||||||
memberExtInfoFilter.userShowFlagNew = 1;
|
|
||||||
memberExtInfoFilter.levelNameNew = 1;
|
|
||||||
Unit unit = Unit.INSTANCE;
|
|
||||||
groupMemberExtReq.memberExtFilter = memberExtInfoFilter;
|
|
||||||
troopLevelFrequencyControl.f(troopUin, new TroopListRepo$fetchTroopLevelInfo$2(b2, groupMemberExtReq, troopUin, new com.tencent.qqnt.troopmemberlist.report.c("fetchTroopLevelInfo")));
|
|
||||||
```
|
|
||||||
# Win
|
|
||||||
参数解析位于 sub_181456A10(24108) -> wrapper.node(24108)+1456A10
|
|
||||||
IGroupService.GetMemberExt(param: object);
|
|
||||||
param展开如下
|
|
||||||
```
|
|
||||||
groupCode string
|
|
||||||
beginUin string
|
|
||||||
dataTime string
|
|
||||||
uinList Array<string>
|
|
||||||
uinNum string
|
|
||||||
groupType string
|
|
||||||
richCardNameVer string
|
|
||||||
sourceType number
|
|
||||||
memberExtFilter object// 参数解析位于 sub_18145A6D0(24108) -> wrapper.node(24108)+145A6D0
|
|
||||||
```
|
|
16
docs/develop/NC 1.6.X的计划.md
Normal file
16
docs/develop/NC 1.6.X的计划.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 开发方向
|
||||||
|
方向一 NativeCall/Hook:
|
||||||
|
1. 崩溃检测机制的实现
|
||||||
|
2. Api_Caller 的Hook 可以拿到Event/Handler 进一步提升NC 即时的拦截与处理一些事件比如ReCall拦截
|
||||||
|
3. Node包装层 进一步分析,拿到脱离自带Listener/Adapter,可以拿到一些更加底层的数据变动 或许包括更多二进制数据
|
||||||
|
|
||||||
|
方向二 全新的无头启动 Way01
|
||||||
|
1. 基于Node启动原理,借助导出符号获取函数地址 再次还原NodeMain
|
||||||
|
|
||||||
|
方向三 发包与收包
|
||||||
|
1. 参考 方向一/3 大概可以收包
|
||||||
|
2. 发包 (暂时没有计划)
|
||||||
|
|
||||||
|
方向四 版本控制
|
||||||
|
1. 根据不同版本进行逻辑控制
|
||||||
|
2. 某些参数的自动提取
|
@@ -1,24 +0,0 @@
|
|||||||
# 前排提示
|
|
||||||
由于Core未处于开源,非组织人员无法参与Core开发,此处为Core开发提示
|
|
||||||
|
|
||||||
# 准备工具
|
|
||||||
frida ida-pro jadx x64dbg ce 内部调试脚本
|
|
||||||
|
|
||||||
## ida-pro
|
|
||||||
1. 用于快速分析入参和返回类型
|
|
||||||
2. 通过静态QLog推测语义
|
|
||||||
3. 提取Listener与Service (常用)
|
|
||||||
## frida
|
|
||||||
1. 用于动态获取QLog推测语义
|
|
||||||
2. 捕捉Native函数 实际入参与数据 分析中间流程
|
|
||||||
|
|
||||||
## jadx
|
|
||||||
1. 通过其它平台实现 静态获取QLog推测语义
|
|
||||||
2. 提供部分未调用代码 参考
|
|
||||||
|
|
||||||
## x64dbg
|
|
||||||
1. 验证IDA的Hook点
|
|
||||||
|
|
||||||
## 内部脚本
|
|
||||||
1. 提取Listener与Service (不调用无类型 不推荐)
|
|
||||||
2. 获取NT调用流程
|
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.6.5",
|
"version": "1.6.6",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch:dev": "vite --mode development",
|
"watch:dev": "vite --mode development",
|
||||||
"watch:prod": "vite --mode production",
|
"watch:prod": "vite --mode production",
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
"json-schema-to-ts": "^3.1.0",
|
"json-schema-to-ts": "^3.1.0",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"silk-wasm": "^3.3.4",
|
"silk-wasm": "^3.6.1",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.16.0"
|
||||||
|
3
script/NapCat.Way01.bat
Normal file
3
script/NapCat.Way01.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
REM 全新启动脚本 基于 Hook Native 预计版本1.6.0左右发布
|
||||||
|
@echo off
|
||||||
|
pause
|
18
script/napcat-9912-utf8.bat
Normal file
18
script/napcat-9912-utf8.bat
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
chcp 65001
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "QQPath=!pathWithoutUninstall!"
|
||||||
|
cd /d !QQPath!
|
||||||
|
echo !QQPath!
|
||||||
|
QQ.exe --enable-logging %*
|
41
script/napcat-9912-utf8.ps1
Normal file
41
script/napcat-9912-utf8.ps1
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location -Path $QQpath
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& ./QQ.exe --enable-logging $params}"
|
17
script/napcat-9912.bat
Normal file
17
script/napcat-9912.bat
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set QQPath=!pathWithoutUninstall!
|
||||||
|
cd /d !QQPath!
|
||||||
|
echo !QQPath!
|
||||||
|
QQ.exe --enable-logging %*
|
41
script/napcat-9912.ps1
Normal file
41
script/napcat-9912.ps1
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location -Path $QQpath
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& ./QQ.exe --enable-logging $params}"
|
@@ -1,7 +1,7 @@
|
|||||||
import { sleep } from '@/common/utils/helper';
|
import { sleep } from '@/common/utils/helper';
|
||||||
import { logError } from './log';
|
import { logError } from './log';
|
||||||
type AsyncQueueTask = (() => void) | (()=>Promise<void>);
|
type AsyncQueueTask = (() => void) | (()=>Promise<void>);
|
||||||
|
// 2024.7.13 废弃
|
||||||
|
|
||||||
export class AsyncQueue {
|
export class AsyncQueue {
|
||||||
private tasks: (AsyncQueueTask)[] = [];
|
private tasks: (AsyncQueueTask)[] = [];
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { logError, logDebug } from "@/common/utils/log";
|
import { logError, logDebug } from '@/common/utils/log';
|
||||||
|
|
||||||
type group_id = number;
|
type group_id = number;
|
||||||
type user_id = number;
|
type user_id = number;
|
||||||
@@ -21,7 +21,8 @@ class cacheNode<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cache<T> = { [key: group_id]: { [key: user_id]: cacheNode<T> } };
|
type cache<T, K = { [key: user_id]: cacheNode<T> }> = { [key: group_id]: K };
|
||||||
|
type removeObject<T> = cache<T, { userId: user_id, value: T }[]>
|
||||||
class LRU<T> {
|
class LRU<T> {
|
||||||
private maxAge: number;
|
private maxAge: number;
|
||||||
private maxSize: number;
|
private maxSize: number;
|
||||||
@@ -29,9 +30,9 @@ class LRU<T> {
|
|||||||
private cache: cache<T>;
|
private cache: cache<T>;
|
||||||
private head: cacheNode<T> | null = null;
|
private head: cacheNode<T> | null = null;
|
||||||
private tail: cacheNode<T> | null = null;
|
private tail: cacheNode<T> | null = null;
|
||||||
private onFuncs: ((node: cacheNode<T>) => void)[] = [];
|
private onFuncs: ((node: removeObject<T>) => void)[] = [];
|
||||||
|
|
||||||
constructor(maxAge: number = 6e4, maxSize: number = 5e3) {
|
constructor(maxAge: number = 6e4 * 3, maxSize: number = 1e4) {
|
||||||
this.maxAge = maxAge;
|
this.maxAge = maxAge;
|
||||||
this.maxSize = maxSize;
|
this.maxSize = maxSize;
|
||||||
this.cache = Object.create(null);
|
this.cache = Object.create(null);
|
||||||
@@ -44,7 +45,7 @@ class LRU<T> {
|
|||||||
// 移除LRU节点
|
// 移除LRU节点
|
||||||
private removeLRUNode(node: cacheNode<T>) {
|
private removeLRUNode(node: cacheNode<T>) {
|
||||||
logDebug(
|
logDebug(
|
||||||
"removeLRUNode",
|
'removeLRUNode',
|
||||||
node.groupId,
|
node.groupId,
|
||||||
node.userId,
|
node.userId,
|
||||||
node.value,
|
node.value,
|
||||||
@@ -53,46 +54,39 @@ class LRU<T> {
|
|||||||
node.prev = node.next = null;
|
node.prev = node.next = null;
|
||||||
delete this.cache[node.groupId][node.userId];
|
delete this.cache[node.groupId][node.userId];
|
||||||
this.removeNode(node);
|
this.removeNode(node);
|
||||||
this.onFuncs.forEach((func) => func(node));
|
this.onFuncs.forEach((func) => func({ [node.groupId]: [node] }));
|
||||||
this.currentSize--;
|
this.currentSize--;
|
||||||
}
|
}
|
||||||
|
|
||||||
public on(func: (node: cacheNode<T>) => void) {
|
public on(func: (node: removeObject<T>) => void) {
|
||||||
this.onFuncs.push(func);
|
this.onFuncs.push(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeExpired() {
|
private removeExpired() {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
let current = this.tail;
|
let current = this.tail;
|
||||||
const nodesToRemove: cacheNode<T>[] = [];
|
let totalNodeNum = 0;
|
||||||
let removedCount = 0;
|
|
||||||
|
const removeObject: cache<T, { userId: user_id, value: T }[]> = {};
|
||||||
|
|
||||||
// 收集需要删除的节点
|
|
||||||
while (current && now - current.timestamp > this.maxAge) {
|
while (current && now - current.timestamp > this.maxAge) {
|
||||||
nodesToRemove.push(current);
|
// 收集节点
|
||||||
|
if (!removeObject[current.groupId]) removeObject[current.groupId] = [];
|
||||||
|
removeObject[current.groupId].push({ userId: current.userId, value: current.value });
|
||||||
|
// 删除LRU节点
|
||||||
|
delete this.cache[current.groupId][current.userId];
|
||||||
current = current.prev;
|
current = current.prev;
|
||||||
removedCount++;
|
totalNodeNum++;
|
||||||
if (removedCount >= 100) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新链表指向
|
|
||||||
if (nodesToRemove.length > 0) {
|
|
||||||
const newTail = nodesToRemove[nodesToRemove.length - 1].prev;
|
|
||||||
if (newTail) {
|
|
||||||
newTail.next = null;
|
|
||||||
} else {
|
|
||||||
this.head = null;
|
|
||||||
}
|
|
||||||
this.tail = newTail;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesToRemove.forEach((node) => {
|
|
||||||
node.prev = node.next = null;
|
|
||||||
delete this.cache[node.groupId][node.userId];
|
|
||||||
|
|
||||||
this.currentSize--;
|
this.currentSize--;
|
||||||
this.onFuncs.forEach((func) => func(node));
|
}
|
||||||
});
|
|
||||||
|
if (!totalNodeNum) return;
|
||||||
|
|
||||||
|
// 跟新链表指向
|
||||||
|
if (current) { current.next = null; } else { this.head = null; }
|
||||||
|
this.tail = current;
|
||||||
|
|
||||||
|
this.onFuncs.forEach(func => func(removeObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
private addNode(node: cacheNode<T>) {
|
private addNode(node: cacheNode<T>) {
|
||||||
@@ -144,7 +138,7 @@ class LRU<T> {
|
|||||||
public get(groupId: group_id, userId: user_id): null | { userId: user_id; value: T };
|
public get(groupId: group_id, userId: user_id): null | { userId: user_id; value: T };
|
||||||
public get(groupId: group_id, userId?: user_id): any {
|
public get(groupId: group_id, userId?: user_id): any {
|
||||||
const groupObject = this.cache[groupId];
|
const groupObject = this.cache[groupId];
|
||||||
if(!groupObject) return userId === undefined ? [] : null;
|
if (!groupObject) return userId === undefined ? [] : null;
|
||||||
|
|
||||||
if (userId === undefined) {
|
if (userId === undefined) {
|
||||||
return Object.entries(groupObject).map(([userId, { value }]) => ({
|
return Object.entries(groupObject).map(([userId, { value }]) => ({
|
||||||
@@ -156,10 +150,12 @@ class LRU<T> {
|
|||||||
if (groupObject[userId]) {
|
if (groupObject[userId]) {
|
||||||
return { userId, value: groupObject[userId].value };
|
return { userId, value: groupObject[userId].value };
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default LRU;
|
export default LRU;
|
||||||
|
@@ -38,11 +38,11 @@ type QQVersionConfigInfo = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
||||||
'baseVersion': '9.9.11-24568',
|
'baseVersion': '9.9.12-25765',
|
||||||
'curVersion': '9.9.11-24568',
|
'curVersion': '9.9.12-25765',
|
||||||
'prevVersion': '',
|
'prevVersion': '',
|
||||||
'onErrorVersions': [],
|
'onErrorVersions': [],
|
||||||
'buildId': '24568'
|
'buildId': '25765'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fs.existsSync(configVersionInfoPath)) {
|
if (fs.existsSync(configVersionInfoPath)) {
|
||||||
@@ -55,23 +55,23 @@ if (fs.existsSync(configVersionInfoPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
||||||
//V1_WIN_NQ_9.9.11_24568_GW_B
|
//V1_WIN_NQ_9.9.12_25765_GW_B
|
||||||
export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toString());
|
export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toString());
|
||||||
// platform_type: 3,
|
// platform_type: 3,
|
||||||
// app_type: 4,
|
// app_type: 4,
|
||||||
// app_version: '9.9.9-23159',
|
// app_version: '9.9.12-25765',
|
||||||
// qua: 'V1_WIN_NQ_9.9.9_23159_GW_B',
|
// qua: 'V1_WIN_NQ_9.9.12_25765_GW_B',
|
||||||
// appid: '537213764',
|
// appid: '537234702',
|
||||||
// platVer: '10.0.26100',
|
// platVer: '10.0.26100',
|
||||||
// clientVer: '9.9.9-23159',
|
// clientVer: '9.9.9-25765',
|
||||||
|
|
||||||
// Linux
|
// Linux
|
||||||
// app_version: '3.2.9-24568',
|
// app_version: '3.2.9-25765',
|
||||||
// qua: 'V1_LNX_NQ_3.2.9_24568_GW_B',
|
// qua: 'V1_LNX_NQ_3.2.10_25765_GW_B',
|
||||||
|
|
||||||
let _appid: string = '537226369'; // 默认为 Windows 平台的 appid
|
let _appid: string = '537234702'; // 默认为 Windows 平台的 appid
|
||||||
if (systemPlatform === 'linux') {
|
if (systemPlatform === 'linux') {
|
||||||
_appid = '537226441';
|
_appid = '537234773';
|
||||||
}
|
}
|
||||||
// todo: mac 平台的 appid
|
// todo: mac 平台的 appid
|
||||||
export const appid = _appid;
|
export const appid = _appid;
|
@@ -1,5 +1,5 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { encode, getDuration, getWavFileInfo, isWav } from 'silk-wasm';
|
import { encode, getDuration, getWavFileInfo, isWav, isSilk } from 'silk-wasm';
|
||||||
import fsPromise from 'fs/promises';
|
import fsPromise from 'fs/promises';
|
||||||
import { log, logError } from './log';
|
import { log, logError } from './log';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
@@ -63,10 +63,11 @@ export async function encodeSilk(filePath: string) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const file = await fsPromise.readFile(filePath);
|
||||||
const pttPath = path.join(TEMP_DIR, uuidv4());
|
const pttPath = path.join(TEMP_DIR, uuidv4());
|
||||||
if (getFileHeader(filePath) !== '02232153494c4b') {
|
if (!isSilk(file)) {
|
||||||
log(`语音文件${filePath}需要转换成silk`);
|
log(`语音文件${filePath}需要转换成silk`);
|
||||||
const _isWav = await isWavFile(filePath);
|
const _isWav = isWav(file);
|
||||||
const pcmPath = pttPath + '.pcm';
|
const pcmPath = pttPath + '.pcm';
|
||||||
let sampleRate = 0;
|
let sampleRate = 0;
|
||||||
const convert = () => {
|
const convert = () => {
|
||||||
@@ -96,7 +97,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
if (!_isWav) {
|
if (!_isWav) {
|
||||||
input = await convert();
|
input = await convert();
|
||||||
} else {
|
} else {
|
||||||
input = fs.readFileSync(filePath);
|
input = file;
|
||||||
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
||||||
const { fmt } = getWavFileInfo(input);
|
const { fmt } = getWavFileInfo(input);
|
||||||
// log(`wav文件信息`, fmt)
|
// log(`wav文件信息`, fmt)
|
||||||
@@ -113,7 +114,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
duration: silk.duration / 1000
|
duration: silk.duration / 1000
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const silk = fs.readFileSync(filePath);
|
const silk = file;
|
||||||
let duration = 0;
|
let duration = 0;
|
||||||
try {
|
try {
|
||||||
duration = getDuration(silk) / 1000;
|
duration = getDuration(silk) / 1000;
|
||||||
|
@@ -72,7 +72,7 @@ class DBUtil extends DBUtilBase {
|
|||||||
private cache: { gid: number; uid: number }[] = [];
|
private cache: { gid: number; uid: number }[] = [];
|
||||||
private maxSize: number;
|
private maxSize: number;
|
||||||
|
|
||||||
constructor(maxSize: number = 5000) {
|
constructor(maxSize: number = 50000) {
|
||||||
this.maxSize = maxSize;
|
this.maxSize = maxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,57 +120,83 @@ class DBUtil extends DBUtilBase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.LURCache.on(async (node) => {
|
this.LURCache.on(async (nodeObject) => {
|
||||||
const { value: time, groupId, userId } = node;
|
|
||||||
|
|
||||||
logDebug('插入发言时间', userId, groupId);
|
Object.entries(nodeObject).forEach(async ([_groupId, datas]) => {
|
||||||
await this.createGroupInfoTimeTableIfNotExist(groupId);
|
const userIds = datas.map(v => v.userId);
|
||||||
|
const groupId = Number(_groupId);
|
||||||
|
logDebug('插入发言时间', _groupId);
|
||||||
|
|
||||||
const method = await this.getDataSetMethod(groupId, userId);
|
await this.createGroupInfoTimeTableIfNotExist(groupId);
|
||||||
logDebug('插入发言时间方法判断', userId, groupId, method);
|
|
||||||
|
|
||||||
const sql =
|
const needCreatUsers = await this.getNeedCreatList(groupId, userIds);
|
||||||
method == 'update'
|
const updateList = needCreatUsers.length > 0 ? datas.filter(user => !needCreatUsers.includes(user.userId)) : datas;
|
||||||
? `UPDATE "${groupId}" SET last_sent_time = ? WHERE user_id = ?`
|
const insertList = needCreatUsers.map(userId => datas.find(e => userId == e.userId)!);
|
||||||
: `INSERT INTO "${groupId}" (last_sent_time, user_id) VALUES (?, ?)`;
|
|
||||||
|
|
||||||
this.db!.all(sql, [time, userId], (err) => {
|
logDebug('updateList', updateList);
|
||||||
if (err) {
|
logDebug('insertList', insertList);
|
||||||
return logError('插入/更新发言时间失败', userId, groupId);
|
|
||||||
|
if (insertList.length) {
|
||||||
|
const insertSql = `INSERT INTO "${groupId}" (last_sent_time, user_id) VALUES ${insertList.map(() => '(?, ?)').join(', ')};`;
|
||||||
|
|
||||||
|
this.db!.all(insertSql, insertList.map(v => [v.value, v.userId]).flat(), err => {
|
||||||
|
if (err) {
|
||||||
|
logError(`群 ${groupId} 插入失败`);
|
||||||
|
logError(`更新Sql : ${insertSql}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
logDebug('插入/更新发言时间成功', userId, groupId);
|
|
||||||
|
if (updateList.length) {
|
||||||
|
const updateSql =
|
||||||
|
`UPDATE "${groupId}" SET last_sent_time = CASE ` +
|
||||||
|
updateList.map(v => `WHEN user_id = ${v.userId} THEN ${v.value}`).join(' ') +
|
||||||
|
' ELSE last_sent_time END WHERE user_id IN ' +
|
||||||
|
`(${updateList.map(v => v.userId).join(', ')});`;
|
||||||
|
|
||||||
|
this.db!.all(updateSql, [], err => {
|
||||||
|
if (err) {
|
||||||
|
logError(`群 ${groupId} 跟新失败`);
|
||||||
|
logError(`更新Sql : ${updateSql}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async getDataSetMethod(groupId: number, userId: number) {
|
async getNeedCreatList(groupId: number, userIds: number[]) {
|
||||||
// 缓存记录
|
|
||||||
if (this.LastSentCache.get(groupId, userId)) {
|
// 获取缓存中没有的
|
||||||
logDebug('缓存命中', userId, groupId);
|
const unhas = userIds.filter(userId => !this.LastSentCache.get(groupId, userId));
|
||||||
return 'update';
|
|
||||||
|
if (unhas.length == 0) {
|
||||||
|
logDebug('缓存全部命中');
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据库判断
|
logDebug('缓存未全部命中');
|
||||||
return new Promise<'insert' | 'update'>((resolve, reject) => {
|
|
||||||
this.db!.all(
|
|
||||||
`SELECT * FROM "${groupId}" WHERE user_id = ?`,
|
|
||||||
[userId],
|
|
||||||
(err, rows) => {
|
|
||||||
if (err) {
|
|
||||||
logError('查询发言时间存在失败', userId, groupId, err);
|
|
||||||
return logError('插入发言时间失败', userId, groupId, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows.length === 0) {
|
const sql = `SELECT * FROM "${groupId}" WHERE user_id IN (${unhas.map(() => '?').join(',')})`;
|
||||||
logDebug('查询发言时间不存在', userId, groupId);
|
|
||||||
return resolve('insert');
|
|
||||||
}
|
|
||||||
|
|
||||||
logDebug('查询发言时间存在', userId, groupId);
|
return new Promise<number[]>((resolve) => {
|
||||||
resolve('update');
|
this.db!.all(sql, unhas, (err, rows: { user_id: number }[]) => {
|
||||||
|
const has = rows.map(v => v.user_id);
|
||||||
|
const needCreatUsers = unhas.filter(userId => !has.includes(userId));
|
||||||
|
|
||||||
|
if (needCreatUsers.length == 0) {
|
||||||
|
logDebug('数据库全部命中');
|
||||||
|
} else {
|
||||||
|
logDebug('数据库未全部命中');
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
resolve(needCreatUsers);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
async createGroupInfoTimeTableIfNotExist(groupId: number) {
|
async createGroupInfoTimeTableIfNotExist(groupId: number) {
|
||||||
const createTableSQL = (groupId: number) =>
|
const createTableSQL = (groupId: number) =>
|
||||||
@@ -408,12 +434,12 @@ class DBUtil extends DBUtilBase {
|
|||||||
logDebug('读取发言时间', groupId);
|
logDebug('读取发言时间', groupId);
|
||||||
return new Promise<IRember[]>((resolve, reject) => {
|
return new Promise<IRember[]>((resolve, reject) => {
|
||||||
this.db!.all(`SELECT * FROM "${groupId}" `, (err, rows: IRember[]) => {
|
this.db!.all(`SELECT * FROM "${groupId}" `, (err, rows: IRember[]) => {
|
||||||
const cache = this.LURCache.get(groupId).map(e=>({user_id:e.userId, last_sent_time:e.value}));
|
const cache = this.LURCache.get(groupId).map(e => ({ user_id: e.userId, last_sent_time: e.value }));
|
||||||
if (err) {
|
if (err) {
|
||||||
logError('查询发言时间失败', groupId);
|
logError('查询发言时间失败', groupId);
|
||||||
return resolve(cache.map(e=>({...e, join_time:0})));
|
return resolve(cache.map(e => ({ ...e, join_time: 0 })));
|
||||||
}
|
}
|
||||||
Object.assign(rows, cache)
|
Object.assign(rows, cache);
|
||||||
logDebug('查询发言时间成功', groupId, rows);
|
logDebug('查询发言时间成功', groupId, rows);
|
||||||
resolve(rows);
|
resolve(rows);
|
||||||
});
|
});
|
||||||
|
@@ -7,7 +7,7 @@ export class RequestUtil {
|
|||||||
static async HttpsGetCookies(url: string): Promise<{ [key: string]: string }> {
|
static async HttpsGetCookies(url: string): Promise<{ [key: string]: string }> {
|
||||||
const client = url.startsWith('https') ? https : http;
|
const client = url.startsWith('https') ? https : http;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let req = client.get(url, (res) => {
|
const req = client.get(url, (res) => {
|
||||||
let cookies: { [key: string]: string } = {};
|
let cookies: { [key: string]: string } = {};
|
||||||
const handleRedirect = (res: http.IncomingMessage) => {
|
const handleRedirect = (res: http.IncomingMessage) => {
|
||||||
//console.log(res.headers.location);
|
//console.log(res.headers.location);
|
||||||
@@ -44,7 +44,7 @@ export class RequestUtil {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
req.on('error', (error: any) => {
|
req.on('error', (error: any) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -21,10 +21,7 @@ import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
|||||||
import { sessionConfig } from '@/core/sessionConfig';
|
import { sessionConfig } from '@/core/sessionConfig';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import { rkeyManager } from '../utils/rkey';
|
import { rkeyManager } from '../utils/rkey';
|
||||||
import { AsyncQueue } from '@/common/utils/AsyncQueue';
|
|
||||||
// const rkeyExpireTime = 1000;
|
|
||||||
|
|
||||||
const getRKeyTaskQueue = new AsyncQueue();
|
|
||||||
|
|
||||||
const downloadMediaTasks: Map<string, (arg: OnRichMediaDownloadCompleteParams) => void> = new Map<string, (arg: OnRichMediaDownloadCompleteParams) => void>();
|
const downloadMediaTasks: Map<string, (arg: OnRichMediaDownloadCompleteParams) => void> = new Map<string, (arg: OnRichMediaDownloadCompleteParams) => void>();
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType } from '../entities';
|
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType, GroupNotifyTypes } from '../entities';
|
||||||
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
|
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
import { logDebug } from '@/common/utils/log';
|
import { logDebug } from '@/common/utils/log';
|
||||||
@@ -31,6 +31,30 @@ export class NTQQGroupApi {
|
|||||||
static async DelGroupFileFolder(groupCode: string, folderId: string) {
|
static async DelGroupFileFolder(groupCode: string, folderId: string) {
|
||||||
return napCatCore.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
|
return napCatCore.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
|
||||||
}
|
}
|
||||||
|
static async addGroupEssence(GroupCode: string, msgId: string) {
|
||||||
|
// 代码没测过
|
||||||
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
let MsgData = await napCatCore.session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: GroupCode }, msgId, 1, false);
|
||||||
|
let param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||||
|
msgSeq: parseInt(MsgData.msgList[0].msgSeq)
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return napCatCore.session.getGroupService().addGroupEssence(param);
|
||||||
|
}
|
||||||
|
static async removeGroupEssence(GroupCode: string, msgId: string) {
|
||||||
|
// 代码没测过
|
||||||
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
let MsgData = await napCatCore.session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: GroupCode }, msgId, 1, false);
|
||||||
|
let param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||||
|
msgSeq: parseInt(MsgData.msgList[0].msgSeq)
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return napCatCore.session.getGroupService().removeGroupEssence(param);
|
||||||
|
}
|
||||||
static async getSingleScreenNotifies(num: number) {
|
static async getSingleScreenNotifies(num: number) {
|
||||||
let [_retData, _doubt, _seq, notifies] = await NTEventDispatch.CallNormalEvent
|
let [_retData, _doubt, _seq, notifies] = await NTEventDispatch.CallNormalEvent
|
||||||
<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
||||||
@@ -87,15 +111,20 @@ export class NTQQGroupApi {
|
|||||||
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
||||||
return napCatCore.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
|
return napCatCore.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
|
||||||
}
|
}
|
||||||
static async handleGroupRequest(notify: GroupNotify, operateType: GroupRequestOperateTypes, reason?: string) {
|
static async handleGroupRequest(flag: string, operateType: GroupRequestOperateTypes, reason?: string) {
|
||||||
|
let flagitem = flag.split('|');
|
||||||
|
let groupCode = flagitem[0];
|
||||||
|
let seq = flagitem[1];
|
||||||
|
let type = parseInt(flagitem[2]);
|
||||||
|
|
||||||
return napCatCore.session.getGroupService().operateSysNotify(
|
return napCatCore.session.getGroupService().operateSysNotify(
|
||||||
false,
|
false,
|
||||||
{
|
{
|
||||||
'operateType': operateType, // 2 拒绝
|
'operateType': operateType, // 2 拒绝
|
||||||
'targetMsg': {
|
'targetMsg': {
|
||||||
'seq': notify.seq, // 通知序列号
|
'seq': seq, // 通知序列号
|
||||||
'type': notify.type,
|
'type': type,
|
||||||
'groupCode': notify.group.groupCode,
|
'groupCode': groupCode,
|
||||||
'postscript': reason || ''
|
'postscript': reason || ''
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -181,12 +181,12 @@ export class NTQQUserApi {
|
|||||||
});
|
});
|
||||||
//uid = Array.from(friends.values()).find((t) => { t.uin == Uin })?.uid; // 从NC维护的QQ Buddy缓存 转换
|
//uid = Array.from(friends.values()).find((t) => { t.uin == Uin })?.uid; // 从NC维护的QQ Buddy缓存 转换
|
||||||
}
|
}
|
||||||
if (!uid) {
|
// if (!uid) {
|
||||||
uid = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uin == Uin })?.uid; //从QQ Native 缓存转换 方法一
|
// uid = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uin == Uin })?.uid; //从QQ Native 缓存转换 方法一
|
||||||
}
|
// }
|
||||||
if (!uid) {
|
// if (!uid) {
|
||||||
uid = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uin == Uin })?.uid; //从QQ Native 非缓存转换 方法二
|
// uid = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uin == Uin })?.uid; //从QQ Native 非缓存转换 方法二
|
||||||
}
|
// }
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
|
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
|
||||||
if (unveifyUid.indexOf("*") == -1) {
|
if (unveifyUid.indexOf("*") == -1) {
|
||||||
@@ -205,7 +205,26 @@ export class NTQQUserApi {
|
|||||||
5000,
|
5000,
|
||||||
[Uid]
|
[Uid]
|
||||||
);
|
);
|
||||||
return ret.uinInfo.get(Uid);
|
let uin = ret.uinInfo.get(Uid);
|
||||||
|
if (!uin) {
|
||||||
|
//从Buddy缓存获取Uin
|
||||||
|
Array.from(friends.values()).forEach((t) => {
|
||||||
|
if (t.uid == Uid) {
|
||||||
|
uin = t.uin;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!uin) {
|
||||||
|
uin = (await NTQQUserApi.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!uin) {
|
||||||
|
// uin = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 缓存转换
|
||||||
|
// }
|
||||||
|
// if (!uin) {
|
||||||
|
// uin = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 非缓存转换
|
||||||
|
// }
|
||||||
|
return uin;
|
||||||
}
|
}
|
||||||
static async getUserDetailInfoByUin(Uin: string) {
|
static async getUserDetailInfoByUin(Uin: string) {
|
||||||
return NTEventDispatch.CallNoListenerEvent
|
return NTEventDispatch.CallNoListenerEvent
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
type Friend,
|
type Friend,
|
||||||
type Group,
|
type Group,
|
||||||
type GroupMember, GroupNotify,
|
type GroupMember,
|
||||||
type SelfInfo,
|
type SelfInfo,
|
||||||
BuddyCategoryType
|
type BuddyCategoryType
|
||||||
} from './entities';
|
} from './entities';
|
||||||
import { isNumeric } from '@/common/utils/helper';
|
import { isNumeric } from '@/common/utils/helper';
|
||||||
import { NTQQGroupApi } from '@/core/apis';
|
import { NTQQGroupApi } from '@/core/apis';
|
||||||
@@ -30,7 +30,6 @@ export const groupMembers: Map<string, Map<string, GroupMember>> = new Map<strin
|
|||||||
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
||||||
export const rawFriends: Array<BuddyCategoryType> = []; // 带分组的好友列表
|
export const rawFriends: Array<BuddyCategoryType> = []; // 带分组的好友列表
|
||||||
|
|
||||||
export const groupNotifies: Record<string, GroupNotify> = {}; // flag->GroupNotify
|
|
||||||
export async function getGroup(qq: string | number): Promise<Group | undefined> {
|
export async function getGroup(qq: string | number): Promise<Group | undefined> {
|
||||||
let group = groups.get(qq.toString());
|
let group = groups.get(qq.toString());
|
||||||
if (!group) {
|
if (!group) {
|
||||||
|
@@ -8,6 +8,7 @@ import {
|
|||||||
SendPicElement,
|
SendPicElement,
|
||||||
SendPttElement,
|
SendPttElement,
|
||||||
SendReplyElement,
|
SendReplyElement,
|
||||||
|
sendShareLocationElement,
|
||||||
SendTextElement,
|
SendTextElement,
|
||||||
SendVideoElement
|
SendVideoElement
|
||||||
} from './index';
|
} from './index';
|
||||||
@@ -27,6 +28,16 @@ export const mFaceCache = new Map<string, string>(); // emojiId -> faceName
|
|||||||
|
|
||||||
|
|
||||||
export class SendMsgElementConstructor {
|
export class SendMsgElementConstructor {
|
||||||
|
static location(): sendShareLocationElement {
|
||||||
|
return {
|
||||||
|
elementType: ElementType.SHARELOCATION,
|
||||||
|
elementId: '',
|
||||||
|
shareLocationElement: {
|
||||||
|
text: "测试",
|
||||||
|
ext: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
static text(content: string): SendTextElement {
|
static text(content: string): SendTextElement {
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.TEXT,
|
elementType: ElementType.TEXT,
|
||||||
|
@@ -23,6 +23,7 @@ export interface GetFileListParam {
|
|||||||
showOnlinedocFolder: number
|
showOnlinedocFolder: number
|
||||||
}
|
}
|
||||||
export enum ElementType {
|
export enum ElementType {
|
||||||
|
UNKNOWN = 0,
|
||||||
TEXT = 1,
|
TEXT = 1,
|
||||||
PIC = 2,
|
PIC = 2,
|
||||||
FILE = 3,
|
FILE = 3,
|
||||||
@@ -30,11 +31,182 @@ export enum ElementType {
|
|||||||
VIDEO = 5,
|
VIDEO = 5,
|
||||||
FACE = 6,
|
FACE = 6,
|
||||||
REPLY = 7,
|
REPLY = 7,
|
||||||
|
WALLET = 9,
|
||||||
|
GreyTip = 8,//Poke别叫戳一搓了 官方名字拍一拍 戳一戳是另一个名字
|
||||||
ARK = 10,
|
ARK = 10,
|
||||||
MFACE = 11,
|
MFACE = 11,
|
||||||
MARKDOWN = 14
|
LIVEGIFT = 12,
|
||||||
|
STRUCTLONGMSG = 13,
|
||||||
|
MARKDOWN = 14,
|
||||||
|
GIPHY = 15,
|
||||||
|
MULTIFORWARD = 16,
|
||||||
|
INLINEKEYBOARD = 17,
|
||||||
|
INTEXTGIFT = 18,
|
||||||
|
CALENDAR = 19,
|
||||||
|
YOLOGAMERESULT = 20,
|
||||||
|
AVRECORD = 21,
|
||||||
|
FEED = 22,
|
||||||
|
TOFURECORD = 23,
|
||||||
|
ACEBUBBLE = 24,
|
||||||
|
ACTIVITY = 25,
|
||||||
|
TOFU = 26,
|
||||||
|
FACEBUBBLE = 27,
|
||||||
|
SHARELOCATION = 28,
|
||||||
|
TASKTOPMSG = 29,
|
||||||
|
RECOMMENDEDMSG = 43,
|
||||||
|
ACTIONBAR = 44
|
||||||
}
|
}
|
||||||
|
export interface SendActionBarElement {
|
||||||
|
elementType: ElementType.ACTIONBAR;
|
||||||
|
elementId: string;
|
||||||
|
actionBarElement: {
|
||||||
|
rows: InlineKeyboardRow[];
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendRecommendedMsgElement {
|
||||||
|
elementType: ElementType.RECOMMENDEDMSG;
|
||||||
|
elementId: string;
|
||||||
|
recommendedMsgElement: {
|
||||||
|
rows: InlineKeyboardRow[];
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface InlineKeyboardButton {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
visitedLabel: string;
|
||||||
|
unsupportTips: string;
|
||||||
|
data: string;
|
||||||
|
specifyRoleIds: string[];
|
||||||
|
specifyTinyids: string[];
|
||||||
|
style: number;
|
||||||
|
type: number;
|
||||||
|
clickLimit: number;
|
||||||
|
atBotShowChannelList: boolean;
|
||||||
|
permissionType: number;
|
||||||
|
}
|
||||||
|
export interface InlineKeyboardRow {
|
||||||
|
buttons: InlineKeyboardButton[];
|
||||||
|
}
|
||||||
|
export interface TofuElementContent {
|
||||||
|
color: string;
|
||||||
|
tittle: string;
|
||||||
|
}
|
||||||
|
export interface SendTaskTopMsgElement {
|
||||||
|
elementType: ElementType.TASKTOPMSG;
|
||||||
|
elementId: string;
|
||||||
|
taskTopMsgElement: {
|
||||||
|
msgTitle: string;
|
||||||
|
msgSummary: string;
|
||||||
|
iconUrl: string;
|
||||||
|
topMsgType: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendTofuRecordElement {
|
||||||
|
elementType: ElementType.TOFURECORD;
|
||||||
|
elementId: string;
|
||||||
|
tofuRecordElement: {
|
||||||
|
type: number;
|
||||||
|
busiid: string;
|
||||||
|
busiuuid: string;
|
||||||
|
descriptionContent: string;
|
||||||
|
contentlist: TofuElementContent[],
|
||||||
|
background: string;
|
||||||
|
icon: string;
|
||||||
|
uinlist: string[],
|
||||||
|
uidlist: string[],
|
||||||
|
busiExtra: string;
|
||||||
|
updateTime: string;
|
||||||
|
dependedmsgid: string;
|
||||||
|
msgtime: string;
|
||||||
|
onscreennotify: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendFaceBubbleElement {
|
||||||
|
elementType: ElementType.FACEBUBBLE;
|
||||||
|
elementId: string;
|
||||||
|
faceBubbleElement: {
|
||||||
|
faceCount: number;
|
||||||
|
faceSummary: string;
|
||||||
|
faceFlag: number;
|
||||||
|
content: string;
|
||||||
|
oldVersionStr: string;
|
||||||
|
faceType: number;
|
||||||
|
others: string;
|
||||||
|
yellowFaceInfo: {
|
||||||
|
index: number;
|
||||||
|
buf: string;
|
||||||
|
compatibleText: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface SendavRecordElement {
|
||||||
|
elementType: ElementType.AVRECORD;
|
||||||
|
elementId: string;
|
||||||
|
avRecordElement: {
|
||||||
|
type: number;
|
||||||
|
time: string;
|
||||||
|
text: string;
|
||||||
|
mainType: number;
|
||||||
|
hasRead: boolean;
|
||||||
|
extraType: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface YoloUserInfo {
|
||||||
|
uid: string;
|
||||||
|
result: number;
|
||||||
|
rank: number;
|
||||||
|
bizId: string
|
||||||
|
}
|
||||||
|
export interface SendInlineKeyboardElement {
|
||||||
|
elementType: ElementType.INLINEKEYBOARD;
|
||||||
|
elementId: string;
|
||||||
|
inlineKeyboardElement: {
|
||||||
|
rows: number;
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface SendYoloGameResultElement {
|
||||||
|
elementType: ElementType.YOLOGAMERESULT;
|
||||||
|
yoloGameResultElement: {
|
||||||
|
UserInfo: YoloUserInfo[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendGiphyElement {
|
||||||
|
elementType: ElementType.GIPHY;
|
||||||
|
elementId: string;
|
||||||
|
giphyElement: {
|
||||||
|
id: string;
|
||||||
|
isClip: boolean;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendWalletElement {
|
||||||
|
elementType: ElementType.UNKNOWN;//不做 设置位置
|
||||||
|
elementId: string;
|
||||||
|
walletElement: {}
|
||||||
|
}
|
||||||
|
export interface SendCalendarElement {
|
||||||
|
elementType: ElementType.CALENDAR;
|
||||||
|
elementId: string;
|
||||||
|
calendarElement: {
|
||||||
|
summary: string;
|
||||||
|
msg: string;
|
||||||
|
expireTimeMs: string;
|
||||||
|
schemaType: number;
|
||||||
|
schema: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendliveGiftElement {
|
||||||
|
elementType: ElementType.LIVEGIFT;
|
||||||
|
elementId: string;
|
||||||
|
liveGiftElement: {}
|
||||||
|
}
|
||||||
export interface SendTextElement {
|
export interface SendTextElement {
|
||||||
elementType: ElementType.TEXT;
|
elementType: ElementType.TEXT;
|
||||||
elementId: string;
|
elementId: string;
|
||||||
@@ -118,6 +290,30 @@ export interface SendMarketFaceElement {
|
|||||||
elementType: ElementType.MFACE;
|
elementType: ElementType.MFACE;
|
||||||
marketFaceElement: MarketFaceElement;
|
marketFaceElement: MarketFaceElement;
|
||||||
}
|
}
|
||||||
|
export interface SendstructLongMsgElement {
|
||||||
|
elementType: ElementType.STRUCTLONGMSG;
|
||||||
|
elementId: string;
|
||||||
|
structLongMsgElement: {
|
||||||
|
xmlContent: string;
|
||||||
|
resId: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendactionBarElement {
|
||||||
|
elementType: ElementType.ACTIONBAR;
|
||||||
|
elementId: string;
|
||||||
|
actionBarElement: {
|
||||||
|
rows: number;
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface sendShareLocationElement {
|
||||||
|
elementType: ElementType.SHARELOCATION;
|
||||||
|
elementId: string;
|
||||||
|
shareLocationElement: {
|
||||||
|
text: string;
|
||||||
|
ext: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface FileElement {
|
export interface FileElement {
|
||||||
fileMd5?: string;
|
fileMd5?: string;
|
||||||
@@ -162,7 +358,7 @@ export interface SendMarkdownElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type SendMessageElement = SendTextElement | SendPttElement |
|
export type SendMessageElement = SendTextElement | SendPttElement |
|
||||||
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement | SendVideoElement | SendArkElement | SendMarkdownElement
|
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement | SendVideoElement | SendArkElement | SendMarkdownElement | sendShareLocationElement
|
||||||
|
|
||||||
export enum AtType {
|
export enum AtType {
|
||||||
notAt = 0,
|
notAt = 0,
|
||||||
@@ -289,6 +485,7 @@ export interface GrayTipElement {
|
|||||||
templId: string;
|
templId: string;
|
||||||
};
|
};
|
||||||
jsonGrayTipElement: {
|
jsonGrayTipElement: {
|
||||||
|
busiId?: number;
|
||||||
jsonStr: string;
|
jsonStr: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -490,6 +687,7 @@ export interface MultiForwardMsgElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RawMessage {
|
export interface RawMessage {
|
||||||
|
msgRandom: string;
|
||||||
// int32, 自己维护的消息id
|
// int32, 自己维护的消息id
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
|
@@ -149,5 +149,18 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
//需要提前判断是否存在 高版本新增
|
||||||
|
addGroupEssence(param: {
|
||||||
|
groupCode: string
|
||||||
|
msgRandom: number,
|
||||||
|
msgSeq: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
//需要提前判断是否存在 高版本新增
|
||||||
|
removeGroupEssence(param: {
|
||||||
|
groupCode: string
|
||||||
|
msgRandom: number,
|
||||||
|
msgSeq: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
isNull(): boolean;
|
isNull(): boolean;
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ import BaseAction from '../BaseAction';
|
|||||||
import { GroupRequestOperateTypes } from '@/core/entities';
|
import { GroupRequestOperateTypes } from '@/core/entities';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { NTQQGroupApi } from '@/core/apis/group';
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
import { groupNotifies } from '@/core/data';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
@@ -23,11 +22,7 @@ export default class SetGroupAddRequest extends BaseAction<Payload, null> {
|
|||||||
protected async _handle(payload: Payload): Promise<null> {
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
const flag = payload.flag.toString();
|
const flag = payload.flag.toString();
|
||||||
const approve = payload.approve?.toString() !== 'false';
|
const approve = payload.approve?.toString() !== 'false';
|
||||||
const notify = groupNotifies[flag];
|
await NTQQGroupApi.handleGroupRequest(flag,
|
||||||
if (!notify) {
|
|
||||||
throw `${flag}对应的加群通知不存在`;
|
|
||||||
}
|
|
||||||
await NTQQGroupApi.handleGroupRequest(notify,
|
|
||||||
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
||||||
payload.reason
|
payload.reason
|
||||||
);
|
);
|
||||||
|
@@ -209,6 +209,10 @@ const _handlers: {
|
|||||||
[OB11MessageDataType.xml]: () => undefined,
|
[OB11MessageDataType.xml]: () => undefined,
|
||||||
|
|
||||||
[OB11MessageDataType.poke]: () => undefined,
|
[OB11MessageDataType.poke]: () => undefined,
|
||||||
|
|
||||||
|
[OB11MessageDataType.Location]: async () => {
|
||||||
|
return SendMsgElementConstructor.location();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlers = <{
|
const handlers = <{
|
||||||
|
@@ -145,7 +145,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
protected async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
||||||
let { peer, group } = await createContext(payload, this.contextMode);
|
const { peer, group } = await createContext(payload, this.contextMode);
|
||||||
|
|
||||||
const messages = normalize(
|
const messages = normalize(
|
||||||
payload.message,
|
payload.message,
|
||||||
|
@@ -43,6 +43,8 @@ import { deleteGroup, getGroupMember, groupMembers, selfInfo, tempGroupCodeMap }
|
|||||||
import { NTQQFileApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
import { NTQQFileApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
import { OB11GroupMsgEmojiLikeEvent } from '@/onebot11/event/notice/OB11MsgEmojiLikeEvent';
|
import { OB11GroupMsgEmojiLikeEvent } from '@/onebot11/event/notice/OB11MsgEmojiLikeEvent';
|
||||||
import { napCatCore } from '@/core';
|
import { napCatCore } from '@/core';
|
||||||
|
import { OB11FriendPokeEvent, OB11GroupPokeEvent } from './event/notice/OB11PokeEvent';
|
||||||
|
import { OB11BaseNoticeEvent } from './event/notice/OB11BaseNoticeEvent';
|
||||||
|
|
||||||
|
|
||||||
export class OB11Constructor {
|
export class OB11Constructor {
|
||||||
@@ -162,7 +164,7 @@ export class OB11Constructor {
|
|||||||
message_data['type'] = 'image';
|
message_data['type'] = 'image';
|
||||||
// message_data["data"]["file"] = element.picElement.sourcePath
|
// message_data["data"]["file"] = element.picElement.sourcePath
|
||||||
message_data['data']['file'] = element.picElement.fileName;
|
message_data['data']['file'] = element.picElement.fileName;
|
||||||
message_data['subType']= element.picElement.picSubType;
|
message_data['subType'] = element.picElement.picSubType;
|
||||||
// message_data["data"]["path"] = element.picElement.sourcePath
|
// message_data["data"]["path"] = element.picElement.sourcePath
|
||||||
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
||||||
|
|
||||||
@@ -285,12 +287,36 @@ export class OB11Constructor {
|
|||||||
resMsg.raw_message = resMsg.raw_message.trim();
|
resMsg.raw_message = resMsg.raw_message.trim();
|
||||||
return resMsg;
|
return resMsg;
|
||||||
}
|
}
|
||||||
|
static async PrivateEvent(msg: RawMessage): Promise<OB11BaseNoticeEvent | undefined> {
|
||||||
|
if (msg.chatType !== ChatType.friend) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const element of msg.elements) {
|
||||||
|
if (element.grayTipElement) {
|
||||||
|
if (element.grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
|
||||||
|
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr);
|
||||||
|
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||||
|
//判断业务类型
|
||||||
|
//Poke事件
|
||||||
|
let pokedetail: any[] = json.items;
|
||||||
|
//筛选item带有uid的元素
|
||||||
|
pokedetail = pokedetail.filter(item => item.uid);
|
||||||
|
//console.log("[NapCat] 群拍一拍 群:", pokedetail, parseInt(msg.peerUid), " ", await NTQQUserApi.getUinByUid(pokedetail[0].uid), "拍了拍", await NTQQUserApi.getUinByUid(pokedetail[1].uid));
|
||||||
|
if (pokedetail.length == 2) {
|
||||||
|
return new OB11FriendPokeEvent(parseInt((await NTQQUserApi.getUinByUid(pokedetail[0].uid))!), parseInt((await NTQQUserApi.getUinByUid(pokedetail[1].uid))!));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
static async GroupEvent(msg: RawMessage): Promise<OB11GroupNoticeEvent | undefined> {
|
static async GroupEvent(msg: RawMessage): Promise<OB11GroupNoticeEvent | undefined> {
|
||||||
|
//log("group msg", msg);
|
||||||
if (msg.chatType !== ChatType.group) {
|
if (msg.chatType !== ChatType.group) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.senderUin) {
|
if (msg.senderUin && msg.senderUin !== '0') {
|
||||||
const member = await getGroupMember(msg.peerUid, msg.senderUin);
|
const member = await getGroupMember(msg.peerUid, msg.senderUin);
|
||||||
if (member && member.cardName !== msg.sendMemberName) {
|
if (member && member.cardName !== msg.sendMemberName) {
|
||||||
const newCardName = msg.sendMemberName || '';
|
const newCardName = msg.sendMemberName || '';
|
||||||
@@ -299,7 +325,6 @@ export class OB11Constructor {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// log("group msg", msg);
|
|
||||||
for (const element of msg.elements) {
|
for (const element of msg.elements) {
|
||||||
const grayTipElement = element.grayTipElement;
|
const grayTipElement = element.grayTipElement;
|
||||||
const groupElement = grayTipElement?.groupElement;
|
const groupElement = grayTipElement?.groupElement;
|
||||||
@@ -369,11 +394,8 @@ export class OB11Constructor {
|
|||||||
busid: element.fileElement.fileBizId || 0
|
busid: element.fileElement.fileBizId || 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grayTipElement) {
|
if (grayTipElement) {
|
||||||
const xmlElement = grayTipElement.xmlElement;
|
if (grayTipElement.xmlElement?.templId === '10382') {
|
||||||
|
|
||||||
if (xmlElement?.templId === '10382') {
|
|
||||||
// 表情回应消息
|
// 表情回应消息
|
||||||
// "content":
|
// "content":
|
||||||
// "<gtip align=\"center\">
|
// "<gtip align=\"center\">
|
||||||
@@ -385,7 +407,7 @@ export class OB11Constructor {
|
|||||||
const emojiLikeData = new fastXmlParser.XMLParser({
|
const emojiLikeData = new fastXmlParser.XMLParser({
|
||||||
ignoreAttributes: false,
|
ignoreAttributes: false,
|
||||||
attributeNamePrefix: ''
|
attributeNamePrefix: ''
|
||||||
}).parse(xmlElement.content);
|
}).parse(grayTipElement.xmlElement.content);
|
||||||
logDebug('收到表情回应我的消息', emojiLikeData);
|
logDebug('收到表情回应我的消息', emojiLikeData);
|
||||||
try {
|
try {
|
||||||
const senderUin = emojiLikeData.gtip.qq.jp;
|
const senderUin = emojiLikeData.gtip.qq.jp;
|
||||||
@@ -422,6 +444,7 @@ export class OB11Constructor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE
|
||||||
else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
|
else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
|
||||||
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
|
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
|
||||||
/*
|
/*
|
||||||
@@ -448,6 +471,18 @@ export class OB11Constructor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
* */
|
* */
|
||||||
|
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||||
|
//判断业务类型
|
||||||
|
//Poke事件
|
||||||
|
let pokedetail: any[] = json.items;
|
||||||
|
//筛选item带有uid的元素
|
||||||
|
pokedetail = pokedetail.filter(item => item.uid);
|
||||||
|
//console.log("[NapCat] 群拍一拍 群:", pokedetail, parseInt(msg.peerUid), " ", await NTQQUserApi.getUinByUid(pokedetail[0].uid), "拍了拍", await NTQQUserApi.getUinByUid(pokedetail[1].uid));
|
||||||
|
if (pokedetail.length == 2) {
|
||||||
|
return new OB11GroupPokeEvent(parseInt(msg.peerUid), parseInt((await NTQQUserApi.getUinByUid(pokedetail[0].uid))!), parseInt((await NTQQUserApi.getUinByUid(pokedetail[1].uid))!));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
|
||||||
const memberUin = json.items[1].param[0];
|
const memberUin = json.items[1].param[0];
|
||||||
const title = json.items[3].txt;
|
const title = json.items[3].txt;
|
||||||
logDebug('收到群成员新头衔消息', json);
|
logDebug('收到群成员新头衔消息', json);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
||||||
|
|
||||||
export abstract class OB11GroupNoticeEvent extends OB11BaseNoticeEvent {
|
export abstract class OB11GroupNoticeEvent extends OB11BaseNoticeEvent {
|
||||||
group_id: number;
|
group_id: number = 0;
|
||||||
user_id: number;
|
user_id: number = 0;
|
||||||
}
|
}
|
@@ -2,29 +2,28 @@ import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
|||||||
import { selfInfo } from '@/core/data';
|
import { selfInfo } from '@/core/data';
|
||||||
import { OB11BaseEvent } from '../OB11BaseEvent';
|
import { OB11BaseEvent } from '../OB11BaseEvent';
|
||||||
|
|
||||||
class OB11PokeEvent extends OB11BaseNoticeEvent{
|
class OB11PokeEvent extends OB11BaseNoticeEvent {
|
||||||
notice_type = 'notify';
|
notice_type = 'notify';
|
||||||
sub_type = 'poke';
|
sub_type = 'poke';
|
||||||
target_id = parseInt(selfInfo.uin);
|
target_id = 0;
|
||||||
user_id: number;
|
user_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OB11FriendPokeEvent extends OB11PokeEvent{
|
export class OB11FriendPokeEvent extends OB11PokeEvent {
|
||||||
sender_id: number;
|
constructor(user_id: number, target_id: number) {
|
||||||
constructor(user_id: number) {
|
|
||||||
super();
|
super();
|
||||||
|
this.target_id = target_id;
|
||||||
this.user_id = user_id;
|
this.user_id = user_id;
|
||||||
this.sender_id = user_id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OB11GroupPokeEvent extends OB11PokeEvent{
|
export class OB11GroupPokeEvent extends OB11PokeEvent {
|
||||||
group_id: number;
|
group_id: number;
|
||||||
|
|
||||||
constructor(group_id: number, user_id: number=0) {
|
constructor(group_id: number, user_id: number = 0, target_id: number = 0,) {
|
||||||
super();
|
super();
|
||||||
this.group_id = group_id;
|
this.group_id = group_id;
|
||||||
this.target_id = user_id;
|
this.target_id = target_id;
|
||||||
this.user_id = user_id;
|
this.user_id = user_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,7 @@ import { OB11Config, ob11Config } from '@/onebot11/config';
|
|||||||
import { httpHeart, ob11HTTPServer } from '@/onebot11/server/http';
|
import { httpHeart, ob11HTTPServer } from '@/onebot11/server/http';
|
||||||
import { ob11WebsocketServer } from '@/onebot11/server/ws/WebsocketServer';
|
import { ob11WebsocketServer } from '@/onebot11/server/ws/WebsocketServer';
|
||||||
import { ob11ReverseWebsockets } from '@/onebot11/server/ws/ReverseWebsocket';
|
import { ob11ReverseWebsockets } from '@/onebot11/server/ws/ReverseWebsocket';
|
||||||
import { getGroup, getGroupMember, groupNotifies, selfInfo, tempGroupCodeMap } from '@/core/data';
|
import { getGroup, getGroupMember, selfInfo, tempGroupCodeMap } from '@/core/data';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
import { dbUtil } from '@/common/utils/db';
|
||||||
import { BuddyListener, GroupListener, NodeIKernelBuddyListener } from '@/core/listeners';
|
import { BuddyListener, GroupListener, NodeIKernelBuddyListener } from '@/core/listeners';
|
||||||
import { OB11FriendRequestEvent } from '@/onebot11/event/request/OB11FriendRequest';
|
import { OB11FriendRequestEvent } from '@/onebot11/event/request/OB11FriendRequest';
|
||||||
@@ -37,8 +37,6 @@ import { Data as SysData } from '@/proto/SysMessage';
|
|||||||
import { Data as DeviceData } from '@/proto/SysMessage.DeviceChange';
|
import { Data as DeviceData } from '@/proto/SysMessage.DeviceChange';
|
||||||
import { OB11FriendPokeEvent, OB11GroupPokeEvent } from './event/notice/OB11PokeEvent';
|
import { OB11FriendPokeEvent, OB11GroupPokeEvent } from './event/notice/OB11PokeEvent';
|
||||||
import { isEqual } from '@/common/utils/helper';
|
import { isEqual } from '@/common/utils/helper';
|
||||||
import { MiniAppUtil } from '@/common/utils/Packet';
|
|
||||||
import { RequestUtil } from '@/common/utils/request';
|
|
||||||
|
|
||||||
//下面几个其实应该移进Core-Data 缓存实现 但是现在在这里方便
|
//下面几个其实应该移进Core-Data 缓存实现 但是现在在这里方便
|
||||||
//
|
//
|
||||||
@@ -50,7 +48,7 @@ export interface LineDevice {
|
|||||||
export let DeviceList = new Array<LineDevice>();
|
export let DeviceList = new Array<LineDevice>();
|
||||||
|
|
||||||
//peer->cached(boolen)
|
//peer->cached(boolen)
|
||||||
const PokeCache = new Map<string, boolean>();
|
// const PokeCache = new Map<string, boolean>();
|
||||||
|
|
||||||
export class NapCatOnebot11 {
|
export class NapCatOnebot11 {
|
||||||
private bootTime: number = Date.now() / 1000; // 秒
|
private bootTime: number = Date.now() / 1000; // 秒
|
||||||
@@ -116,38 +114,39 @@ export class NapCatOnebot11 {
|
|||||||
try {
|
try {
|
||||||
// 生产环境会自己去掉
|
// 生产环境会自己去掉
|
||||||
const hex = buf2hex(Buffer.from(protobufData));
|
const hex = buf2hex(Buffer.from(protobufData));
|
||||||
|
//console.log(hex);
|
||||||
const sysMsg = SysData.fromBinary(Buffer.from(protobufData));
|
const sysMsg = SysData.fromBinary(Buffer.from(protobufData));
|
||||||
const peeruin = sysMsg.header[0].peerNumber;
|
const peeruin = sysMsg.header[0].peerNumber;
|
||||||
const peeruid = sysMsg.header[0].peerString;
|
const peeruid = sysMsg.header[0].peerString;
|
||||||
const MsgType = sysMsg.body[0].msgType;
|
const MsgType = sysMsg.body[0].msgType;
|
||||||
const subType0 = sysMsg.body[0].subType0;
|
const subType0 = sysMsg.body[0].subType0;
|
||||||
const subType1 = sysMsg.body[0].subType1;
|
const subType1 = sysMsg.body[0].subType1;
|
||||||
let pokeEvent: OB11FriendPokeEvent | OB11GroupPokeEvent;
|
// let pokeEvent: OB11FriendPokeEvent | OB11GroupPokeEvent;
|
||||||
//console.log(peeruid);
|
// //console.log(peeruid);
|
||||||
if (MsgType == 528 && subType0 == 290 && hex.length < 250 && hex.endsWith('04')) {
|
// if (MsgType == 528 && subType0 == 290 && hex.length < 250 && hex.endsWith('04')) {
|
||||||
// 防止上报两次 私聊戳一戳
|
// // 防止上报两次 私聊戳一戳
|
||||||
if (PokeCache.has(peeruid)) {
|
// if (PokeCache.has(peeruid)) {
|
||||||
log('[私聊] 用户 ', peeruin, ' 对你戳一戳');
|
// //log('[私聊] 用户 ', peeruin, ' 对你戳一戳');
|
||||||
pokeEvent = new OB11FriendPokeEvent(peeruin);
|
// pokeEvent = new OB11FriendPokeEvent(parseInt(selfInfo.uin), peeruin);
|
||||||
postOB11Event(pokeEvent);
|
// postOB11Event(pokeEvent);
|
||||||
}
|
// }
|
||||||
PokeCache.set(peeruid, false);
|
// PokeCache.set(peeruid, false);
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
PokeCache.delete(peeruid);
|
// PokeCache.delete(peeruid);
|
||||||
}, 1000);
|
// }, 1000);
|
||||||
}
|
// }
|
||||||
if (MsgType == 732 && subType0 == 20 && hex.length < 150 && hex.endsWith('04')) {
|
// if (MsgType == 732 && subType0 == 20 && hex.length < 150 && hex.endsWith('04')) {
|
||||||
// 防止上报两次 群聊戳一戳
|
// // 防止上报两次 群聊戳一戳
|
||||||
if (PokeCache.has(peeruid)) {
|
// if (PokeCache.has(peeruid)) {
|
||||||
log('[群聊] 群组 ', peeruin, ' 戳一戳');
|
// log('[群聊] 群组 ', peeruin, ' 戳一戳');
|
||||||
pokeEvent = new OB11GroupPokeEvent(peeruin);
|
// pokeEvent = new OB11GroupPokeEvent(peeruin);
|
||||||
postOB11Event(pokeEvent);
|
// postOB11Event(pokeEvent);
|
||||||
}
|
// }
|
||||||
PokeCache.set(peeruid, false);
|
// PokeCache.set(peeruid, false);
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
PokeCache.delete(peeruid);
|
// PokeCache.delete(peeruid);
|
||||||
}, 1000);
|
// }, 1000);
|
||||||
}
|
// }
|
||||||
if (MsgType == 528 && subType0 == 349) {
|
if (MsgType == 528 && subType0 == 349) {
|
||||||
const sysDeviceMsg = DeviceData.fromBinary(Buffer.from(protobufData));
|
const sysDeviceMsg = DeviceData.fromBinary(Buffer.from(protobufData));
|
||||||
DeviceList = [];
|
DeviceList = [];
|
||||||
@@ -332,12 +331,21 @@ export class NapCatOnebot11 {
|
|||||||
postOB11Event(msg);
|
postOB11Event(msg);
|
||||||
// log("post msg", msg)
|
// log("post msg", msg)
|
||||||
}).catch(e => logError('constructMessage error: ', e));
|
}).catch(e => logError('constructMessage error: ', e));
|
||||||
|
|
||||||
OB11Constructor.GroupEvent(message).then(groupEvent => {
|
OB11Constructor.GroupEvent(message).then(groupEvent => {
|
||||||
if (groupEvent) {
|
if (groupEvent) {
|
||||||
// log("post group event", groupEvent);
|
// log("post group event", groupEvent);
|
||||||
postOB11Event(groupEvent);
|
postOB11Event(groupEvent);
|
||||||
}
|
}
|
||||||
}).catch(e => logError('constructGroupEvent error: ', e));
|
}).catch(e => logError('constructGroupEvent error: ', e));
|
||||||
|
|
||||||
|
OB11Constructor.PrivateEvent(message).then(privateEvent => {
|
||||||
|
if (privateEvent) {
|
||||||
|
// log("post private event", privateEvent);
|
||||||
|
postOB11Event(privateEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async SetConfig(NewOb11: OB11Config) {
|
async SetConfig(NewOb11: OB11Config) {
|
||||||
@@ -407,13 +415,8 @@ export class NapCatOnebot11 {
|
|||||||
if (notifyTime < this.bootTime) {
|
if (notifyTime < this.bootTime) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const flag = notify.group.groupCode + '|' + notify.seq;
|
const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type;
|
||||||
const existNotify = groupNotifies[flag];
|
|
||||||
if (existNotify) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
logDebug('收到群通知', notify);
|
logDebug('收到群通知', notify);
|
||||||
groupNotifies[flag] = notify;
|
|
||||||
// let member2: GroupMember;
|
// let member2: GroupMember;
|
||||||
// if (notify.user2.uid) {
|
// if (notify.user2.uid) {
|
||||||
// member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
|
// member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { OB11Response } from '../action/OB11Response';
|
import { OB11Response } from '../action/OB11Response';
|
||||||
import { HttpServerBase } from '@/common/server/http';
|
import { HttpServerBase } from '@/common/server/http';
|
||||||
import { actionHandlers, actionMap } from '../action';
|
import { actionMap } from '../action';
|
||||||
import { ob11Config } from '@/onebot11/config';
|
import { ob11Config } from '@/onebot11/config';
|
||||||
import { selfInfo } from '@/core/data';
|
import { selfInfo } from '@/core/data';
|
||||||
import { OB11HeartbeatEvent } from '@/onebot11/event/meta/OB11HeartbeatEvent';
|
import { OB11HeartbeatEvent } from '@/onebot11/event/meta/OB11HeartbeatEvent';
|
||||||
import { postOB11Event } from '@/onebot11/server/postOB11Event';
|
import { postOB11Event } from '@/onebot11/server/postOB11Event';
|
||||||
import { napCatCore } from '@/core';
|
|
||||||
|
|
||||||
class OB11HTTPServer extends HttpServerBase {
|
class OB11HTTPServer extends HttpServerBase {
|
||||||
name = 'OneBot V11 server';
|
name = 'OneBot V11 server';
|
||||||
|
@@ -12,7 +12,7 @@ import { OB11FriendRequestEvent } from '../event/request/OB11FriendRequest';
|
|||||||
import { OB11GroupRequestEvent } from '../event/request/OB11GroupRequest';
|
import { OB11GroupRequestEvent } from '../event/request/OB11GroupRequest';
|
||||||
import { isNull } from '@/common/utils/helper';
|
import { isNull } from '@/common/utils/helper';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
import { dbUtil } from '@/common/utils/db';
|
||||||
import { getGroup, groupNotifies, selfInfo } from '@/core/data';
|
import { getGroup, selfInfo } from '@/core/data';
|
||||||
import { NTQQFriendApi, NTQQGroupApi, NTQQUserApi } from '@/core/apis';
|
import { NTQQFriendApi, NTQQGroupApi, NTQQUserApi } from '@/core/apis';
|
||||||
import createSendElements from '../action/msg/SendMsg/create-send-elements';
|
import createSendElements from '../action/msg/SendMsg/create-send-elements';
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ async function handleMsg(msg: OB11Message, quickAction: QuickAction) {
|
|||||||
async function handleGroupRequest(request: OB11GroupRequestEvent, quickAction: QuickActionGroupRequest) {
|
async function handleGroupRequest(request: OB11GroupRequestEvent, quickAction: QuickActionGroupRequest) {
|
||||||
if (!isNull(quickAction.approve)) {
|
if (!isNull(quickAction.approve)) {
|
||||||
NTQQGroupApi.handleGroupRequest(
|
NTQQGroupApi.handleGroupRequest(
|
||||||
groupNotifies[request.flag],
|
request.flag,
|
||||||
quickAction.approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
quickAction.approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
||||||
quickAction.reason,
|
quickAction.reason,
|
||||||
).then().catch(logError);
|
).then().catch(logError);
|
||||||
|
@@ -87,7 +87,7 @@ export class ReverseWebsocket {
|
|||||||
'X-Self-ID': selfInfo.uin,
|
'X-Self-ID': selfInfo.uin,
|
||||||
'Authorization': `Bearer ${token}`,
|
'Authorization': `Bearer ${token}`,
|
||||||
'x-client-role': 'Universal', // koishi-adapter-onebot 需要这个字段
|
'x-client-role': 'Universal', // koishi-adapter-onebot 需要这个字段
|
||||||
"User-Agent": "OneBot/11",
|
'User-Agent': 'OneBot/11',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
registerWsEventSender(this.websocket);
|
registerWsEventSender(this.websocket);
|
||||||
|
@@ -59,7 +59,8 @@ export enum OB11MessageDataType {
|
|||||||
poke = 'poke',
|
poke = 'poke',
|
||||||
dice = 'dice',
|
dice = 'dice',
|
||||||
RPS = 'rps',
|
RPS = 'rps',
|
||||||
miniapp = 'miniapp'//json类
|
miniapp = 'miniapp',//json类
|
||||||
|
Location = 'location'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OB11MessageMFace {
|
export interface OB11MessageMFace {
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const version = '1.6.5';
|
export const version = '1.6.6';
|
||||||
|
@@ -29,7 +29,7 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
undefined,
|
undefined,
|
||||||
SettingButton('V1.6.5', 'napcat-update-button', 'secondary')
|
SettingButton('V1.6.6', 'napcat-update-button', 'secondary')
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -167,7 +167,7 @@ async function onSettingWindowCreated(view) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
void 0,
|
void 0,
|
||||||
SettingButton("V1.6.5", "napcat-update-button", "secondary")
|
SettingButton("V1.6.6", "napcat-update-button", "secondary")
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -21,20 +21,16 @@ function genCpModule(module: string) {
|
|||||||
return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false };
|
return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false };
|
||||||
}
|
}
|
||||||
let startScripts: string[] | undefined = undefined;
|
let startScripts: string[] | undefined = undefined;
|
||||||
let MoeHooModule: any = [];
|
|
||||||
if (process.env.NAPCAT_BUILDSYS == 'linux') {
|
if (process.env.NAPCAT_BUILDSYS == 'linux') {
|
||||||
if (process.env.NAPCAT_BUILDARCH == 'x64') {
|
if (process.env.NAPCAT_BUILDARCH == 'x64') {
|
||||||
MoeHooModule = [{ src: './src/core.lib/MoeHoo-linux-x64.node', dest: 'dist' }];
|
|
||||||
}
|
}
|
||||||
startScripts = ['./script/napcat.sh'];
|
startScripts = ['./script/napcat.sh'];
|
||||||
} else if (process.env.NAPCAT_BUILDSYS == 'win32') {
|
} else if (process.env.NAPCAT_BUILDSYS == 'win32') {
|
||||||
if (process.env.NAPCAT_BUILDARCH == 'x64') {
|
if (process.env.NAPCAT_BUILDARCH == 'x64') {
|
||||||
MoeHooModule = [{ src: './src/core.lib/MoeHoo-win32-x64.node', dest: 'dist' }];
|
|
||||||
}
|
}
|
||||||
startScripts = ['./script/napcat.ps1', './script/napcat.bat', './script/napcat-utf8.bat', './script/napcat-utf8.ps1', './script/napcat-log.ps1'];
|
startScripts = ['./script/napcat.ps1', './script/napcat.bat', './script/napcat-utf8.bat', './script/napcat-utf8.ps1', './script/napcat-log.ps1','./script/NapCat.164.bat','./script/napcat-9912.ps1','./script/napcat-9912-utf8.ps1','./script/napcat-9912.bat','./script/napcat-9912-utf8.bat'];
|
||||||
} else {
|
} else {
|
||||||
MoeHooModule = [{ src: './src/core.lib/MoeHoo-win32-x64.node', dest: 'dist' }, { src: './src/core.lib/MoeHoo-linux-x64.node', dest: 'dist' }];
|
startScripts = ['./script/napcat.sh', './script/napcat.ps1', './script/napcat.bat', './script/napcat-utf8.bat', './script/napcat-utf8.ps1', './script/napcat-log.ps1','./script/napcat-9912.ps1','./script/napcat-9912-utf8.ps1','./script/napcat-9912.bat','./script/napcat-9912-utf8.bat'];
|
||||||
startScripts = ['./script/napcat.sh', './script/napcat.ps1', './script/napcat.bat', './script/napcat-utf8.bat', './script/napcat-utf8.ps1', './script/napcat-log.ps1'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseConfigPlugin: PluginOption[] = [
|
const baseConfigPlugin: PluginOption[] = [
|
||||||
|
Reference in New Issue
Block a user