Merge remote-tracking branch 'origin/main'

# Conflicts:
#	script/napcat.bat
This commit is contained in:
linyuchen
2024-04-15 22:31:18 +08:00
14 changed files with 278 additions and 109 deletions

View File

@@ -3,7 +3,7 @@ module.exports = {
'es2021': true,
'node': true
},
'ignorePatterns': ['src/core/'],
'ignorePatterns': ['src/core/', 'src/core.lib/'],
'extends': [
'eslint:recommended',
'plugin:@typescript-eslint/recommended'

81
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Bug 反馈
description: 报告可能的 NapCat 异常行为
title: '[BUG] '
labels: bug
body:
- type: markdown
attributes:
value: |
欢迎来到 NapCat 的 Issue Tracker请填写以下表格来提交 Bug。
在提交新的 Bug 反馈前,请确保您:
* 已经搜索了现有的 issues并且没有找到可以解决您问题的方法
* 不与现有的某一 issue 重复
- type: input
id: system-version
attributes:
label: 系统版本
description: 运行 QQNT 的系统版本
placeholder: Windows 10 Pro Workstation 22H2
validations:
required: true
- type: input
id: qqnt-version
attributes:
label: QQNT 版本
description: 可在 QQNT 的「关于」的设置页中找到
placeholder: 9.9.7-21804
validations:
required: true
- type: input
id: napcat-version
attributes:
label: NapCat 版本
description: 可在 LiteLoaderQQNT 的设置页或是 QQNT 的设置页侧栏中找到
placeholder: 1.0.0
validations:
required: true
- type: input
id: onebot-client-version
attributes:
label: OneBot 客户端
description: 连接至 NapCat 的客户端版本信息
placeholder: Overflow 2.16.0-2cf7991-SNAPSHOT
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: 发生了什么?
description: 填写你认为的 NapCat 的不正常行为
validations:
required: true
- type: textarea
id: how-reproduce
attributes:
label: 如何复现
description: 填写应当如何操作才能触发这个不正常行为
placeholder: |
1. xxx
2. xxx
3. xxx
validations:
required: true
- type: textarea
id: what-expected
attributes:
label: 期望的结果?
description: 填写你认为 NapCat 应当执行的正常行为
validations:
required: true
- type: textarea
id: napcat-log
attributes:
label: NapCat 运行日志
description: 粘贴相关日志内容到此处
render: shell
- type: textarea
id: onebot-client-log
attributes:
label: OneBot 客户端运行日志
description: 粘贴 OneBot 客户端的相关日志内容到此处
render: shell

67
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: "Build"
on:
push:
jobs:
build-linux:
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target_platform: [linux,darwin]
target_arch: [x64, arm64]
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCatQQ'
submodules: true
token: ${{ secrets.NAPCAT_BUILD }}
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NuCat Linux
run: |
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
npm run build:prod
cd dist
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
cd ..
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
path: dist
build-win32:
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target_platform: [win32]
target_arch: [x64]
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCatQQ'
submodules: true
token: ${{ secrets.NAPCAT_BUILD }}
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NuCat Linux
run: |
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
npm run build:prod
cd dist
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
cd ..
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
path: dist

View File

@@ -16,7 +16,7 @@ jobs:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCat'
repository: 'NapNeko/NapCatQQ'
submodules: true
token: ${{ secrets.NAPCAT_BUILD }}
- name: Use Node.js 20.X
@@ -46,7 +46,7 @@ jobs:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCat'
repository: 'NapNeko/NapCatQQ'
submodules: true
token: ${{ secrets.NAPCAT_BUILD }}
- name: Use Node.js 20.X
@@ -65,3 +65,28 @@ jobs:
with:
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
path: dist
release-napcat:
needs: [build-win32,build-linux]
runs-on: ubuntu-latest
steps:
- name: Download All Artifact
uses: actions/download-artifact@v4
- name: Compress subdirectories
run: |
for dir in */; do
base=$(basename "$dir")
zip -r "${base}.zip" "$dir"
done
- name: Create Release Draft and Upload Artifacts
uses: softprops/action-gh-release@v1
with:
name: NapCat V0.0.0
token: ${{ secrets.NAPCAT_BUILD }}
files: |
NapCat.win32.x64.zip
NapCat.linux.x64.zip
NapCat.linux.arm64.zip
NapCat.darwin.x64.zip
NapCat.darwin.arm64.zip
draft: true

View File

@@ -10,13 +10,15 @@ NapCatQQ瞌睡猫QQ不准叫我NCQQ像睡着了一样在后台低
由于 Linux 上的 QQ 图形依赖较多,会导致内存占用小高,大约 **100+M**,目前正在研究如何优化
具体占用会因人而异QQ 群、好友越多占用越高
## 下载
前往 Release 页面下载最新版本
## 启动
NapCat 是基于 官方NTQQ 实现的Bot框架因此先需要安装官方QQ
NapCat 是基于 官方NTQQ 实现的Bot框架因此先需要安装官方QQ**注意同个账号不能同时登录原版 QQ 和 NapCatQQ**
*如果没有安装 QQ 请往后翻查看安装方法*
@@ -62,7 +64,9 @@ json 配置内容参数解释:
### Windows 启动
运行`powershell ./napcat.ps1`, 或者 `napcat.bat`,如果出现乱码,可以尝试运行`napcat_utf8.ps1`
运行`powershell ./napcat.ps1`, 或者 `napcat.bat`,如果出现乱码,可以尝试运行`napcat-utf8.ps1``napcat-utf8.bat`
*如果出现 powershell 运行不了,以管理员身份打开 powershell输入 `Set-ExecutionPolicy RemoteSigned`*
### Linux 启动
@@ -80,17 +84,13 @@ json 配置内容参数解释:
目前还在研究怎么精简安装暂时只能安装官方QQ整体依赖
```bash
下载QQ的deb包
下载QQ
[deb x86版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_amd64_01.deb)
[deb arm版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_arm64_01.deb)
[rpm x86版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_x86_64_01.rpm)
[rpm arm版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_aarch64_01.rpm)
```
```bash
```bash
sudo apt install ./qq.deb
@@ -141,6 +141,9 @@ $env:FFMPEG_PATH="d:\ffmpeg\bin\ffmpeg.exe"
不用管,这是正常现象,是因为 QQ 本身的问题,不影响使用
## API 文档
参考 [LLOneBot](https://llonebot.github.io/zh-CN/develop/api) 的文档
<!--
QQ群545402644

View File

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

View File

@@ -2,21 +2,17 @@
setlocal enabledelayedexpansion
chcp 65001
:loop_read
for /f "tokens=3" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
set "RetString=%%a"
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
)
goto :loop_read
:napcat_boot
for %%a in ("!RetString!") do (
set "pathWithoutUninstall=%%~dpa"
set "fileName=%%~na"
set "extension=%%~xa"
)
set "QQPath=!pathWithoutUninstall!QQ.exe"
set ELECTRON_RUN_AS_NODE=1
echo !QQPath!
!QQPath! ./napcat.cjs
"!QQPath!" ./napcat.cjs %*

View File

@@ -1,22 +1,17 @@
@echo off
setlocal enabledelayedexpansion
:loop_read
for /f "tokens=3" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
set "RetString=%%a"
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
)
goto :loop_read
:napcat_boot
for %%a in ("!RetString!") do (
set "pathWithoutUninstall=%%~dpa"
set "fileName=%%~na"
set "extension=%%~xa"
)
set "QQPath=!pathWithoutUninstall!QQ.exe"
set ELECTRON_RUN_AS_NODE=1
echo "!QQPath!"
"!QQPath!" %~dp0/napcat.cjs %*
echo !QQPath!
"!QQPath!" ./napcat.cjs %*

