Compare commits

...

8 Commits

Author SHA1 Message Date
手瓜一十雪
81a8115c56 release:1.8.3 2024-08-07 01:32:48 +08:00
手瓜一十雪
0ddd26bd51 release: 1.8.3 2024-08-07 01:28:22 +08:00
手瓜一十雪
69e2133a27 chore: BootWay05Script 2024-08-07 01:25:02 +08:00
手瓜一十雪
b04f85949b docs: todo 2024-08-06 23:42:21 +08:00
手瓜一十雪
667dba01ae build: OB11InputStatusEvent 2024-08-06 23:39:22 +08:00
手瓜一十雪
85a8cef628 chore: 移除部分无用内容 2024-08-06 21:14:11 +08:00
手瓜一十雪
3099acfd00 DOCS:TODO 2024-08-06 20:46:17 +08:00
手瓜一十雪
b00fe0b5f8 docs: 标记TODO和兜底接口 2024-08-06 20:38:49 +08:00
21 changed files with 254 additions and 36 deletions

View File

@@ -1,4 +1,4 @@
# v1.8.2
# v1.8.3
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
@@ -6,8 +6,8 @@ QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
Way03/Way05
## 新增与调整
1. 多层转发消息接收/发送
2. 消息列表排序修正
1. 输入状态变更事件加入
2. Way05 支持Bat启动
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "1.8.2",
"version": "1.8.3",
"scripts": {
"watch:dev": "vite --mode development",
"watch:prod": "vite --mode production",

90
script/BootWay05.bat Normal file
View File

@@ -0,0 +1,90 @@
@echo off
REM 检查当前会话是否具有管理员权限
openfiles >nul 2>&1
if %errorlevel% neq 0 (
REM 如果不是管理员,则重新启动脚本以管理员模式运行
echo 请求管理员权限...
powershell -Command "Start-Process cmd -ArgumentList '/c %~f0 %*' -Verb RunAs"
exit /b
)
REM 设置当前工作目录
cd /d %~dp0
REM 获取当前目录路径
set currentPath=%cd%
set currentPath=%currentPath:\=/%
REM 生成JavaScript代码
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
REM 将JavaScript代码保存到文件中
echo %jsCode% > loadScript.js
echo JavaScript code has been generated and saved to loadScript.js
REM 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
set NAPCAT_PATH=%cd%\loadScript.js
REM 获取QQ路径
: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%QQ.exe
REM 拿不到QQ路径则退出
if not exist "%QQpath%" (
echo provided QQ path is invalid: %QQpath%
pause
exit /b
)
REM 收集dbghelp.dll路径和HASH信息
set QQdir=%~dp0
set oldDllPath=%QQdir%dbghelp.dll
set newDllPath=%currentPath%\dbghelp.dll
for /f "tokens=*" %%A in ('certutil -hashfile "%oldDllPath%" MD5') do (
if not defined oldDllHash set oldDllHash=%%A
)
for /f "tokens=*" %%A in ('certutil -hashfile "%newDllPath%" MD5') do (
if not defined newDllHash set newDllHash=%%A
)
REM 如果文件一致则跳过
if "%oldDllHash%" neq "%newDllHash%" (
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
if %errorlevel% equ 0 (
REM 文件占用则退出
echo dbghelp.dll is in use, cannot continue.
) else (
REM 文件未占用则尝试覆盖
copy /y "%newDllPath%" "%oldDllPath%"
if %errorlevel% neq 0 (
echo Failed to copy dbghelp.dll
pause
exit /b
) else (
echo dbghelp.dll has been copied to %QQdir%
)
)
)
REM 带参数启动QQ
REM 判断wt是否存在存在则通过wt启动不存在则通过cmd启动
REM %QQPath% --enable-logging %*
where wt >nul 2>nul
if %errorlevel% equ 0 (
wt "cmd" /c "%QQPath%" --enable-logging %*
) else (
%QQPath%" --enable-logging %*
)

93
script/BootWay05.utf8.bat Normal file
View File

@@ -0,0 +1,93 @@
@echo off
REM 检查当前会话是否具有管理员权限
openfiles >nul 2>&1
if %errorlevel% neq 0 (
REM 如果不是管理员,则重新启动脚本以管理员模式运行
echo 请求管理员权限...
where wt >nul 2>nul
if %errorlevel% equ 0 (
powershell -Command "Start-Process cmd -ArgumentList ' /c %~f0 %*' -Verb RunAs"
) else (
powershell -Command "Start-Process wt -ArgumentList 'cmd /c %~f0 %*' -Verb RunAs"
)
REM wt "cmd" /c "%~f0 %*"
exit /b
)
REM 设置当前工作目录
cd /d %~dp0
REM 获取当前目录路径
set currentPath=%cd%
set currentPath=%currentPath:\=/%
REM 生成JavaScript代码
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
REM 将JavaScript代码保存到文件中
echo %jsCode% > loadScript.js
echo JavaScript code has been generated and saved to loadScript.js
REM 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
set NAPCAT_PATH=%cd%\loadScript.js
REM 获取QQ路径
: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%QQ.exe
REM 拿不到QQ路径则退出
if not exist "%QQpath%" (
echo provided QQ path is invalid: %QQpath%
pause
exit /b
)
REM 收集dbghelp.dll路径和HASH信息
set QQdir=%~dp0
set oldDllPath=%QQdir%dbghelp.dll
set newDllPath=%currentPath%\dbghelp.dll
for /f "tokens=*" %%A in ('certutil -hashfile "%oldDllPath%" MD5') do (
if not defined oldDllHash set oldDllHash=%%A
)
for /f "tokens=*" %%A in ('certutil -hashfile "%newDllPath%" MD5') do (
if not defined newDllHash set newDllHash=%%A
)
REM 如果文件一致则跳过
if "%oldDllHash%" neq "%newDllHash%" (
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
if %errorlevel% equ 0 (
REM 文件占用则退出
echo dbghelp.dll is in use, cannot continue.
) else (
REM 文件未占用则尝试覆盖
copy /y "%newDllPath%" "%oldDllPath%"
if %errorlevel% neq 0 (
echo Failed to copy dbghelp.dll
pause
exit /b
) else (
echo dbghelp.dll has been copied to %QQdir%
)
)
)
REM 带参数启动QQ
REM 判断wt是否存在存在则通过wt启动不存在则通过cmd启动
REM %QQPath% --enable-logging %*
chcp 65001
"%QQPath%" --enable-logging %*

View File

@@ -1,6 +1,6 @@
// --------------------
// 2024.7.3 9.9.12 BootWay.03 其余方法暂不公开(此方案为临时方案 Win平台已验证
// 缺陷 (已知)
// 1.与非入侵式不同 现在破坏本体代码
// 2.重启代码与正常启动代码失效
// 3.Win需要补丁

View File

@@ -43,6 +43,7 @@ setTimeout(() => {
await sleep(100);
// NTQQMsgApi.CheckSendMode().then().catch();
loadMessageUnique().then().catch();
//下面的代码还没摸清 不要使用
//let data = await napCatCore.session.getMsgService().sendSsoCmdReqByContend("LightAppSvc.mini_app_growguard.ReportExecute","1124343");
//console.log(data);
});
@@ -72,6 +73,7 @@ export class NTQQMsgApi {
}
static async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
//console.log(peer, msgSeq, emojiId, emojiType, count);
//注意此处emojiType 可选值一般为1-2 2好像是unicode表情dec值 大部分情况 Taged M likiowa
return napCatCore.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, "", false, 20)
}
// static napCatCore: NapCatCore | null = null;
@@ -104,7 +106,7 @@ export class NTQQMsgApi {
filterSendersUid: [],
filterMsgToTime: '0',
filterMsgFromTime: '0',
isReverseOrder: isReverseOrder,
isReverseOrder: isReverseOrder,//此参数有点离谱 注意不是本次查询的排序 而是全部消历史信息的排序 默认false 从新消息拉取到旧消息
isIncludeCurrent: true,
pageLimit: count,
});
@@ -113,6 +115,7 @@ export class NTQQMsgApi {
static async getMsgsByMsgId(peer: Peer | undefined, msgIds: string[] | undefined) {
if (!peer) throw new Error('peer is not allowed');
if (!msgIds) throw new Error('msgIds is not allowed');
//Mlikiowa 参数不合规会导致NC异常崩溃 原因是TX未对进入参数判断 对应Android标记@NotNull AndroidJADX分析可得
return await napCatCore.session.getMsgService().getMsgsByMsgId(peer, msgIds);
}
static async getSingleMsg(peer: Peer, seq: string) {
@@ -123,7 +126,7 @@ export class NTQQMsgApi {
}
static async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
chatInfo: peer,
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
filterMsgType: [],
filterSendersUid: [],
filterMsgToTime: '0',
@@ -150,6 +153,7 @@ export class NTQQMsgApi {
1,
5000,
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
return true;
},
GroupCode,
@@ -168,19 +172,26 @@ export class NTQQMsgApi {
}, msgIds);
}
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
// function generateMsgId() {
// const timestamp = Math.floor(Date.now() / 1000);
// const random = Math.floor(Math.random() * Math.pow(2, 32));
// const buffer = Buffer.alloc(8);
// buffer.writeUInt32BE(timestamp, 0);
// buffer.writeUInt32BE(random, 4);
// const msgId = BigInt("0x" + buffer.toString('hex')).toString();
// return msgId;
// }
function generateMsgId() {
const timestamp = Math.floor(Date.now() / 1000);
const random = Math.floor(Math.random() * Math.pow(2, 32));
const buffer = Buffer.alloc(8);
buffer.writeUInt32BE(timestamp, 0);
buffer.writeUInt32BE(random, 4);
const msgId = BigInt("0x" + buffer.toString('hex')).toString();
return msgId;
}
// 此处有采用Hack方法 利用数据返回正确得到对应消息
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
let msgId: string;
try {
msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
} catch (error) {
//if (!napCatCore.session.getMsgService()['generateMsgUniqueId'])
//兜底识别策略V2
msgId = generateMsgId().toString();
}
let data = await NTEventDispatch.CallNormalEvent<
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void