View File

@@ -1,38 +1,38 @@
import { request } from "https";
import { request } from 'https';
export function noifyLoginStatus() {
let req = request(
{
hostname: 'napcat.wumiao.wang',
path: '/api/send',
port: 443,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0`
}
},
(res) => {
//let data = '';
res.on('data', (chunk) => {
//data += chunk;
});
res.on('end', () => {
//console.log('Response:', data);
});
}
);
let StatesData = {
type: "event",
payload: {
"website": "952bf82f-8f49-4456-aec5-e17db5f27f7e",
"hostname": "napcat.demo.cn",
"screen": "1920x1080",
"language": "zh-CN",
"title": "OneBot.Login",
"url": "/login/onebot11",
"referrer": "https://napcat.demo.cn/login?type=onebot11"
}
};
req.write(JSON.stringify(StatesData));
req.end();
const req = request(
{
hostname: 'napcat.wumiao.wang',
path: '/api/send',
port: 443,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0'
}
},
(res) => {
//let data = '';
res.on('data', (chunk) => {
//data += chunk;
});
res.on('end', () => {
//console.log('Response:', data);
});
}
);
const StatesData = {
type: 'event',
payload: {
'website': '952bf82f-8f49-4456-aec5-e17db5f27f7e',
'hostname': 'napcat.demo.cn',
'screen': '1920x1080',
'language': 'zh-CN',
'title': 'OneBot.Login',
'url': '/login/onebot11',
'referrer': 'https://napcat.demo.cn/login?type=onebot11'
}
};
req.write(JSON.stringify(StatesData));
req.end();
}

View File

@@ -1,44 +1,44 @@
import { get as httpsGet } from "node:https";
import { get as httpsGet } from 'node:https';
function requestMirror(url: string): Promise<string | undefined> {
return new Promise((resolve, reject) => {
httpsGet(url, (response) => {
let data = '';
response.on('data', (chunk) => {
data += chunk;
});
return new Promise((resolve, reject) => {
httpsGet(url, (response) => {
let data = '';
response.on('data', (chunk) => {
data += chunk;
});
response.on('end', () => {
try {
const parsedData = JSON.parse(data);
const version = parsedData.version;
resolve(version);
} catch (error) {
// 解析失败或无法访问域名,跳过
resolve(undefined);
}
});
}).on('error', (error) => {
// 请求失败,跳过
resolve(undefined);
});
response.on('end', () => {
try {
const parsedData = JSON.parse(data);
const version = parsedData.version;
resolve(version);
} catch (error) {
// 解析失败或无法访问域名,跳过
resolve(undefined);
}
});
}).on('error', (error) => {
// 请求失败,跳过
resolve(undefined);
});
});
}
export async function checkVersion(): Promise<string> {
return new Promise(async (resolve, reject) => {
const MirrorList =
return new Promise(async (resolve, reject) => {
const MirrorList =
[
"https://fastly.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json",
"https://gcore.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json",
"https://cdn.jsdelivr.us/gh/NapNeko/NapCatQQ@main/package.json",
"https://jsd.cdn.zzko.cn/gh/NapNeko/NapCatQQ@main/package.json"
'https://fastly.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json',
'https://gcore.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json',
'https://cdn.jsdelivr.us/gh/NapNeko/NapCatQQ@main/package.json',
'https://jsd.cdn.zzko.cn/gh/NapNeko/NapCatQQ@main/package.json'
];
for (const url of MirrorList) {
const version = await requestMirror(url);
if (version) {
resolve(version);
}
}
reject("get verison error!");
});
for (const url of MirrorList) {
const version = await requestMirror(url);
if (version) {
resolve(version);
}
}
reject('get verison error!');
});
}

View File

@@ -39,6 +39,6 @@ export class GoCQHTTGetForwardMsgAction extends BaseAction<Payload, any> {
(<OB11ForwardMessage>msg).content = msg.message;
delete (<any>msg).message;
});
return {messages};
return { messages };
}
}

View File

@@ -160,7 +160,7 @@ export async function createSendElements(messageData: OB11MessageData[], group:
// }
// log("找到文件缓存", file);
// }
const {path, isLocal, fileName, errMsg} = (await uri2local(file));
const { path, isLocal, fileName, errMsg } = (await uri2local(file));
if (errMsg) {
throw errMsg;
}
@@ -309,7 +309,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
if (this.getSpecialMsgNum(payload, OB11MessageDataType.node)) {
try {
const returnMsg = await this.handleForwardNode(peer, messages as OB11MessageNode[], group);
return {message_id: returnMsg!.id!};
return { message_id: returnMsg!.id! };
} catch (e: any) {
throw ('发送转发消息失败 ' + e.toString());
}
@@ -317,8 +317,8 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
if (this.getSpecialMsgNum(payload, OB11MessageDataType.music)) {
const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic;
if (music) {
const {url, audio, title, content, image} = music.data;
const selfPeer: Peer = {peerUid: selfInfo.uid, chatType: ChatType.friend};
const { url, audio, title, content, image } = music.data;
const selfPeer: Peer = { peerUid: selfInfo.uid, chatType: ChatType.friend };
// 搞不定!
// const musicMsg = await this.send(selfPeer, [this.genMusicElement(url, audio, title, content, image)], [], false)
// 转发
@@ -329,12 +329,12 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
}
}
// log("send msg:", peer, sendElements)
const {sendElements, deleteAfterSentFiles} = await createSendElements(messages, group);
const { sendElements, deleteAfterSentFiles } = await createSendElements(messages, group);
const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles);
deleteAfterSentFiles.map(f => fs.unlink(f, () => {
}));
const res = {message_id: returnMsg.id!};
const res = { message_id: returnMsg.id! };
// console.log(res);
return res;
}
@@ -452,7 +452,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
if (nodeMsg) {
nodeMsgArray.push(nodeMsg);
if (!srcPeer) {
srcPeer = {chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid};
srcPeer = { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
} else if (srcPeer.peerUid !== nodeMsg.peerUid) {
needSendSelf = true;
srcPeer = selfPeer;
@@ -501,7 +501,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
token: '5c1e4905f926dd3a64a4bd3841460351',
type: 'normal'
},
extra: {app_type: 1, appid: 100497308, uin: selfInfo.uin},
extra: { app_type: 1, appid: 100497308, uin: selfInfo.uin },
meta: {
news: {
action: '',

View File

@@ -24,11 +24,13 @@ checkVersion().then((remoteVersion: string) => {
for (const k of [0, 1, 2]) {
if (parseInt(remoteVersionList[k]) > parseInt(localVersionList[k])) {
console.log('检测到更新,请前往 https://github.com/NapNeko/NapCatQQ 下载 NapCatQQ V', remoteVersion);
return;
} else if (parseInt(remoteVersionList[k]) < parseInt(localVersionList[k])) {
break;
}
}
console.log('当前已是最新版本,版本:', localVersion);
return;
});
new NapCatOnebot11();
napCatCore.addLoginSuccessCallback(() => {
@@ -50,7 +52,7 @@ napCatCore.on('system.login.qrcode', (qrCodeData: { url: string, base64: string
fs.writeFile(qrcodePath, qrCodeData.base64.split('data:image/png;base64')[1], 'base64').then(() => {
console.log('二维码已保存到', qrcodePath);
});
qrcode.generate(qrCodeData.url, {small: true}, (res) => {
qrcode.generate(qrCodeData.url, { small: true }, (res) => {
console.log(res);
});
});

View File

@@ -120,7 +120,7 @@ export class NapCatOnebot11 {
}
async postReceiveMsg(msgList: RawMessage[]) {
const {debug, reportSelfMessage} = ob11Config;
const { debug, reportSelfMessage } = ob11Config;
for (const message of msgList) {
// console.log("ob11 收到新消息", message)
// if (message.senderUin !== selfInfo.uin){