View File

@@ -77,7 +77,7 @@ export const sessionConfig: WrapperSessionInitConfig | any = {};
export async function genSessionConfig(selfUin: string, selfUid: string, account_path: string): Promise<WrapperSessionInitConfig> {
const downloadPath = path.join(account_path, 'NapCat', 'temp');
fs.mkdirSync(downloadPath, { recursive: true });
let guid: string = await getMachineId();
let guid: string = await getMachineId();//26702 支持JS获取guid值 在LoginService中获取 TODO mlikiow a
//console.log(guid);
// guid = '52afb776-82f6-4e59-9d38-44705b112d0a';
//let guid: string = await getMachineId();

View File

@@ -15,6 +15,7 @@ export class FetchCustomFace extends BaseAction<Payload, string[]> {
actionName = ActionName.FetchCustomFace;
PayloadSchema = SchemaData;
protected async _handle(payload: Payload) {
//48 可能正好是QQ需要的一个页面的数量 Tagged Mlikiowa
const ret = await NTQQMsgApi.fetchFavEmojiList(payload.count || 48);
return ret.emojiInfoList.map(e => e.url);
}

View File

@@ -1,11 +1,7 @@
import BaseAction from '../BaseAction';
import { ActionName, BaseCheckResult } from '../types';
import * as fs from 'node:fs';
import { NTQQUserApi } from '@/core/apis/user';
import { checkFileReceived, uri2local } from '@/common/utils/file';
import { napCatCore, NTQQGroupApi } from '@/core';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
// import { log } from "../../../common/utils";
const SchemaData = {
type: 'object',

View File

@@ -10,9 +10,7 @@ const SchemaData = {
properties: {
words: {
type: 'array',
items: {
type: 'string',
}
items: { type: 'string' }
}
},
required: ['words'],

View File

@@ -27,7 +27,7 @@ async function handleOb11FileLikeMessage(
{ data: inputdata }: OB11MessageFileBase,
{ deleteAfterSentFiles }: MessageContext
) {
//有的奇怪的框架将url作为参数 而不是file 此时优先url
//有的奇怪的框架将url作为参数 而不是file 此时优先url 同时注意可能传入的是非file://开头的目录 By Mlikiowa
const { path, isLocal, fileName, errMsg,success } = (await uri2local(inputdata?.url || inputdata.file));
if (!success) {

View File

@@ -224,7 +224,7 @@ export class OB11Constructor {
//读取视频链接并兜底
let videoUrl;//Array
if (msg.peerUin = '284840486') {
//合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究
//合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Taged TODO
}
try {

View File

@@ -2,5 +2,5 @@ import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
export class OB11GroupAdminNoticeEvent extends OB11GroupNoticeEvent {
notice_type = 'group_admin';
sub_type: 'set' | 'unset'; // "set" | "unset"
sub_type: 'set' | 'unset' = "set"; // "set" | "unset"
}

View File

@@ -0,0 +1,16 @@
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
//输入状态事件 初步完成 Mlikio wa Todo 需要做一些过滤
export class OB11InputStatusEvent extends OB11BaseNoticeEvent {
notice_type = 'notify';
sub_type = 'input_status';
status_text = "对方正在输入..."
eventType = 1;
user_id = 0;
constructor(user_id: number, eventType: number, status_text: string) {
super();
this.user_id = user_id;
this.eventType = eventType;
this.status_text = status_text;
}
}

View File

@@ -8,7 +8,6 @@ export interface MsgEmojiLike {
export class OB11GroupMsgEmojiLikeEvent extends OB11GroupNoticeEvent {
notice_type = 'group_msg_emoji_like';
message_id: number;
sub_type: 'ban' | 'lift_ban';
likes: MsgEmojiLike[];
constructor(groupId: number, userId: number, messageId: number, likes: MsgEmojiLike[]) {

View File

@@ -1,6 +1,4 @@
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
import { selfInfo } from '@/core/data';
import { OB11BaseEvent } from '../OB11BaseEvent';
class OB11PokeEvent extends OB11BaseNoticeEvent {
notice_type = 'notify';

View File

@@ -33,6 +33,7 @@ import { logMessage, logNotice, logRequest } from '@/onebot11/log';
import { OB11Message } from '@/onebot11/types';
import { isEqual } from '@/common/utils/helper';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { OB11InputStatusEvent } from './event/notice/OB11InputStatusEvent';
//下面几个其实应该移进Core-Data 缓存实现 但是现在在这里方便
//
@@ -92,6 +93,21 @@ export class NapCatOnebot11 {
}
// Create MsgListener
const msgListener = new MsgListener();
msgListener.onInputStatusPush = async (data: {
chatType: number;
eventType: number;
fromUin: string;
interval: string;
showTime: string;
statusText: string;
timestamp: string;
toUin: string;
}
) => {
let uin = await NTQQUserApi.getUinByUid(data.fromUin);
logNotice(`[输入状态] ${uin} ${data.statusText}`);
postOB11Event(new OB11InputStatusEvent(parseInt(uin), data.eventType, data.statusText));
}
msgListener.onRecvSysMsg = async (protobufData: number[]) => {
// function buf2hex(buffer: Buffer) {
// return [...new Uint8Array(buffer)]

View File

@@ -1 +1 @@
export const version = '1.8.2';
export const version = '1.8.3';

View File

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

View File

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

View File

@@ -26,9 +26,9 @@ if (process.env.NAPCAT_BUILDSYS == 'linux') {
} else if (process.env.NAPCAT_BUILDSYS == 'win32') {
if (process.env.NAPCAT_BUILDARCH == 'x64') {
}
startScripts = ['./script/dbghelp.dll', './script/BootWay05.ps1', './script/napcat-9912.ps1', './script/napcat-9912-utf8.ps1', './script/napcat-9912.bat', './script/napcat-9912-utf8.bat'];
startScripts = ['./script/BootWay05.bat', './script/BootWay05.utf8.bat', './script/dbghelp.dll', './script/BootWay05.ps1', './script/napcat-9912.ps1', './script/napcat-9912-utf8.ps1', './script/napcat-9912.bat', './script/napcat-9912-utf8.bat'];
} else {
startScripts = ['./script/dbghelp.dll', './script/BootWay05.ps1', './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/BootWay05.bat', './script/BootWay05.utf8.bat', './script/dbghelp.dll', './script/BootWay05.ps1', './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'];
}
const baseConfigPlugin: PluginOption[] = [