mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
290 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9c34f558d3 | ||
![]() |
3e2da3b490 | ||
![]() |
fb4a4f50be | ||
![]() |
6596e9cab6 | ||
![]() |
f1b137f2e1 | ||
![]() |
535720d0fe | ||
![]() |
f063cf4a16 | ||
![]() |
90bbdbf2fe | ||
![]() |
5f1d8fb99d | ||
![]() |
5486ffcdcc | ||
![]() |
adfd123970 | ||
![]() |
f1a364bfa2 | ||
![]() |
9da714bf15 | ||
![]() |
fc73295520 | ||
![]() |
ab955e41fb | ||
![]() |
c64367335c | ||
![]() |
edc787eb3e | ||
![]() |
f2c69fc68b | ||
![]() |
d947fe743b | ||
![]() |
5b37ae9026 | ||
![]() |
ec9e042b29 | ||
![]() |
337ac0eab9 | ||
![]() |
f6a1b784c4 | ||
![]() |
332fcecb78 | ||
![]() |
18590be1e7 | ||
![]() |
b76edcaf1d | ||
![]() |
6024cabb69 | ||
![]() |
08446e648e | ||
![]() |
14af7a3572 | ||
![]() |
cdc4275f81 | ||
![]() |
a9ade98315 | ||
![]() |
f3ae6fa70f | ||
![]() |
96457bbec3 | ||
![]() |
8f465e376e | ||
![]() |
adc366a959 | ||
![]() |
a20a6bc8bb | ||
![]() |
b176fa66d4 | ||
![]() |
f81b1926fb | ||
![]() |
7b7609a068 | ||
![]() |
670d4108e6 | ||
![]() |
a5c7b88a40 | ||
![]() |
e52b2e6d69 | ||
![]() |
e4066fb8df | ||
![]() |
f7a0fb22b4 | ||
![]() |
cad2ae723c | ||
![]() |
889a8c6093 | ||
![]() |
573418914f | ||
![]() |
d7fb6f9c05 | ||
![]() |
136e27d655 | ||
![]() |
d5ff2d7099 | ||
![]() |
2a7f8d0c99 | ||
![]() |
e3ca5df713 | ||
![]() |
bda32f3e8f | ||
![]() |
a7c6e45a92 | ||
![]() |
7c20ca9b64 | ||
![]() |
a201461eff | ||
![]() |
e5d9df37c5 | ||
![]() |
106fbaf086 | ||
![]() |
a0024c98d5 | ||
![]() |
684a702638 | ||
![]() |
aec4a009d1 | ||
![]() |
822af575c9 | ||
![]() |
485efa7d44 | ||
![]() |
3d09d45423 | ||
![]() |
4c69c6d9fd | ||
![]() |
920a41acef | ||
![]() |
0cf13a284c | ||
![]() |
a89cdef436 | ||
![]() |
881d88f4ad | ||
![]() |
a72c96f56d | ||
![]() |
bc8235b209 | ||
![]() |
0087495749 | ||
![]() |
9560afd4a7 | ||
![]() |
56ec8559a0 | ||
![]() |
99ca79ac7d | ||
![]() |
24564f4c74 | ||
![]() |
212c802a1e | ||
![]() |
984b5d6c40 | ||
![]() |
0e3a4191a9 | ||
![]() |
570a34bca5 | ||
![]() |
c9a0c29286 | ||
![]() |
b5f804ec22 | ||
![]() |
dadbb83271 | ||
![]() |
848aacdbbf | ||
![]() |
da3665a167 | ||
![]() |
dcf0a06217 | ||
![]() |
3b5e6553cd | ||
![]() |
509390af20 | ||
![]() |
9ad511a9c0 | ||
![]() |
89c102513d | ||
![]() |
5e65ae76ad | ||
![]() |
b6c364cd78 | ||
![]() |
e086b8707f | ||
![]() |
90dddd10a9 | ||
![]() |
2dd0907565 | ||
![]() |
f31b0d0c71 | ||
![]() |
a0825b75f7 | ||
![]() |
a3bd4c0f73 | ||
![]() |
a3e8c9b28a | ||
![]() |
d7fb850b4a | ||
![]() |
d084778a6e | ||
![]() |
8ca30de760 | ||
![]() |
8a10b81bd9 | ||
![]() |
4a93c4e584 | ||
![]() |
50177cd6bd | ||
![]() |
71a2e52739 | ||
![]() |
4fac6d5aa3 | ||
![]() |
37d061b602 | ||
![]() |
40193e4edc | ||
![]() |
b4e9d61871 | ||
![]() |
d44b589e55 | ||
![]() |
68216415b6 | ||
![]() |
ba53da18d1 | ||
![]() |
9b76fa3582 | ||
![]() |
13d8d10a7f | ||
![]() |
5c6c1bb09d | ||
![]() |
12105d96ea | ||
![]() |
4054756035 | ||
![]() |
16769c7838 | ||
![]() |
cd076c5959 | ||
![]() |
f52e1aa131 | ||
![]() |
fdc1ef7e9a | ||
![]() |
9cccf2d47b | ||
![]() |
0796f27f2a | ||
![]() |
6c84014e0d | ||
![]() |
cd496a22bf | ||
![]() |
0200343780 | ||
![]() |
47fb629d26 | ||
![]() |
71ae08706b | ||
![]() |
50dd798757 | ||
![]() |
0c8cf73746 | ||
![]() |
1bee811312 | ||
![]() |
b4c0068637 | ||
![]() |
f484c6e5fe | ||
![]() |
7a08187c5f | ||
![]() |
c4d7d5a0d4 | ||
![]() |
5b75e753a7 | ||
![]() |
326e9b86ce | ||
![]() |
d22f5d369c | ||
![]() |
d76503995c | ||
![]() |
eab930c083 | ||
![]() |
e430cc54f2 | ||
![]() |
fd26a9c698 | ||
![]() |
e79b608f77 | ||
![]() |
42b23a6c9c | ||
![]() |
8d94f24c71 | ||
![]() |
6ac74c39d9 | ||
![]() |
836eb7b708 | ||
![]() |
698624b4dc | ||
![]() |
5c1df82076 | ||
![]() |
5d649b3687 | ||
![]() |
a6a3d71155 | ||
![]() |
1cc9d501ab | ||
![]() |
7a98025df8 | ||
![]() |
44d6ed5e80 | ||
![]() |
b5f2226bef | ||
![]() |
ddbffe55d2 | ||
![]() |
9676b1d0e9 | ||
![]() |
8142d3bfeb | ||
![]() |
755ad27a0a | ||
![]() |
5afa2dcdf1 | ||
![]() |
03098ee024 | ||
![]() |
a2bfdd003c | ||
![]() |
7eb80646ba | ||
![]() |
6fd24e57d3 | ||
![]() |
22c90adb47 | ||
![]() |
df0c6fafbe | ||
![]() |
dc30321b04 | ||
![]() |
63dd98d2df | ||
![]() |
caaa6ed506 | ||
![]() |
caf23792cb | ||
![]() |
e430db20aa | ||
![]() |
6fc5da9b67 | ||
![]() |
f428e57724 | ||
![]() |
14ab21fe9a | ||
![]() |
85626e19da | ||
![]() |
8712160fd7 | ||
![]() |
75b33f5cb1 | ||
![]() |
f5e8ede847 | ||
![]() |
3b3f684a8c | ||
![]() |
a78b60d40e | ||
![]() |
9ff06a3c44 | ||
![]() |
8532dc486c | ||
![]() |
861340f4bf | ||
![]() |
cdcb51ebe4 | ||
![]() |
0b11786d7d | ||
![]() |
1742247a9a | ||
![]() |
42bad123b2 | ||
![]() |
2d1e87defc | ||
![]() |
1c6f783a07 | ||
![]() |
6aafc097d5 | ||
![]() |
4010f233dd | ||
![]() |
75f67caa1b | ||
![]() |
d760ce54b7 | ||
![]() |
956976ebd5 | ||
![]() |
f9c2d4ca6c | ||
![]() |
dd5cc3c38c | ||
![]() |
daed4cc13e | ||
![]() |
6ff614dd18 | ||
![]() |
eb70ac4266 | ||
![]() |
a3a431adb7 | ||
![]() |
e12c72ab98 | ||
![]() |
9f8549b831 | ||
![]() |
b2de256f87 | ||
![]() |
7f32a5cf9e | ||
![]() |
56f8314d29 | ||
![]() |
4ceb2a8669 | ||
![]() |
c778d3b699 | ||
![]() |
47eda9cdf2 | ||
![]() |
dcaec4d356 | ||
![]() |
aee4f349c6 | ||
![]() |
daa2c39902 | ||
![]() |
5770fc02a1 | ||
![]() |
47cafd295b | ||
![]() |
3296f2daf8 | ||
![]() |
962616545c | ||
![]() |
11ea92c078 | ||
![]() |
1d64fa4817 | ||
![]() |
c46f2956c2 | ||
![]() |
8f6d4298be | ||
![]() |
3bce81326e | ||
![]() |
2ae9f6d0fe | ||
![]() |
9266828278 | ||
![]() |
a8a2ffc33e | ||
![]() |
27c4543471 | ||
![]() |
50a02cb59e | ||
![]() |
50579bb9e6 | ||
![]() |
50512ca63c | ||
![]() |
8b3577b216 | ||
![]() |
7553aab932 | ||
![]() |
5dacdcfe5e | ||
![]() |
8645a412b7 | ||
![]() |
acad07a588 | ||
![]() |
51bbb480bb | ||
![]() |
f0306cd10a | ||
![]() |
25253ad9e7 | ||
![]() |
51f2fb8e8b | ||
![]() |
9e8d650cbd | ||
![]() |
d222ccfa58 | ||
![]() |
9a05aaa906 | ||
![]() |
00fdce8876 | ||
![]() |
29b51adf7d | ||
![]() |
8e14b39969 | ||
![]() |
d9fb4d6c4d | ||
![]() |
fcf2f4c5f2 | ||
![]() |
4e97501690 | ||
![]() |
b0402391fb | ||
![]() |
f05a862cf9 | ||
![]() |
3ea92d57c2 | ||
![]() |
254b85fbd8 | ||
![]() |
16371c0cc4 | ||
![]() |
2256d67e2b | ||
![]() |
0af0bdede6 | ||
![]() |
379f31b9de | ||
![]() |
771a524734 | ||
![]() |
560e18a610 | ||
![]() |
147bdfab95 | ||
![]() |
36b1b0f663 | ||
![]() |
91511e4c3f | ||
![]() |
6a72056b25 | ||
![]() |
62e0c57a50 | ||
![]() |
9d92270931 | ||
![]() |
f61321d5a6 | ||
![]() |
08ab2f8649 | ||
![]() |
82962c4b42 | ||
![]() |
bd24e8a4ad | ||
![]() |
6224d9a292 | ||
![]() |
bbc58f3671 | ||
![]() |
fcd620283f | ||
![]() |
a78def3d2d | ||
![]() |
43e94a5db0 | ||
![]() |
e77bcc1267 | ||
![]() |
9b458958b8 | ||
![]() |
35419ade29 | ||
![]() |
15bd2ee887 | ||
![]() |
9394bafa8e | ||
![]() |
94150a0c48 | ||
![]() |
8955fdfc23 | ||
![]() |
c13aa6a545 | ||
![]() |
c73b50bd4a | ||
![]() |
0a17a38bf1 | ||
![]() |
0f7bfe1d66 | ||
![]() |
cf3f488663 | ||
![]() |
5f536fdb73 | ||
![]() |
99d0b13cce | ||
![]() |
b04937f012 | ||
![]() |
91c9b059cf | ||
![]() |
35cc643440 | ||
![]() |
b23bb8c46a | ||
![]() |
45df093fac |
25
.github/workflows/release.yml
vendored
25
.github/workflows/release.yml
vendored
@@ -95,14 +95,25 @@ jobs:
|
||||
steps:
|
||||
- name: Download All Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
# - name: Compress subdirectories
|
||||
# run: |
|
||||
# cd ./NapCat.Shell/
|
||||
# zip -q -r NapCat.Shell.zip *
|
||||
# cd ..
|
||||
# rm ./NapCat.Shell.zip -rf
|
||||
# mv ./NapCat.Shell/NapCat.Shell.zip ./
|
||||
- name: Compress subdirectories
|
||||
run: |
|
||||
for dir in */; do
|
||||
base=$(basename "$dir")
|
||||
zip -r "${base}.zip" "$dir"
|
||||
done
|
||||
|
||||
cd ./NapCat.Shell/
|
||||
zip -q -r NapCat.Shell.zip *
|
||||
cd ..
|
||||
cd ./NapCat.Framework/
|
||||
zip -q -r NapCat.Framework.zip *
|
||||
cd ..
|
||||
rm ./NapCat.Shell.zip -rf
|
||||
rm ./NapCat.Framework.zip -rf
|
||||
mv ./NapCat.Shell/NapCat.Shell.zip ./
|
||||
mv ./NapCat.Framework/NapCat.Framework.zip ./
|
||||
- name: Extract version from tag
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||
|
||||
@@ -118,6 +129,4 @@ jobs:
|
||||
files: |
|
||||
NapCat.Framework.zip
|
||||
NapCat.Shell.zip
|
||||
# NapCat.darwin.x64.zip
|
||||
# NapCat.darwin.arm64.zip
|
||||
draft: true
|
||||
|
6
LICENSE
6
LICENSE
@@ -1,4 +1,4 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
GNU GENERAL PUBLIC Without Social media promotion LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
@@ -111,6 +111,10 @@ above, provided that you also meet all of these conditions:
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
d)You may use this software in accordance with the above terms,
|
||||
but you are not allowed to promote this project or your projects
|
||||
based on this project on any public social media.
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
|
23
README.md
23
README.md
@@ -3,32 +3,33 @@
|
||||
</div>
|
||||
|
||||
---
|
||||
## 欢迎回来
|
||||
NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现。
|
||||
|
||||
## 项目介绍
|
||||
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
|
||||
|
||||
## 项目优势
|
||||
## 猫猫技能
|
||||
- [x] **多种启动方式**:支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动
|
||||
- [x] **低占用**:无头模式占用资源极低,适合在服务器上运行
|
||||
- [x] **超多接口**:在实现大部分Onebot接口上扩展了一套私有API
|
||||
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
|
||||
|
||||
## 如何使用
|
||||
## 使用猫猫
|
||||
|
||||
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||
|
||||
**首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程。
|
||||
|
||||
## 相关链接
|
||||
## 回家旅途
|
||||
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
|
||||
|
||||
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
|
||||
|
||||
## 附加协议
|
||||
禁止未授权任何项目使用Core部分代码用于二次开发与分发
|
||||
|
||||
## 鸣谢名单
|
||||
## 猫猫朋友
|
||||
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供初始版本基础
|
||||
|
||||
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持
|
||||
|
||||
---
|
||||
**任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**
|
||||
|
||||
## 约法三章
|
||||
|
||||
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 NapCat 代码开发。**
|
||||
|
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"manifest_version": 4,
|
||||
"type": "extension",
|
||||
"name": "NapCat",
|
||||
"slug": "NapCat",
|
||||
"description": "OneBot v11 protocol implementation with NapCat logic",
|
||||
"version": "2.0.4",
|
||||
"name": "NapCatQQ",
|
||||
"slug": "NapCat.Framework",
|
||||
"description": "高性能的 OneBot 11 协议实现",
|
||||
"version": "2.2.11",
|
||||
"icon": "./logo.png",
|
||||
"authors": [
|
||||
{
|
||||
@@ -30,4 +30,4 @@
|
||||
"main": "./liteloader.cjs",
|
||||
"preload": "./preload.cjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "napcat",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "2.0.4",
|
||||
"version": "2.2.11",
|
||||
"scripts": {
|
||||
"build:framework": "vite build --mode framework",
|
||||
"build:shell": "vite build --mode shell",
|
||||
|
77
script/BootWay05_init.bat
Normal file
77
script/BootWay05_init.bat
Normal file
@@ -0,0 +1,77 @@
|
||||
@echo off
|
||||
REM Check if the script is running as administrator
|
||||
openfiles >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
REM If not, restart the script in administrator mode
|
||||
echo Requesting administrator privileges...
|
||||
powershell -Command "Start-Process cmd -ArgumentList '/c %~f0 %*' -Verb RunAs"
|
||||
exit /b
|
||||
)
|
||||
|
||||
cd /d %~dp0
|
||||
|
||||
set currentPath=%cd%
|
||||
set currentPath=%currentPath:\=/%
|
||||
|
||||
REM Generate JavaScript code
|
||||
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
|
||||
|
||||
REM Save JavaScript code to a file
|
||||
echo %jsCode% > loadScript.js
|
||||
echo JavaScript code has been generated and saved to loadScript.js
|
||||
|
||||
REM Set NAPCAT_PATH environment variable to the address of loadScript.js in the current directory
|
||||
set NAPCAT_PATH=%cd%\loadScript.js
|
||||
|
||||
REM Get QQ path and cache it
|
||||
: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"
|
||||
)
|
||||
|
||||
set "pathWithoutUninstall=%RetString:Uninstall.exe=%"
|
||||
|
||||
SET QQPath=%pathWithoutUninstall%QQ.exe
|
||||
echo %QQPath%>qq_path_cache.txt
|
||||
echo QQ path %QQPath% has been cached to qq_path_cache.txt
|
||||
|
||||
REM Exit if QQ path is invalid
|
||||
if not exist "%QQpath%" (
|
||||
echo provided QQ path is invalid: %QQpath%
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
REM Collect dbghelp.dll path and HASH information
|
||||
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 Compare the HASH of the old and new dbghelp.dll, and replace the old one if they are different
|
||||
if "%oldDllHash%" neq "%newDllHash%" (
|
||||
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
|
||||
if %errorlevel% equ 0 (
|
||||
REM If the file is in use, prompt the user to close QQ
|
||||
echo dbghelp.dll is in use, please close QQ first.
|
||||
) else (
|
||||
copy /y "%newDllPath%" "%oldDllPath%"
|
||||
if %errorlevel% neq 0 (
|
||||
echo Copy dbghelp.dll failed, please check and try again.
|
||||
pause
|
||||
exit /b
|
||||
) else (
|
||||
echo dbghelp.dll has been updated.
|
||||
echo Please run BootWay05_run.bat to start QQ.
|
||||
echo If you update QQ in the future, please run BootWay05_init.bat again.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
)
|
10
script/BootWay05_run.bat
Normal file
10
script/BootWay05_run.bat
Normal file
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
set /p QQPath=<qq_path_cache.txt
|
||||
echo QQ path %QQPath% has been read from qq_path_cache.txt
|
||||
echo If failed to start QQ, please try running this script in administrator mode.
|
||||
|
||||
set NAPCAT_PATH=%cd%\loadScript.js
|
||||
|
||||
REM Launch QQ.exe with params provided
|
||||
|
||||
"%QQPath%" --enable-logging %*
|
13
script/BootWay05_run.utf8.bat
Normal file
13
script/BootWay05_run.utf8.bat
Normal file
@@ -0,0 +1,13 @@
|
||||
@echo off
|
||||
|
||||
chcp 65001
|
||||
|
||||
set /p QQPath=<qq_path_cache.txt
|
||||
echo QQ path %QQPath% has been read from qq_path_cache.txt
|
||||
echo If failed to start QQ, please try running this script in administrator mode.
|
||||
|
||||
set NAPCAT_PATH=%cd%\loadScript.js
|
||||
|
||||
REM Launch QQ.exe with params provided
|
||||
|
||||
"%QQPath%" --enable-logging %*
|
@@ -1,7 +1,7 @@
|
||||
import { NodeIQQNTWrapperSession } from '@/core/wrapper/wrapper';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
interface Internal_MapKey {
|
||||
interface InternalMapKey {
|
||||
timeout: number;
|
||||
createtime: number;
|
||||
func: (...arg: any[]) => any;
|
||||
@@ -11,36 +11,31 @@ interface Internal_MapKey {
|
||||
export type ListenerClassBase = Record<string, string>;
|
||||
|
||||
export interface ListenerIBase {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(listener: any): ListenerClassBase;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export class LegacyNTEventWrapper {
|
||||
private listenerMapping: Record<string, ListenerIBase>; //ListenerName-Unique -> Listener构造函数
|
||||
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
|
||||
private listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例
|
||||
private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
|
||||
private EventTask = new Map<string, Map<string, Map<string, InternalMapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
|
||||
|
||||
constructor(
|
||||
listenerMapping: Record<string, ListenerIBase>,
|
||||
wrapperSession: NodeIQQNTWrapperSession,
|
||||
wrapperSession: NodeIQQNTWrapperSession
|
||||
) {
|
||||
this.listenerMapping = listenerMapping;
|
||||
this.WrapperSession = wrapperSession;
|
||||
}
|
||||
|
||||
createProxyDispatch(ListenerMainName: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const current = this;
|
||||
const dispatcherListenerFunc = this.dispatcherListener.bind(this);
|
||||
return new Proxy(
|
||||
{},
|
||||
{
|
||||
get(target: any, prop: any, receiver: any) {
|
||||
// console.log('get', prop, typeof target[prop]);
|
||||
if (typeof target[prop] === 'undefined') {
|
||||
// 如果方法不存在,返回一个函数,这个函数调用existentMethod
|
||||
return (...args: any[]) => {
|
||||
current.dispatcherListener.apply(current, [ListenerMainName, prop, ...args]).then();
|
||||
dispatcherListenerFunc(ListenerMainName, prop, ...args).then();
|
||||
};
|
||||
}
|
||||
// 如果方法存在,正常返回
|
||||
@@ -58,8 +53,6 @@ export class LegacyNTEventWrapper {
|
||||
if (eventNameArr.length > 1) {
|
||||
const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', '');
|
||||
const eventName = eventNameArr[1];
|
||||
//getNodeIKernelGroupListener,GroupService
|
||||
//console.log('2', eventName);
|
||||
const services = (this.WrapperSession as unknown as eventType)[serviceName]();
|
||||
let event = services[eventName];
|
||||
//重新绑定this
|
||||
@@ -72,32 +65,29 @@ export class LegacyNTEventWrapper {
|
||||
}
|
||||
|
||||
createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
|
||||
const ListenerType = this.listenerMapping![listenerMainName];
|
||||
let Listener = this.listenerManager.get(listenerMainName + uniqueCode);
|
||||
if (!Listener && ListenerType) {
|
||||
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
|
||||
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
|
||||
const existListener = this.listenerManager.get(listenerMainName + uniqueCode);
|
||||
if (!existListener) {
|
||||
const Listener = this.createProxyDispatch(listenerMainName);
|
||||
const ServiceSubName = /^NodeIKernel(.*?)Listener$/.exec(listenerMainName)![1];
|
||||
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
|
||||
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
|
||||
addfunc!(Listener as T);
|
||||
//console.log(addfunc!(Listener as T));
|
||||
this.listenerManager.set(listenerMainName + uniqueCode, Listener);
|
||||
return Listener as T;
|
||||
}
|
||||
return Listener as T;
|
||||
return existListener as T;
|
||||
}
|
||||
|
||||
//统一回调清理事件
|
||||
async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
||||
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args);
|
||||
this.EventTask.get(ListenerMainName)
|
||||
?.get(ListenerSubName)
|
||||
?.forEach((task, uuid) => {
|
||||
//console.log(task.func, uuid, task.createtime, task.timeout);
|
||||
if (task.createtime + task.timeout < Date.now()) {
|
||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
|
||||
return;
|
||||
}
|
||||
if (task.checker && task.checker(...args)) {
|
||||
if (task?.checker?.(...args)) {
|
||||
task.func(...args);
|
||||
}
|
||||
});
|
||||
@@ -105,21 +95,10 @@ export class LegacyNTEventWrapper {
|
||||
|
||||
async callNoListenerEvent<EventType extends (...args: any[]) => Promise<any> | any>(
|
||||
EventName = '',
|
||||
timeout: number = 3000,
|
||||
...args: Parameters<EventType>
|
||||
) {
|
||||
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
||||
const EventFunc = this.createEventFunction<EventType>(EventName);
|
||||
let complete = false;
|
||||
setTimeout(() => {
|
||||
if (!complete) {
|
||||
reject(new Error('NTEvent EventName:' + EventName + ' timeout'));
|
||||
}
|
||||
}, timeout);
|
||||
const retData = await EventFunc!(...args);
|
||||
complete = true;
|
||||
resolve(retData);
|
||||
});
|
||||
): Promise<Awaited<ReturnType<EventType>>> {
|
||||
const EventFunc = this.createEventFunction<EventType>(EventName);
|
||||
return EventFunc!(...args);
|
||||
}
|
||||
|
||||
async RegisterListen<ListenerType extends (...args: any[]) => void>(
|
||||
@@ -166,7 +145,88 @@ export class LegacyNTEventWrapper {
|
||||
this.createListenerFunction(ListenerMainName);
|
||||
});
|
||||
}
|
||||
async CallNormalEventV2<
|
||||
EventType extends (...args: any[]) => Promise<any>,
|
||||
ListenerType extends (...args: any[]) => void
|
||||
>(
|
||||
EventName = '',
|
||||
ListenerName = '',
|
||||
waitTimes = 1,
|
||||
timeout: number = 3000,
|
||||
checkerEvent: (ret: Awaited<ReturnType<EventType>>) => boolean = () => true,
|
||||
checkerListener: (...args: Parameters<ListenerType>) => boolean = () => true,
|
||||
...args: Parameters<EventType>
|
||||
) {
|
||||
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
|
||||
async (resolve, reject) => {
|
||||
const id = randomUUID();
|
||||
let complete = 0;
|
||||
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||
let retEvent: any = {};
|
||||
const databack = () => {
|
||||
if (complete == 0) {
|
||||
reject(
|
||||
new Error(
|
||||
'Timeout: NTEvent EventName:' +
|
||||
EventName +
|
||||
' ListenerName:' +
|
||||
ListenerName +
|
||||
' EventRet:\n' +
|
||||
JSON.stringify(retEvent, null, 4) +
|
||||
'\n',
|
||||
),
|
||||
);
|
||||
} else {
|
||||
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
||||
}
|
||||
};
|
||||
|
||||
const ListenerNameList = ListenerName.split('/');
|
||||
const ListenerMainName = ListenerNameList[0];
|
||||
const ListenerSubName = ListenerNameList[1];
|
||||
|
||||
const Timeouter = setTimeout(databack, timeout);
|
||||
|
||||
const eventCallbak = {
|
||||
timeout: timeout,
|
||||
createtime: Date.now(),
|
||||
checker: checkerListener,
|
||||
func: (...args: any[]) => {
|
||||
complete++;
|
||||
retData = args as Parameters<ListenerType>;
|
||||
if (complete >= waitTimes) {
|
||||
clearTimeout(Timeouter);
|
||||
databack();
|
||||
}
|
||||
},
|
||||
};
|
||||
if (!this.EventTask.get(ListenerMainName)) {
|
||||
this.EventTask.set(ListenerMainName, new Map());
|
||||
}
|
||||
if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) {
|
||||
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
|
||||
}
|
||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
|
||||
this.createListenerFunction(ListenerMainName);
|
||||
const EventFunc = this.createEventFunction<EventType>(EventName);
|
||||
retEvent = await EventFunc!(...(args as any[]));
|
||||
if (!checkerEvent(retEvent)) {
|
||||
clearTimeout(Timeouter);
|
||||
reject(
|
||||
new Error(
|
||||
'EventChecker Failed: NTEvent EventName:' +
|
||||
EventName +
|
||||
' ListenerName:' +
|
||||
ListenerName +
|
||||
' EventRet:\n' +
|
||||
JSON.stringify(retEvent, null, 4) +
|
||||
'\n',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
async CallNormalEvent<
|
||||
EventType extends (...args: any[]) => Promise<any>,
|
||||
ListenerType extends (...args: any[]) => void
|
||||
@@ -236,28 +296,3 @@ export class LegacyNTEventWrapper {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 示例代码 快速创建事件
|
||||
// let NTEvent = new NTEventWrapper();
|
||||
// let TestEvent = NTEvent.CreatEventFunction<(force: boolean) => Promise<Number>>('NodeIKernelProfileLikeService/GetTest');
|
||||
// if (TestEvent) {
|
||||
// TestEvent(true);
|
||||
// }
|
||||
|
||||
// 示例代码 快速创建监听Listener类
|
||||
// let NTEvent = new NTEventWrapper();
|
||||
// NTEvent.CreatListenerFunction<NodeIKernelMsgListener>('NodeIKernelMsgListener', 'core')
|
||||
|
||||
// 调用接口
|
||||
//let NTEvent = new NTEventWrapper();
|
||||
//let ret = await NTEvent.CallNormalEvent<(force: boolean) => Promise<Number>, (data1: string, data2: number) => void>('NodeIKernelProfileLikeService/GetTest', 'NodeIKernelMsgListener/onAddSendMsg', 1, 3000, true);
|
||||
|
||||
// 注册监听 解除监听
|
||||
// NTEventDispatch.RigisterListener('NodeIKernelMsgListener/onAddSendMsg','core',cb);
|
||||
// NTEventDispatch.UnRigisterListener('NodeIKernelMsgListener/onAddSendMsg','core');
|
||||
|
||||
// let GetTest = NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode);
|
||||
// GetTest('test');
|
||||
|
||||
// always模式
|
||||
// NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode,(...args:any[])=>{ console.log(args) });
|
||||
|
@@ -6,6 +6,7 @@ export type ListenerClassBase = Record<string, string>;
|
||||
export interface ListenerIBase {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(listener: any): ListenerClassBase;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export class NTEventChannel extends EventEmitter {
|
||||
@@ -26,12 +27,11 @@ export class NTEventChannel extends EventEmitter {
|
||||
}
|
||||
|
||||
createProxyDispatch(ListenerMainName: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const current = this;
|
||||
const dispatcherListener = this.dispatcherListener.bind(this);
|
||||
return new Proxy({}, {
|
||||
get(_target: any, prop: any, _receiver: any) {
|
||||
return (...args: any[]) => {
|
||||
current.dispatcherListener.apply(current, [ListenerMainName + '/' + prop, ...args]);
|
||||
dispatcherListener(ListenerMainName + '/' + prop, ...args);
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -47,7 +47,7 @@ export class NTEventChannel extends EventEmitter {
|
||||
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
|
||||
if (!Listener) throw new Error('Init Listener failed');
|
||||
//实例化NTQQ Listener外包装
|
||||
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
|
||||
const ServiceSubName = /^NodeIKernel(.*?)Listener$/.exec(listenerMainName)![1];
|
||||
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
|
||||
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
|
||||
//添加Listener到NTQQ
|
||||
@@ -124,14 +124,7 @@ export class NTEventChannel extends EventEmitter {
|
||||
EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
||||
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
||||
const EventFunc = this.createEventFunction<EventType>(EventName);
|
||||
let complete = false;
|
||||
const Timeouter = setTimeout(() => {
|
||||
if (!complete) {
|
||||
reject(new Error('NTEvent EventName:' + EventName + ' timeout'));
|
||||
}
|
||||
}, timeout);
|
||||
const retData = await EventFunc!(...args);
|
||||
complete = true;
|
||||
resolve(retData);
|
||||
});
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ import path, { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs';
|
||||
|
||||
export const napcat_version = '2.0.4';
|
||||
export const napcat_version = '2.2.11';
|
||||
|
||||
export class NapCatPathWrapper {
|
||||
binaryPath: string;
|
||||
|
@@ -53,7 +53,7 @@ export abstract class ConfigBase<T> {
|
||||
}
|
||||
|
||||
|
||||
save(newConfigData: T = this.configData as T) {
|
||||
save(newConfigData: T = this.configData) {
|
||||
const logger = this.coreContext.context.logger;
|
||||
const selfInfo = this.coreContext.selfInfo;
|
||||
this.configData = newConfigData;
|
||||
|
@@ -15,23 +15,13 @@ export class LimitedHashTable<K, V> {
|
||||
}
|
||||
|
||||
set(key: K, value: V): void {
|
||||
// const isExist = this.keyToValue.get(key);
|
||||
// if (isExist && isExist === value) {
|
||||
// return;
|
||||
// }
|
||||
this.keyToValue.set(key, value);
|
||||
this.valueToKey.set(value, key);
|
||||
while (this.keyToValue.size !== this.valueToKey.size) {
|
||||
//console.log('keyToValue.size !== valueToKey.size Error Atom');
|
||||
this.keyToValue.clear();
|
||||
this.valueToKey.clear();
|
||||
}
|
||||
// console.log('---------------');
|
||||
// console.log(this.keyToValue);
|
||||
// console.log(this.valueToKey);
|
||||
// console.log('---------------');
|
||||
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
|
||||
//console.log(this.keyToValue.size > this.maxSize, this.valueToKey.size > this.maxSize);
|
||||
const oldestKey = this.keyToValue.keys().next().value;
|
||||
this.valueToKey.delete(this.keyToValue.get(oldestKey)!);
|
||||
this.keyToValue.delete(oldestKey);
|
||||
@@ -101,17 +91,13 @@ class MessageUniqueWrapper {
|
||||
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
|
||||
}
|
||||
|
||||
createMsg(peer: Peer, msgId: string): number | undefined {
|
||||
createMsg(peer: Peer, msgId: string) {
|
||||
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||
const hash = crypto.createHash('md5').update(key).digest();
|
||||
//设置第一个bit为0 保证shortId为正数
|
||||
hash[0] &= 0x7f;
|
||||
const shortId = hash.readInt32BE(0);
|
||||
//减少性能损耗
|
||||
// const isExist = this.msgIdMap.getKey(shortId);
|
||||
// if (isExist && isExist === msgId) {
|
||||
// return shortId;
|
||||
// }
|
||||
this.msgIdMap.set(msgId, shortId);
|
||||
this.msgDataMap.set(key, shortId);
|
||||
return shortId;
|
||||
|
@@ -46,7 +46,7 @@ export class QQBasicInfoWrapper {
|
||||
}
|
||||
|
||||
requireMinNTQQBuild(buildStr: string) {
|
||||
const currentBuild = parseInt(this.getQQBuildStr() || '0');
|
||||
const currentBuild = +(this.getQQBuildStr() ?? '0');
|
||||
if (currentBuild == 0) throw new Error('QQBuildStr获取失败');
|
||||
return currentBuild >= parseInt(buildStr);
|
||||
}
|
||||
@@ -74,8 +74,6 @@ export class QQBasicInfoWrapper {
|
||||
this.context.logger.log(
|
||||
`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`,
|
||||
);
|
||||
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: this.getQUAInternal() };
|
||||
return { appid: systemPlatform === 'linux' ? '537240795' : '537240709', qua: this.getQUAInternal() };
|
||||
}
|
||||
}
|
||||
|
||||
export let QQBasicInfo: QQBasicInfoWrapper | undefined;
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import fs from 'fs';
|
||||
import fsPromise, { stat } from 'fs/promises';
|
||||
import { stat } from 'fs/promises';
|
||||
import crypto, { randomUUID } from 'crypto';
|
||||
import util from 'util';
|
||||
import path from 'node:path';
|
||||
import * as fileType from 'file-type';
|
||||
import { LogWrapper } from './log';
|
||||
import { solveProblem } from './helper';
|
||||
|
||||
export function isGIF(path: string) {
|
||||
const buffer = Buffer.alloc(4);
|
||||
@@ -13,7 +13,6 @@ export function isGIF(path: string) {
|
||||
fs.closeSync(fd);
|
||||
return buffer.toString() === 'GIF8';
|
||||
}
|
||||
|
||||
// 定义一个异步函数来检查文件是否存在
|
||||
export function checkFileReceived(path: string, timeout: number = 3000): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -94,7 +93,6 @@ export async function file2base64(path: string) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export function calculateFileMD5(filePath: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 创建一个流式读取器
|
||||
@@ -166,136 +164,109 @@ type Uri2LocalRes = {
|
||||
isLocal: boolean
|
||||
}
|
||||
|
||||
export async function uri2local(TempDir: string, UriOrPath: string, fileName: string | null = null): Promise<Uri2LocalRes> {
|
||||
const res = {
|
||||
success: false,
|
||||
errMsg: '',
|
||||
fileName: '',
|
||||
ext: '',
|
||||
path: '',
|
||||
isLocal: false,
|
||||
};
|
||||
if (!fileName) fileName = randomUUID();
|
||||
let filePath = path.join(TempDir, fileName);//临时目录
|
||||
let url = null;
|
||||
//区分path和uri
|
||||
export async function checkFileV2(filePath: string) {
|
||||
try {
|
||||
if (fs.existsSync(UriOrPath)) url = new URL('file://' + UriOrPath);
|
||||
} catch (error: any) {
|
||||
}
|
||||
try {
|
||||
url = new URL(UriOrPath);
|
||||
} catch (error: any) {
|
||||
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
|
||||
if (ext) {
|
||||
fs.renameSync(filePath, filePath + `.${ext}`);
|
||||
filePath += `.${ext}`;
|
||||
return { success: true, ext: ext, path: filePath };
|
||||
}
|
||||
} catch (e) {
|
||||
// log("获取文件类型失败", filePath,e.stack)
|
||||
}
|
||||
return { success: false, ext: '', path: filePath };
|
||||
}
|
||||
|
||||
//验证url
|
||||
if (!url) {
|
||||
res.errMsg = `UriOrPath ${UriOrPath} 解析失败,可能${UriOrPath}不存在`;
|
||||
return res;
|
||||
}
|
||||
export enum FileUriType {
|
||||
Unknown = 0,
|
||||
Local = 1,
|
||||
Remote = 2,
|
||||
Base64 = 3
|
||||
}
|
||||
|
||||
if (url.protocol == 'base64:') {
|
||||
// base64转成文件
|
||||
const base64Data = UriOrPath.split('base64://')[1];
|
||||
try {
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
fs.writeFileSync(filePath, buffer);
|
||||
} catch (e: any) {
|
||||
res.errMsg = 'base64文件下载失败,' + e.toString();
|
||||
return res;
|
||||
export async function checkUriType(Uri: string) {
|
||||
|
||||
const LocalFileRet = await solveProblem((uri: string) => {
|
||||
if (fs.existsSync(uri)) {
|
||||
return { Uri: uri, Type: FileUriType.Local };
|
||||
}
|
||||
} else if (url.protocol == 'http:' || url.protocol == 'https:') {
|
||||
// 下载文件
|
||||
let buffer: Buffer | null = null;
|
||||
try {
|
||||
buffer = await httpDownload(UriOrPath);
|
||||
} catch (e: any) {
|
||||
res.errMsg = `${url}下载失败,` + e.toString();
|
||||
return res;
|
||||
return undefined;
|
||||
}, Uri);
|
||||
if (LocalFileRet) return LocalFileRet;
|
||||
|
||||
const OtherFileRet = await solveProblem((uri: string) => {
|
||||
//再判断是否是Http
|
||||
if (uri.startsWith('http://') || uri.startsWith('https://')) {
|
||||
return { Uri: uri, Type: FileUriType.Remote };
|
||||
}
|
||||
try {
|
||||
const pathInfo = path.parse(decodeURIComponent(url.pathname));
|
||||
if (pathInfo.name) {
|
||||
fileName = pathInfo.name;
|
||||
if (pathInfo.ext) {
|
||||
fileName += pathInfo.ext;
|
||||
// res.ext = pathInfo.ext
|
||||
}
|
||||
}
|
||||
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
|
||||
res.fileName = fileName;
|
||||
filePath = path.join(TempDir, randomUUID() + fileName);
|
||||
fs.writeFileSync(filePath, buffer);
|
||||
} catch (e: any) {
|
||||
res.errMsg = `${url}下载失败,` + e.toString();
|
||||
return res;
|
||||
//再判断是否是Base64
|
||||
if (uri.startsWith('base64://')) {
|
||||
return { Uri: uri, Type: FileUriType.Base64 };
|
||||
}
|
||||
} else {
|
||||
let pathname: string;
|
||||
if (url.protocol === 'file:') {
|
||||
if (uri.startsWith('file://')) {
|
||||
let filePath: string;
|
||||
// await fs.copyFile(url.pathname, filePath);
|
||||
pathname = decodeURIComponent(url.pathname);
|
||||
const pathname = decodeURIComponent(new URL(uri).pathname);
|
||||
if (process.platform === 'win32') {
|
||||
filePath = pathname.slice(1);
|
||||
} else {
|
||||
filePath = pathname;
|
||||
}
|
||||
} else {
|
||||
// 26702执行forword file文件操作 不应该在这里乱来
|
||||
// const cache = await dbUtil.getFileCacheByName(uri);
|
||||
// if (cache) {
|
||||
// filePath = cache.path;
|
||||
// } else {
|
||||
// filePath = uri;
|
||||
// }
|
||||
return { Uri: filePath, Type: FileUriType.Local };
|
||||
}
|
||||
res.isLocal = true;
|
||||
}
|
||||
// else{
|
||||
// res.errMsg = `不支持的file协议,` + url.protocol
|
||||
// return res
|
||||
// }
|
||||
// if (isGIF(filePath) && !res.isLocal) {
|
||||
// await fs.rename(filePath, filePath + ".gif");
|
||||
// filePath += ".gif";
|
||||
// }
|
||||
if (!res.isLocal && !res.ext) {
|
||||
try {
|
||||
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
|
||||
if (ext) {
|
||||
fs.renameSync(filePath, filePath + `.${ext}`);
|
||||
filePath += `.${ext}`;
|
||||
res.fileName += `.${ext}`;
|
||||
res.ext = ext;
|
||||
}
|
||||
} catch (e) {
|
||||
// log("获取文件类型失败", filePath,e.stack)
|
||||
}
|
||||
}
|
||||
res.success = true;
|
||||
res.path = filePath;
|
||||
return res;
|
||||
}, Uri);
|
||||
if (OtherFileRet) return OtherFileRet;
|
||||
|
||||
return { Uri: Uri, Type: FileUriType.Unknown };
|
||||
}
|
||||
|
||||
export async function copyFolder(sourcePath: string, destPath: string, logger: LogWrapper) {
|
||||
try {
|
||||
const entries = await fsPromise.readdir(sourcePath, { withFileTypes: true });
|
||||
await fsPromise.mkdir(destPath, { recursive: true });
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(sourcePath, entry.name);
|
||||
const dstPath = path.join(destPath, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await copyFolder(srcPath, dstPath, logger);
|
||||
} else {
|
||||
try {
|
||||
await fsPromise.copyFile(srcPath, dstPath);
|
||||
} catch (error) {
|
||||
logger.logError(`无法复制文件 '${srcPath}' 到 '${dstPath}': ${error}`);
|
||||
// 这里可以决定是否要继续复制其他文件
|
||||
}
|
||||
export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise<Uri2LocalRes> {
|
||||
const { Uri: HandledUri, Type: UriType } = await checkUriType(uri);
|
||||
//解析失败
|
||||
|
||||
if (UriType == FileUriType.Unknown) {
|
||||
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
|
||||
}
|
||||
//解析File协议和本地文件
|
||||
if (UriType == FileUriType.Local) {
|
||||
const fileExt = path.extname(HandledUri);
|
||||
const filename = path.basename(HandledUri, fileExt);
|
||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: HandledUri, isLocal: true };
|
||||
}
|
||||
//接下来都要有文件名
|
||||
if (!filename) filename = randomUUID();
|
||||
//解析Http和Https协议
|
||||
|
||||
if (UriType == FileUriType.Remote) {
|
||||
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
|
||||
if (pathInfo.name) {
|
||||
filename = pathInfo.name;
|
||||
if (pathInfo.ext) {
|
||||
filename += pathInfo.ext;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.logError('复制文件夹时出错:', error);
|
||||
filename = filename.replace(/[/\\:*?"<>|]/g, '_');
|
||||
const fileExt = path.extname(HandledUri);
|
||||
const filePath = path.join(dir, filename);
|
||||
const buffer = await httpDownload(HandledUri);
|
||||
fs.writeFileSync(filePath, buffer);
|
||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
|
||||
}
|
||||
}
|
||||
//解析Base64
|
||||
if (UriType == FileUriType.Base64) {
|
||||
const base64 = HandledUri.replace(/^base64:\/\//, '');
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
let filePath = path.join(dir, filename);
|
||||
let fileExt = '';
|
||||
fs.writeFileSync(filePath, buffer);
|
||||
const { success, ext, path: fileTypePath } = await checkFileV2(filePath);
|
||||
if (success) {
|
||||
filePath = fileTypePath;
|
||||
fileExt = ext;
|
||||
filename = filename + '.' + ext;
|
||||
}
|
||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
|
||||
}
|
||||
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
|
||||
}
|
@@ -1,12 +1,29 @@
|
||||
import crypto from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import fs from 'fs';
|
||||
import * as fsPromise from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import { QQLevel } from '@/core';
|
||||
|
||||
//下面这个类是用于将uid+msgid合并的类
|
||||
export async function solveProblem<T extends (...arg: any[]) => any>(func: T, ...args: Parameters<T>): Promise<ReturnType<T> | undefined> {
|
||||
return new Promise<ReturnType<T> | undefined>((resolve) => {
|
||||
try {
|
||||
const result = func(...args);
|
||||
resolve(result);
|
||||
} catch (e) {
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function solveAsyncProblem<T extends (...args: any[]) => Promise<any>>(func: T, ...args: Parameters<T>): Promise<Awaited<ReturnType<T>> | undefined> {
|
||||
return new Promise<Awaited<ReturnType<T>> | undefined>((resolve) => {
|
||||
func(...args).then((result) => {
|
||||
resolve(result);
|
||||
}).catch((e) => {
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
//下面这个类是用于将uid+msgid合并的类
|
||||
export class UUIDConverter {
|
||||
static encode(highStr: string, lowStr: string): string {
|
||||
const high = BigInt(highStr);
|
||||
@@ -53,12 +70,6 @@ export async function runAllWithTimeout<T>(tasks: Promise<T>[], timeout: number)
|
||||
.map((result) => (result as { status: 'fulfilled'; value: T }).value);
|
||||
}
|
||||
|
||||
export function getMd5(s: string) {
|
||||
const h = crypto.createHash('md5');
|
||||
h.update(s);
|
||||
return h.digest('hex');
|
||||
}
|
||||
|
||||
export function isNull(value: any) {
|
||||
return value === undefined || value === null;
|
||||
}
|
||||
@@ -103,19 +114,19 @@ export function isEqual(obj1: any, obj2: any) {
|
||||
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
|
||||
if (os.platform() === 'linux') {
|
||||
return {
|
||||
baseVersion: '3.2.12-26702',
|
||||
curVersion: '3.2.12-26702',
|
||||
baseVersion: '3.2.12-27254',
|
||||
curVersion: '3.2.12-27254',
|
||||
prevVersion: '',
|
||||
onErrorVersions: [],
|
||||
buildId: '26702',
|
||||
buildId: '27254',
|
||||
};
|
||||
}
|
||||
return {
|
||||
baseVersion: '9.9.15-26702',
|
||||
curVersion: '9.9.15-26702',
|
||||
baseVersion: '9.9.15-27254',
|
||||
curVersion: '9.9.15-27254',
|
||||
prevVersion: '',
|
||||
onErrorVersions: [],
|
||||
buildId: '26702',
|
||||
buildId: '27254',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -137,28 +148,6 @@ export function getQQVersionConfigPath(exePath: string = ''): string | undefined
|
||||
return configVersionInfoPath;
|
||||
}
|
||||
|
||||
export async function deleteOldFiles(directoryPath: string, daysThreshold: number) {
|
||||
try {
|
||||
const files = await fsPromise.readdir(directoryPath);
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(directoryPath, file);
|
||||
const stats = await fsPromise.stat(filePath);
|
||||
const lastModifiedTime = stats.mtimeMs;
|
||||
const currentTime = Date.now();
|
||||
const timeDifference = currentTime - lastModifiedTime;
|
||||
const daysDifference = timeDifference / (1000 * 60 * 60 * 24);
|
||||
|
||||
if (daysDifference > daysThreshold) {
|
||||
await fsPromise.unlink(filePath); // Delete the file
|
||||
//console.log(`Deleted: ${filePath}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
//console.error('Error deleting files:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export function calcQQLevel(level: QQLevel) {
|
||||
const { crownNum, sunNum, moonNum, starNum } = level;
|
||||
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;
|
||||
|
@@ -2,6 +2,7 @@ import log4js, { Configuration } from 'log4js';
|
||||
import { truncateString } from '@/common/utils/helper';
|
||||
import path from 'node:path';
|
||||
import chalk from 'chalk';
|
||||
import { AtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/core';
|
||||
|
||||
export enum LogLevel {
|
||||
DEBUG = 'debug',
|
||||
@@ -135,4 +136,104 @@ export class LogWrapper {
|
||||
logFatal(...args: any[]) {
|
||||
this._log(LogLevel.FATAL, ...args);
|
||||
}
|
||||
|
||||
logMessage(msg: RawMessage, selfInfo: SelfInfo) {
|
||||
const isSelfSent = msg.senderUin === selfInfo.uin;
|
||||
this.log(`${
|
||||
isSelfSent ? '发送 ->' : '接收 <-'
|
||||
} ${rawMessageToText(msg)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
||||
if (recursiveLevel > 2) {
|
||||
return '...';
|
||||
}
|
||||
|
||||
const tokens: string[] = [];
|
||||
|
||||
if (msg.chatType == ChatType.KCHATTYPEC2C) {
|
||||
tokens.push(`私聊 (${msg.peerUin})`);
|
||||
} else if (msg.chatType == ChatType.KCHATTYPEGROUP) {
|
||||
tokens.push(`群聊 (群 ${msg.peerUin} 的 ${msg.senderUin})`);
|
||||
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
|
||||
tokens.push('移动设备');
|
||||
} else /* temp */ {
|
||||
tokens.push(`临时消息 (${msg.peerUin})`);
|
||||
}
|
||||
|
||||
// message content
|
||||
|
||||
function msgElementToText(element: MessageElement) {
|
||||
if (element.textElement) {
|
||||
if (element.textElement.atType === AtType.notAt) {
|
||||
return element.textElement.content;
|
||||
} else if (element.textElement.atType === AtType.atAll) {
|
||||
return `@全体成员`;
|
||||
} else if (element.textElement.atType === AtType.atUser) {
|
||||
return `${element.textElement.content} (${element.textElement.atUid})`;
|
||||
}
|
||||
}
|
||||
|
||||
if (element.replyElement) {
|
||||
const recordMsgOrNull = msg.records.find(
|
||||
record => element.replyElement!.sourceMsgIdInRecords === record.msgId
|
||||
);
|
||||
return `[回复消息 ${
|
||||
recordMsgOrNull &&
|
||||
recordMsgOrNull.peerUin != '284840486' // 非转发消息; 否则定位不到
|
||||
?
|
||||
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
|
||||
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
|
||||
}]`;
|
||||
}
|
||||
|
||||
if (element.picElement) {
|
||||
return `[图片 ${element.picElement.fileName}]`;
|
||||
}
|
||||
|
||||
if (element.fileElement) {
|
||||
return `[文件 ${element.fileElement.fileName}]`;
|
||||
}
|
||||
|
||||
if (element.videoElement) {
|
||||
return `[视频 ${element.videoElement.fileName}]`;
|
||||
}
|
||||
|
||||
if (element.pttElement) {
|
||||
return `[语音 ${element.pttElement.duration}s]`;
|
||||
}
|
||||
|
||||
if (element.arkElement) {
|
||||
return `[卡片消息 ${element.arkElement.bytesData}]`;
|
||||
}
|
||||
|
||||
if (element.faceElement) {
|
||||
return `[表情 ${element.faceElement.faceText ?? ''}]`;
|
||||
}
|
||||
|
||||
if (element.marketFaceElement) {
|
||||
return `[商城表情 ${element.marketFaceElement.faceName}]`;
|
||||
}
|
||||
|
||||
if (element.markdownElement) {
|
||||
return `[Markdown ${element.markdownElement.content}]`;
|
||||
}
|
||||
|
||||
if (element.multiForwardMsgElement) {
|
||||
return `[转发消息]`;
|
||||
}
|
||||
|
||||
if (element.elementType === ElementType.GreyTip) {
|
||||
return `[灰条消息]`; // TODO: resolve the text
|
||||
}
|
||||
|
||||
return `[未实现 (ElementType = ${element.elementType})]`;
|
||||
}
|
||||
|
||||
for (const element of msg.elements) {
|
||||
tokens.push(msgElementToText(element));
|
||||
}
|
||||
|
||||
return tokens.join(' ');
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ import { LogWrapper } from './log';
|
||||
export function proxyHandlerOf(logger: LogWrapper) {
|
||||
return {
|
||||
get(target: any, prop: any, receiver: any) {
|
||||
// console.log('get', prop, typeof target[prop]);
|
||||
if (typeof target[prop] === 'undefined') {
|
||||
// 如果方法不存在,返回一个函数,这个函数调用existentMethod
|
||||
return (..._args: unknown[]) => {
|
||||
|
@@ -30,7 +30,7 @@ export async function getMachineId(): Promise<string> {
|
||||
if (!machineId) {
|
||||
machineId = (async () => {
|
||||
const id = await getMacMachineId();
|
||||
return id || randomUUID(); // fallback, generate a UUID
|
||||
return id ?? randomUUID(); // fallback, generate a UUID
|
||||
})();
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@@ -1,17 +1,4 @@
|
||||
interface IDependsAdapter {
|
||||
onMSFStatusChange(arg1: number, arg2: number): void;
|
||||
|
||||
onMSFSsoError(args: unknown): void;
|
||||
|
||||
getGroupCode(args: unknown): void;
|
||||
}
|
||||
|
||||
export interface NodeIDependsAdapter extends IDependsAdapter {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(adapter: IDependsAdapter): NodeIDependsAdapter;
|
||||
}
|
||||
|
||||
export class DependsAdapter implements IDependsAdapter {
|
||||
export class NodeIDependsAdapter {
|
||||
onMSFStatusChange(arg1: number, arg2: number) {
|
||||
// console.log(arg1, arg2);
|
||||
// if (arg1 == 2 && arg2 == 2) {
|
||||
|
@@ -1,17 +1,4 @@
|
||||
interface IDispatcherAdapter {
|
||||
dispatchRequest(arg: unknown): void;
|
||||
|
||||
dispatchCall(arg: unknown): void;
|
||||
|
||||
dispatchCallWithJson(arg: unknown): void;
|
||||
}
|
||||
|
||||
export interface NodeIDispatcherAdapter extends IDispatcherAdapter {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(adapter: IDispatcherAdapter): NodeIDispatcherAdapter;
|
||||
}
|
||||
|
||||
export class DispatcherAdapter implements IDispatcherAdapter {
|
||||
export class NodeIDispatcherAdapter {
|
||||
dispatchRequest(arg: unknown) {
|
||||
}
|
||||
|
||||
|
@@ -1,27 +1,4 @@
|
||||
interface IGlobalAdapter {
|
||||
onLog(...args: unknown[]): void;
|
||||
|
||||
onGetSrvCalTime(...args: unknown[]): void;
|
||||
|
||||
onShowErrUITips(...args: unknown[]): void;
|
||||
|
||||
fixPicImgType(...args: unknown[]): void;
|
||||
|
||||
getAppSetting(...args: unknown[]): void;
|
||||
|
||||
onInstallFinished(...args: unknown[]): void;
|
||||
|
||||
onUpdateGeneralFlag(...args: unknown[]): void;
|
||||
|
||||
onGetOfflineMsg(...args: unknown[]): void;
|
||||
}
|
||||
|
||||
export interface NodeIGlobalAdapter extends IGlobalAdapter {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(adapter: IGlobalAdapter): NodeIGlobalAdapter;
|
||||
}
|
||||
|
||||
export class GlobalAdapter implements IGlobalAdapter {
|
||||
export class NodeIGlobalAdapter {
|
||||
onLog(...args: unknown[]) {
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,11 @@ import {
|
||||
IMAGE_HTTP_HOST_NT,
|
||||
Peer,
|
||||
PicElement,
|
||||
PicType,
|
||||
SendFileElement,
|
||||
SendPicElement,
|
||||
SendPttElement,
|
||||
SendVideoElement,
|
||||
} from '@/core/entities';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
@@ -18,7 +23,12 @@ import imageSize from 'image-size';
|
||||
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
||||
import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService';
|
||||
import { RkeyManager } from '../helper/rkey';
|
||||
import { calculateFileMD5 } from '@/common/utils/file';
|
||||
import { calculateFileMD5, isGIF } from '@/common/utils/file';
|
||||
import pathLib from 'node:path';
|
||||
import { defaultVideoThumbB64, getVideoInfo } from '@/common/utils/video';
|
||||
import ffmpeg from 'fluent-ffmpeg';
|
||||
import fsnormal from 'node:fs';
|
||||
import { encodeSilk } from '@/common/utils/audio';
|
||||
|
||||
|
||||
export class NTQQFileApi {
|
||||
@@ -84,6 +94,198 @@ export class NTQQFileApi {
|
||||
};
|
||||
}
|
||||
|
||||
async createValidSendFileElement(
|
||||
filePath: string,
|
||||
fileName: string = '',
|
||||
folderId: string = ''
|
||||
): Promise<SendFileElement> {
|
||||
const { fileName: _fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.FILE);
|
||||
if (fileSize === 0) {
|
||||
throw new Error('文件异常,大小为0');
|
||||
}
|
||||
return {
|
||||
elementType: ElementType.FILE,
|
||||
elementId: '',
|
||||
fileElement: {
|
||||
fileName: fileName || _fileName,
|
||||
folderId: folderId,
|
||||
filePath: path,
|
||||
fileSize: (fileSize).toString(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async createValidSendPicElement(
|
||||
picPath: string,
|
||||
summary: string = '',
|
||||
subType: 0 | 1 = 0
|
||||
): Promise<SendPicElement> {
|
||||
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
|
||||
if (fileSize === 0) {
|
||||
throw new Error('文件异常,大小为0');
|
||||
}
|
||||
const imageSize = await this.core.apis.FileApi.getImageSize(picPath);
|
||||
const picElement: any = {
|
||||
md5HexStr: md5,
|
||||
fileSize: fileSize.toString(),
|
||||
picWidth: imageSize?.width,
|
||||
picHeight: imageSize?.height,
|
||||
fileName: fileName,
|
||||
sourcePath: path,
|
||||
original: true,
|
||||
picType: isGIF(picPath) ? PicType.gif : PicType.jpg,
|
||||
picSubType: subType,
|
||||
fileUuid: '',
|
||||
fileSubId: '',
|
||||
thumbFileSize: 0,
|
||||
summary,
|
||||
};
|
||||
return {
|
||||
elementType: ElementType.PIC,
|
||||
elementId: '',
|
||||
picElement,
|
||||
};
|
||||
}
|
||||
|
||||
async createValidSendVideoElement(
|
||||
filePath: string,
|
||||
fileName: string = '',
|
||||
diyThumbPath: string = '',
|
||||
): Promise<SendVideoElement> {
|
||||
const logger = this.core.context.logger;
|
||||
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
|
||||
if (fileSize === 0) {
|
||||
throw new Error('文件异常,大小为0');
|
||||
}
|
||||
let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
|
||||
thumb = pathLib.dirname(thumb);
|
||||
let videoInfo = {
|
||||
width: 1920, height: 1080,
|
||||
time: 15,
|
||||
format: 'mp4',
|
||||
size: fileSize,
|
||||
filePath,
|
||||
};
|
||||
try {
|
||||
videoInfo = await getVideoInfo(path, logger);
|
||||
} catch (e) {
|
||||
logger.logError('获取视频信息失败', e);
|
||||
}
|
||||
const createThumb = new Promise<string | undefined>((resolve, reject) => {
|
||||
const thumbFileName = `${md5}_0.png`;
|
||||
const thumbPath = pathLib.join(thumb, thumbFileName);
|
||||
ffmpeg(filePath)
|
||||
.on('error', (err) => {
|
||||
logger.logDebug('获取视频封面失败,使用默认封面', err);
|
||||
if (diyThumbPath) {
|
||||
fsPromises.copyFile(diyThumbPath, thumbPath).then(() => {
|
||||
resolve(thumbPath);
|
||||
}).catch(reject);
|
||||
} else {
|
||||
fsnormal.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
|
||||
resolve(thumbPath);
|
||||
}
|
||||
})
|
||||
.screenshots({
|
||||
timestamps: [0],
|
||||
filename: thumbFileName,
|
||||
folder: thumb,
|
||||
size: videoInfo.width + 'x' + videoInfo.height,
|
||||
}).on('end', () => {
|
||||
resolve(thumbPath);
|
||||
});
|
||||
});
|
||||
const thumbPath = new Map();
|
||||
const _thumbPath = await createThumb;
|
||||
const thumbSize = _thumbPath ? (await fsPromises.stat(_thumbPath)).size : 0;
|
||||
// log("生成缩略图", _thumbPath)
|
||||
thumbPath.set(0, _thumbPath);
|
||||
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : "";
|
||||
// "fileElement": {
|
||||
// "fileMd5": "",
|
||||
// "fileName": "1.mp4",
|
||||
// "filePath": "C:\\Users\\nanae\\OneDrive\\Desktop\\1.mp4",
|
||||
// "fileSize": "1847007",
|
||||
// "picHeight": 1280,
|
||||
// "picWidth": 720,
|
||||
// "picThumbPath": {},
|
||||
// "file10MMd5": "",
|
||||
// "fileSha": "",
|
||||
// "fileSha3": "",
|
||||
// "fileUuid": "",
|
||||
// "fileSubId": "",
|
||||
// "thumbFileSize": 750
|
||||
// }
|
||||
return {
|
||||
elementType: ElementType.VIDEO,
|
||||
elementId: '',
|
||||
videoElement: {
|
||||
fileName: fileName || _fileName,
|
||||
filePath: path,
|
||||
videoMd5: md5,
|
||||
thumbMd5,
|
||||
fileTime: videoInfo.time,
|
||||
thumbPath: thumbPath,
|
||||
thumbSize,
|
||||
thumbWidth: videoInfo.width,
|
||||
thumbHeight: videoInfo.height,
|
||||
fileSize: '' + fileSize,
|
||||
// fileFormat: videotype
|
||||
// fileUuid: "",
|
||||
// transferStatus: 0,
|
||||
// progress: 0,
|
||||
// invalidState: 0,
|
||||
// fileSubId: "",
|
||||
// fileBizId: null,
|
||||
// originVideoMd5: "",
|
||||
// fileFormat: 2,
|
||||
// import_rich_media_context: null,
|
||||
// sourceVideoCodecFormat: 2
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async createValidSendPttElement(pttPath: string): Promise<SendPttElement> {
|
||||
const {
|
||||
converted,
|
||||
path: silkPath,
|
||||
duration,
|
||||
} = await encodeSilk(pttPath, this.core.NapCatTempPath, this.core.context.logger);
|
||||
// 生成语音 Path: silkPath Time: duration
|
||||
if (!silkPath) {
|
||||
throw new Error('语音转换失败, 请检查语音文件是否正常');
|
||||
}
|
||||
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(silkPath, ElementType.PTT);
|
||||
if (fileSize === 0) {
|
||||
throw new Error('文件异常,大小为0');
|
||||
}
|
||||
if (converted) {
|
||||
fsPromises.unlink(silkPath);
|
||||
}
|
||||
return {
|
||||
elementType: ElementType.PTT,
|
||||
elementId: '',
|
||||
pttElement: {
|
||||
fileName: fileName,
|
||||
filePath: path,
|
||||
md5HexStr: md5,
|
||||
fileSize: fileSize,
|
||||
// duration: Math.max(1, Math.round(fileSize / 1024 / 3)), // 一秒钟大概是3kb大小, 小于1秒的按1秒算
|
||||
duration: duration ?? 1,
|
||||
formatType: 1,
|
||||
voiceType: 1,
|
||||
voiceChangeType: 0,
|
||||
canConvert2Text: true,
|
||||
waveAmplitudes: [
|
||||
0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17,
|
||||
],
|
||||
fileSubId: '',
|
||||
playState: 1,
|
||||
autoConvertText: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async downloadMediaByUuid() {
|
||||
//napCatCore.session.getRichMediaService().downloadFileForFileUuid();
|
||||
}
|
||||
@@ -117,31 +319,31 @@ export class NTQQFileApi {
|
||||
filePath: string
|
||||
}) => Promise<unknown>,
|
||||
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/downloadRichMedia',
|
||||
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||
1,
|
||||
timeout,
|
||||
(arg: OnRichMediaDownloadCompleteParams) => {
|
||||
if (arg.msgId === msgId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
{
|
||||
fileModelId: '0',
|
||||
downloadSourceType: 0,
|
||||
triggerType: 1,
|
||||
msgId: msgId,
|
||||
chatType: chatType,
|
||||
peerUid: peerUid,
|
||||
elementId: elementId,
|
||||
thumbSize: 0,
|
||||
downloadType: 1,
|
||||
filePath: thumbPath,
|
||||
},
|
||||
);
|
||||
let msg = await this.core.apis.MsgApi.getMsgsByMsgId({
|
||||
>(
|
||||
'NodeIKernelMsgService/downloadRichMedia',
|
||||
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||
1,
|
||||
timeout,
|
||||
(arg: OnRichMediaDownloadCompleteParams) => {
|
||||
if (arg.msgId === msgId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
{
|
||||
fileModelId: '0',
|
||||
downloadSourceType: 0,
|
||||
triggerType: 1,
|
||||
msgId: msgId,
|
||||
chatType: chatType,
|
||||
peerUid: peerUid,
|
||||
elementId: elementId,
|
||||
thumbSize: 0,
|
||||
downloadType: 1,
|
||||
filePath: thumbPath,
|
||||
},
|
||||
);
|
||||
const msg = await this.core.apis.MsgApi.getMsgsByMsgId({
|
||||
guildId: '',
|
||||
chatType: chatType,
|
||||
peerUid: peerUid,
|
||||
@@ -150,16 +352,16 @@ export class NTQQFileApi {
|
||||
return data[1].filePath;
|
||||
}
|
||||
//获取原始消息
|
||||
let FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId);
|
||||
const FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId);
|
||||
if (!FileElements) {
|
||||
//失败则就乱来 Todo
|
||||
return data[1].filePath;
|
||||
}
|
||||
//从原始消息获取文件路径
|
||||
let filePath =
|
||||
FileElements?.fileElement?.filePath ||
|
||||
FileElements?.pttElement?.filePath ||
|
||||
FileElements?.videoElement?.filePath ||
|
||||
const filePath =
|
||||
FileElements?.fileElement?.filePath ??
|
||||
FileElements?.pttElement?.filePath ??
|
||||
FileElements?.videoElement?.filePath ??
|
||||
FileElements?.picElement?.sourcePath;
|
||||
return filePath;
|
||||
}
|
||||
@@ -179,7 +381,7 @@ export class NTQQFileApi {
|
||||
async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) {
|
||||
let GroupData;
|
||||
let BuddyData;
|
||||
if (peer.chatType === ChatType.group) {
|
||||
if (peer.chatType === ChatType.KCHATTYPEGROUP) {
|
||||
GroupData =
|
||||
[{
|
||||
groupCode: peer.peerUid,
|
||||
@@ -189,7 +391,7 @@ export class NTQQFileApi {
|
||||
groupName: 'NapCat.Cached',
|
||||
remark: 'NapCat.Cached',
|
||||
}];
|
||||
} else if (peer.chatType === ChatType.friend) {
|
||||
} else if (peer.chatType === ChatType.KCHATTYPEC2C) {
|
||||
BuddyData = [{
|
||||
category_name: 'NapCat.Cached',
|
||||
peerUid: peer.peerUid,
|
||||
@@ -280,10 +482,10 @@ export class NTQQFileApi {
|
||||
let id = '';
|
||||
const Listener = this.core.eventWrapper.RegisterListen<(params: OnListener) => void>
|
||||
(
|
||||
'NodeIKernelSearchListener/onSearchFileKeywordsResult',
|
||||
1,
|
||||
20000,
|
||||
(params) => id !== '' && params.searchId == id,
|
||||
'NodeIKernelSearchListener/onSearchFileKeywordsResult',
|
||||
1,
|
||||
20000,
|
||||
(params) => id !== '' && params.searchId == id,
|
||||
);
|
||||
id = await Event!(keys, 12);
|
||||
const [ret] = (await Listener);
|
||||
@@ -317,7 +519,7 @@ export class NTQQFileApi {
|
||||
}
|
||||
} else if (fileMd5 || md5HexStr) {
|
||||
// 没有url,需要自己拼接
|
||||
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`;
|
||||
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr)!.toUpperCase()}/0`;
|
||||
}
|
||||
this.context.logger.logDebug('图片url获取失败', element);
|
||||
return '';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Friend, FriendV2, User } from '@/core/entities';
|
||||
import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
|
||||
import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelBuddyListener, NodeIKernelBuddyService, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
|
||||
import { LimitedHashTable } from '@/common/utils/MessageUnique';
|
||||
|
||||
export class NTQQFriendApi {
|
||||
@@ -21,15 +21,11 @@ export class NTQQFriendApi {
|
||||
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
|
||||
);
|
||||
return Array.from(data.values());
|
||||
}
|
||||
|
||||
async getBuddyIdMapCache(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||
return await this.getBuddyIdMap(refresh);
|
||||
}
|
||||
|
||||
async getBuddyIdMap(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||
const uids: string[] = [];
|
||||
const retMap: LimitedHashTable<string, string> = new LimitedHashTable<string, string>(5000);
|
||||
@@ -37,7 +33,7 @@ export class NTQQFriendApi {
|
||||
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
|
||||
);
|
||||
data.forEach((value, key) => {
|
||||
retMap.set(value.uin!, value.uid!);
|
||||
@@ -59,7 +55,7 @@ export class NTQQFriendApi {
|
||||
return item.buddyUids;
|
||||
}));
|
||||
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
|
||||
);
|
||||
return buddyListV2.map(category => ({
|
||||
categoryId: category.categoryId,
|
||||
@@ -74,29 +70,19 @@ export class NTQQFriendApi {
|
||||
async isBuddy(uid: string) {
|
||||
return this.context.session.getBuddyService().isBuddy(uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param forced
|
||||
* @returns
|
||||
*/
|
||||
async getFriends(forced = false): Promise<User[]> {
|
||||
const [_retData, _BuddyArg] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
|
||||
(
|
||||
'NodeIKernelBuddyService/getBuddyList',
|
||||
'NodeIKernelBuddyListener/onBuddyListChange',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
forced,
|
||||
);
|
||||
const friends: User[] = [];
|
||||
for (const categoryItem of _BuddyArg) {
|
||||
for (const friend of categoryItem.buddyList) {
|
||||
friends.push(friend);
|
||||
}
|
||||
}
|
||||
return friends;
|
||||
async clearBuddyReqUnreadCnt() {
|
||||
return this.context.session.getBuddyService().clearBuddyReqUnreadCnt();
|
||||
}
|
||||
async getBuddyReq() {
|
||||
const [, ret] = await this.core.eventWrapper.CallNormalEventV2<
|
||||
NodeIKernelBuddyService['getBuddyReq'],
|
||||
NodeIKernelBuddyListener['onBuddyReqChange']
|
||||
>(
|
||||
'NodeIKernelBuddyService/getBuddyReq',
|
||||
'NodeIKernelBuddyListener/onBuddyReqChange',
|
||||
1,
|
||||
5000);
|
||||
return ret;
|
||||
}
|
||||
|
||||
async handleFriendRequest(flag: string, accept: boolean) {
|
||||
|
@@ -7,12 +7,13 @@ import {
|
||||
GroupNotify,
|
||||
GroupRequestOperateTypes,
|
||||
InstanceContext,
|
||||
KickMemberV2Req,
|
||||
MemberExtSourceType,
|
||||
NapCatCore,
|
||||
NodeIKernelGroupListener,
|
||||
NodeIKernelGroupService,
|
||||
} from '@/core';
|
||||
import { isNumeric, runAllWithTimeout } from '@/common/utils/helper';
|
||||
import { isNumeric, runAllWithTimeout, sleep } from '@/common/utils/helper';
|
||||
|
||||
export class NTQQGroupApi {
|
||||
context: InstanceContext;
|
||||
@@ -23,7 +24,9 @@ export class NTQQGroupApi {
|
||||
constructor(context: InstanceContext, core: NapCatCore) {
|
||||
this.context = context;
|
||||
this.core = core;
|
||||
this.initCache().then().catch(context.logger.logError);
|
||||
sleep(1000).then(() => {
|
||||
this.initCache().then().catch(context.logger.logError);
|
||||
});
|
||||
}
|
||||
async initCache() {
|
||||
this.groups = await this.getGroups();
|
||||
@@ -114,7 +117,7 @@ export class NTQQGroupApi {
|
||||
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||
chatInfo: {
|
||||
peerUid: GroupCode,
|
||||
chatType: ChatType.group,
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
},
|
||||
filterMsgType: [],
|
||||
filterSendersUid: uids,
|
||||
@@ -175,7 +178,7 @@ export class NTQQGroupApi {
|
||||
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||
chatInfo: {
|
||||
peerUid: GroupCode,
|
||||
chatType: ChatType.group,
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
},
|
||||
filterMsgType: [],
|
||||
filterSendersUid: uids,
|
||||
@@ -219,7 +222,21 @@ export class NTQQGroupApi {
|
||||
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||
return this.context.session.getGroupService().addGroupEssence(param);
|
||||
}
|
||||
|
||||
async kickMemberV2Inner(param: KickMemberV2Req) {
|
||||
return this.context.session.getGroupService().kickMemberV2(param);
|
||||
}
|
||||
async deleteGroupBulletin(GroupCode: string, feedId: string) {
|
||||
const _Pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
||||
return this.context.session.getGroupService().deleteGroupBulletin(GroupCode, _Pskey, feedId);
|
||||
}
|
||||
async quitGroupV2(GroupCode: string, needDeleteLocalMsg: boolean) {
|
||||
const param = {
|
||||
groupCode: GroupCode,
|
||||
needDeleteLocalMsg: needDeleteLocalMsg
|
||||
};
|
||||
//应该是直接返回不需要Listener的 未经测试 需测试再发布
|
||||
return this.context.session.getGroupService().quitGroupV2(param);
|
||||
}
|
||||
async removeGroupEssence(GroupCode: string, msgId: string) {
|
||||
// 代码没测过
|
||||
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||
@@ -253,22 +270,28 @@ export class NTQQGroupApi {
|
||||
}
|
||||
|
||||
async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
|
||||
type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
|
||||
type EventType = NodeIKernelGroupService['getMemberInfo'];
|
||||
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
|
||||
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
|
||||
const [, , , _members] = await this.core.eventWrapper.CallNormalEvent<EventType, ListenerType>
|
||||
(
|
||||
'NodeIKernelGroupService/getMemberInfo',
|
||||
const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void>
|
||||
(
|
||||
'NodeIKernelGroupListener/onMemberInfoChange',
|
||||
1,
|
||||
5000,
|
||||
(groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
|
||||
return groupCode == GroupCode && members.has(uid);
|
||||
forced ? 5000 : 250,
|
||||
(params) => {
|
||||
return params === GroupCode;
|
||||
},
|
||||
GroupCode, [uid], forced,
|
||||
);
|
||||
return _members.get(uid);
|
||||
);
|
||||
const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo');
|
||||
const retData = await EventFunc!(GroupCode, [uid], forced);
|
||||
if (retData.result !== 0) {
|
||||
throw new Error(`${retData.errMsg}`);
|
||||
}
|
||||
const result = await Listener as unknown;
|
||||
let member: GroupMember | undefined;
|
||||
if (Array.isArray(result) && result?.[2] instanceof Map) {
|
||||
const members = result[2] as Map<string, GroupMember>;
|
||||
member = members.get(uid);
|
||||
}
|
||||
return member;
|
||||
}
|
||||
|
||||
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||
@@ -276,10 +299,10 @@ export class NTQQGroupApi {
|
||||
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
||||
const result = await groupService.getNextMemberList(sceneId!, undefined, num);
|
||||
if (result.errCode !== 0) {
|
||||
throw ('获取群成员列表出错,' + result.errMsg);
|
||||
throw new Error('获取群成员列表出错,' + result.errMsg);
|
||||
}
|
||||
|
||||
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values()));
|
||||
this.context.logger.logDebug(`获取群(${groupQQ})成员列表结果:`, `members: ${result.result.infos.size}`); //, Array.from(result.result.infos.values()));
|
||||
return result.result.infos;
|
||||
/*
|
||||
console.log(sceneId);
|
||||
@@ -308,7 +331,6 @@ export class NTQQGroupApi {
|
||||
arkJson: string
|
||||
}>>(
|
||||
'NodeIKernelGroupService/getGroupRecommendContactArkJson',
|
||||
5000,
|
||||
GroupCode,
|
||||
);
|
||||
return ret.arkJson;
|
||||
@@ -334,7 +356,7 @@ export class NTQQGroupApi {
|
||||
'seq': seq, // 通知序列号
|
||||
'type': type,
|
||||
'groupCode': groupCode,
|
||||
'postscript': reason || ' ', // 仅传空值可能导致处理失败,故默认给个空格
|
||||
'postscript': reason ?? ' ', // 仅传空值可能导致处理失败,故默认给个空格
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
||||
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement, SendStatusType } from '@/core/entities';
|
||||
import { InstanceContext, NapCatCore } from '@/core';
|
||||
import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
|
||||
import { GroupFileInfoUpdateParamType } from '@/core/listeners';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
|
||||
export class NTQQMsgApi {
|
||||
@@ -16,6 +16,9 @@ export class NTQQMsgApi {
|
||||
return this.context.session.getMsgService().fetchLongMsg(peer, msgId);
|
||||
}
|
||||
|
||||
async sendShowInputStatusReq(peer: Peer, eventType: number) {
|
||||
return this.context.session.getMsgService().sendShowInputStatusReq(peer.chatType, eventType, peer.peerUid);
|
||||
}
|
||||
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
|
||||
@@ -37,7 +40,6 @@ export class NTQQMsgApi {
|
||||
emojiId = emojiId.toString();
|
||||
return this.context.session.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set);
|
||||
}
|
||||
|
||||
async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
|
||||
msgList: RawMessage[]
|
||||
} | undefined> {
|
||||
@@ -94,7 +96,22 @@ export class NTQQMsgApi {
|
||||
async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
||||
return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
|
||||
}
|
||||
|
||||
async getMsgExBySeq(peer: Peer, msgSeq: string) {
|
||||
const DateNow = Math.floor(Date.now() / 1000);
|
||||
const filterMsgFromTime = (DateNow - 300).toString();
|
||||
const filterMsgToTime = DateNow.toString();
|
||||
const ret = await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
|
||||
filterMsgType: [],
|
||||
filterSendersUid: [],
|
||||
filterMsgToTime: filterMsgToTime,
|
||||
filterMsgFromTime: filterMsgFromTime,
|
||||
isReverseOrder: false,
|
||||
isIncludeCurrent: true,
|
||||
pageLimit: 100,
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
async setMsgRead(peer: Peer) {
|
||||
return this.context.session.getMsgService().setMsgRead(peer);
|
||||
}
|
||||
@@ -102,19 +119,19 @@ export class NTQQMsgApi {
|
||||
async getGroupFileList(GroupCode: string, params: GetFileListParam) {
|
||||
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
|
||||
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
|
||||
>(
|
||||
'NodeIKernelRichMediaService/getGroupFileList',
|
||||
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
|
||||
1,
|
||||
5000,
|
||||
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
||||
(groupFileListResult: GroupFileInfoUpdateParamType) => void
|
||||
>(
|
||||
'NodeIKernelRichMediaService/getGroupFileList',
|
||||
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
|
||||
1,
|
||||
5000,
|
||||
(groupFileListResult: GroupFileInfoUpdateParamType) => {
|
||||
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
|
||||
return true;
|
||||
},
|
||||
GroupCode,
|
||||
params,
|
||||
);
|
||||
return true;
|
||||
},
|
||||
GroupCode,
|
||||
params,
|
||||
);
|
||||
return data[1].item;
|
||||
}
|
||||
|
||||
@@ -129,64 +146,9 @@ export class NTQQMsgApi {
|
||||
peerUid: peer.peerUid,
|
||||
}, msgIds);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||
let msgId: string;
|
||||
try {
|
||||
msgId = await this.getMsgUnique(peer.chatType, await this.getServerTime());
|
||||
} catch (error) {
|
||||
//if (!napCatCore.session.getMsgService()['generateMsgUniqueId'])
|
||||
//兜底识别策略V2
|
||||
msgId = generateMsgId().toString();
|
||||
}
|
||||
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/sendMsg',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
timeout,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.msgId === msgId && msgRecord.sendStatus === 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
msgId,
|
||||
peer,
|
||||
msgElements,
|
||||
new Map(),
|
||||
);
|
||||
const retMsg = data[1].find(msgRecord => {
|
||||
if (msgRecord.msgId === msgId) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return retMsg;
|
||||
}
|
||||
|
||||
sendMsgEx(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||
//return NTQQMsgApi.sendMsgV1(peer, msgElements, waitComplete, timeout);
|
||||
}
|
||||
async PrepareTempChat(toUserUid: string, GroupCode: string, nickname: string) {
|
||||
//By Jadx/Ida Mlikiowa
|
||||
let TempGameSession = {
|
||||
const TempGameSession = {
|
||||
nickname: "",
|
||||
gameAppId: "",
|
||||
selfTinyId: "",
|
||||
@@ -194,7 +156,7 @@ export class NTQQMsgApi {
|
||||
peerOpenId: "",
|
||||
};
|
||||
return this.context.session.getMsgService().prepareTempChat({
|
||||
chatType: ChatType.temp,
|
||||
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
|
||||
peerUid: toUserUid,
|
||||
peerNickname: nickname,
|
||||
fromGroupCode: GroupCode,
|
||||
@@ -209,35 +171,35 @@ export class NTQQMsgApi {
|
||||
}
|
||||
async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||
//唉? !我有个想法
|
||||
if (peer.chatType === ChatType.temp && peer.guildId && peer.guildId !== '') {
|
||||
let member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid!);
|
||||
if (peer.chatType === ChatType.KCHATTYPETEMPC2CFROMGROUP && peer.guildId && peer.guildId !== '') {
|
||||
const member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid!);
|
||||
if (member) {
|
||||
await this.PrepareTempChat(peer.peerUid, peer.guildId, member.nick);
|
||||
}
|
||||
}
|
||||
const msgId = await this.getMsgUnique(peer.chatType, await this.getServerTime());
|
||||
const msgId = await this.generateMsgUniqueId(peer.chatType, await this.getServerTime());
|
||||
peer.guildId = msgId;
|
||||
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/sendMsg',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
timeout,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.guildId === msgId && msgRecord.sendStatus === 2) {
|
||||
return true;
|
||||
>(
|
||||
'NodeIKernelMsgService/sendMsg',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
timeout,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
'0',
|
||||
peer,
|
||||
msgElements,
|
||||
new Map(),
|
||||
);
|
||||
return false;
|
||||
},
|
||||
'0',
|
||||
peer,
|
||||
msgElements,
|
||||
new Map(),
|
||||
);
|
||||
const retMsg = data[1].find(msgRecord => {
|
||||
if (msgRecord.guildId === msgId) {
|
||||
return true;
|
||||
@@ -246,21 +208,14 @@ export class NTQQMsgApi {
|
||||
return retMsg;
|
||||
}
|
||||
|
||||
async getMsgUnique(chatType: number, time: string) {
|
||||
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||
return this.context.session.getMsgService().generateMsgUniqueId(chatType, time);
|
||||
}
|
||||
return this.context.session.getMsgService().getMsgUniqueId(time);
|
||||
async generateMsgUniqueId(chatType: number, time: string) {
|
||||
return this.context.session.getMsgService().generateMsgUniqueId(chatType, time);
|
||||
}
|
||||
|
||||
async getServerTime() {
|
||||
return this.context.session.getMSFService().getServerTime();
|
||||
}
|
||||
|
||||
async getServerTimeV2() {
|
||||
return this.core.eventWrapper.callNoListenerEvent<() => string>('NodeIKernelMsgService/getServerTime', 5000);
|
||||
}
|
||||
|
||||
async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||
return this.context.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
|
||||
}
|
||||
@@ -272,31 +227,31 @@ export class NTQQMsgApi {
|
||||
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
5000,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
|
||||
return true;
|
||||
>(
|
||||
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
5000,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (const msgRecord of msgRecords) {
|
||||
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
msgInfos,
|
||||
srcPeer,
|
||||
destPeer,
|
||||
[],
|
||||
new Map(),
|
||||
);
|
||||
return false;
|
||||
},
|
||||
msgInfos,
|
||||
srcPeer,
|
||||
destPeer,
|
||||
[],
|
||||
new Map(),
|
||||
);
|
||||
for (const msg of data[1]) {
|
||||
const arkElement = msg.elements.find(ele => ele.arkElement);
|
||||
if (!arkElement) {
|
||||
continue;
|
||||
}
|
||||
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
|
||||
const forwardData: any = JSON.parse(arkElement.arkElement?.bytesData ?? "");
|
||||
if (forwardData.app != 'com.tencent.multimsg') {
|
||||
continue;
|
||||
}
|
||||
|
@@ -2,77 +2,6 @@ import { RequestUtil } from '@/common/utils/request';
|
||||
import { MiniAppLuaJsonType } from '@/core';
|
||||
import { InstanceContext, NapCatCore } from '..';
|
||||
|
||||
// let t = await napCatCore.session.getGroupService().shareDigest({
|
||||
// appId: "100497308",
|
||||
// appType: 1,
|
||||
// msgStyle: 0,
|
||||
// recvUin: "726067488",
|
||||
// sendType: 1,
|
||||
// clientInfo: {
|
||||
// platform: 1
|
||||
// },
|
||||
// richMsg: {
|
||||
// usingArk: true,
|
||||
// title: "Bot测试title",
|
||||
// summary: "Bot测试summary",
|
||||
// url: "https://www.bilibili.com",
|
||||
// pictureUrl: "https://y.qq.com/music/photo_new/T002R300x300M0000035DC6W4ZpSqf_1.jpg?max_age=2592000",
|
||||
// brief: "Bot测试brief",
|
||||
// }
|
||||
// });
|
||||
// {
|
||||
// errCode: 0,
|
||||
// errMsg: '',
|
||||
// rsp: {
|
||||
// sendType: 1,
|
||||
// recvUin: '726067488',
|
||||
// recvOpenId: '',
|
||||
// errCode: 901501,
|
||||
// errMsg: 'imagent service_error:150_OIDB_NO_PRIV',
|
||||
// extInfo: {
|
||||
// wording: '消息下发失败(错误码:901501)',
|
||||
// jumpResult: 0,
|
||||
// jumpUrl: '',
|
||||
// level: 0,
|
||||
// subLevel: 0,
|
||||
// developMsg: 'imagent error'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// export class MusicSign {
|
||||
// private readonly url: string;
|
||||
|
||||
// constructor(url: string) {
|
||||
// this.url = url;
|
||||
// }
|
||||
|
||||
// sign(postData: CustomMusicSignPostData | IdMusicSignPostData): Promise<any> {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// fetch(this.url, {
|
||||
// method: 'POST', // 指定请求方法为 POST
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json' // 设置请求头,指明发送的数据类型为 JSON
|
||||
// },
|
||||
// body: JSON.stringify(postData) // 将 JavaScript 对象转换为 JSON 字符串作为请求体
|
||||
// })
|
||||
// .then(response => {
|
||||
// if (!response.ok) {
|
||||
// reject(response.statusText); // 请求失败,返回错误信息
|
||||
// }
|
||||
// return response.json(); // 解析 JSON 格式的响应体
|
||||
// })
|
||||
// .then(data => {
|
||||
// logDebug('音乐消息生成成功', data);
|
||||
// resolve(data);
|
||||
// })
|
||||
// .catch(error => {
|
||||
// reject(error);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
export class NTQQMusicSignApi {
|
||||
context: InstanceContext;
|
||||
core: NapCatCore;
|
||||
|
@@ -32,7 +32,6 @@ export class NTQQSystemApi {
|
||||
arkJson: string
|
||||
}>>(
|
||||
'NodeIKernelCollectionService/collectionArkShare',
|
||||
5000,
|
||||
'1717662698058',
|
||||
);
|
||||
return ret;
|
||||
|
@@ -3,6 +3,7 @@ import { NodeIKernelProfileListener } from '@/core/listeners';
|
||||
import { RequestUtil } from '@/common/utils/request';
|
||||
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
|
||||
import { InstanceContext, NapCatCore } from '..';
|
||||
import { solveAsyncProblem } from '@/common/utils/helper';
|
||||
|
||||
export class NTQQUserApi {
|
||||
context: InstanceContext;
|
||||
@@ -100,7 +101,7 @@ export class NTQQUserApi {
|
||||
return retData;
|
||||
}
|
||||
|
||||
async fetchUserDetailInfo(uid: string) {
|
||||
async fetchUserDetailInfo(uid: string, mode: UserDetailSource = UserDetailSource.KDB) {
|
||||
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||
const [_retData, profile] = await this.core.eventWrapper.CallNormalEvent<EventService, EventListener>(
|
||||
@@ -111,7 +112,7 @@ export class NTQQUserApi {
|
||||
(profile) => profile.uid === uid,
|
||||
'BuddyProfileStore',
|
||||
[uid],
|
||||
UserDetailSource.KSERVER,
|
||||
mode,
|
||||
[ProfileBizType.KALL],
|
||||
);
|
||||
const RetUser: User = {
|
||||
@@ -120,32 +121,20 @@ export class NTQQUserApi {
|
||||
...profile.simpleInfo.vasInfo,
|
||||
...profile.commonExt,
|
||||
...profile.simpleInfo.baseInfo,
|
||||
qqLevel: profile.commonExt.qqLevel,
|
||||
qqLevel: profile.commonExt?.qqLevel,
|
||||
age: profile.simpleInfo.baseInfo.age,
|
||||
pendantId: '',
|
||||
};
|
||||
return RetUser;
|
||||
}
|
||||
|
||||
async getUserDetailInfo(uid: string) {
|
||||
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||
return this.fetchUserDetailInfo(uid);
|
||||
async getUserDetailInfo(uid: string): Promise<User> {
|
||||
const retUser = await solveAsyncProblem(async (uid) => this.fetchUserDetailInfo(uid, UserDetailSource.KDB), uid);
|
||||
if (retUser && retUser.uin !== '0') {
|
||||
return retUser;
|
||||
}
|
||||
return this.getUserDetailInfoOld(uid);
|
||||
}
|
||||
|
||||
async getUserDetailInfoOld(uid: string) {
|
||||
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo'];
|
||||
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged'];
|
||||
const [_retData, profile] = await this.core.eventWrapper.CallNormalEvent<EventService, EventListener>(
|
||||
'NodeIKernelProfileService/getUserDetailInfoWithBizInfo',
|
||||
'NodeIKernelProfileListener/onProfileDetailInfoChanged',
|
||||
2,
|
||||
5000,
|
||||
(profile) => profile.uid === uid,
|
||||
uid,
|
||||
[0],
|
||||
);
|
||||
return profile;
|
||||
this.context.logger.logDebug('[NapCat] [Mark] getUserDetailInfo Mode1 Failed.');
|
||||
return this.fetchUserDetailInfo(uid, UserDetailSource.KSERVER);
|
||||
}
|
||||
|
||||
async modifySelfProfile(param: ModifyProfileParams) {
|
||||
@@ -155,16 +144,10 @@ export class NTQQUserApi {
|
||||
//需要异常处理
|
||||
async getCookies(domain: string) {
|
||||
const ClientKeyData = await this.forceFetchClientKey();
|
||||
const requestUrl = `https://ssl.ptlogin2.qq.com/jump?${
|
||||
new URLSearchParams({
|
||||
ptlang: '1033',
|
||||
clientuin: this.core.selfInfo.uin,
|
||||
clientkey: ClientKeyData.clientKey,
|
||||
u1: `https://user.qzone.qq.com/${this.core.selfInfo.uin}/infocenter`,
|
||||
keyindex: '19',
|
||||
})
|
||||
}`;
|
||||
return await RequestUtil.HttpsGetCookies(requestUrl);
|
||||
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin +
|
||||
'&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + this.core.selfInfo.uin + '%2Finfocenter&keyindex=19%27';
|
||||
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
|
||||
return cookies;
|
||||
}
|
||||
|
||||
async getPSkey(domainList: string[]) {
|
||||
@@ -209,39 +192,14 @@ export class NTQQUserApi {
|
||||
return skey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
async getUidByUin(Uin: string) {
|
||||
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||
return await this.getUidByUinV2(Uin);
|
||||
}
|
||||
return await this.getUidByUinV1(Uin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
async getUinByUid(Uid: string) {
|
||||
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||
return await this.getUinByUidV2(Uid);
|
||||
}
|
||||
return await this.getUinByUidV1(Uid);
|
||||
}
|
||||
|
||||
//后期改成流水线处理
|
||||
async getUidByUinV2(Uin: string) {
|
||||
let uid = (await this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
|
||||
let uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
|
||||
if (uid) return uid;
|
||||
uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
|
||||
uid = (await this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
|
||||
if (uid) return uid;
|
||||
uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||
if (uid) return uid;
|
||||
// console.log((await this.core.getApiContext().FriendApi.getBuddyIdMapCache(true)));
|
||||
uid = (await this.core.apis.FriendApi.getBuddyIdMapCache(true)).getValue(Uin);//从Buddy缓存获取Uid
|
||||
if (uid) return uid;
|
||||
uid = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getValue(Uin);
|
||||
if (uid) return uid;
|
||||
const unveifyUid = (await this.getUserDetailInfoByUinV2(Uin)).detail.uid;//从QQ Native 特殊转换
|
||||
if (unveifyUid.indexOf('*') == -1) uid = unveifyUid;
|
||||
//if (uid) return uid;
|
||||
@@ -250,50 +208,18 @@ export class NTQQUserApi {
|
||||
|
||||
//后期改成流水线处理
|
||||
async getUinByUidV2(Uid: string) {
|
||||
let uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
|
||||
let uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
|
||||
uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await this.core.apis.FriendApi.getBuddyIdMapCache(true)).getKey(Uid);//从Buddy缓存获取Uin
|
||||
if (uin) return uin;
|
||||
uin = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getKey(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await this.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||
return uin;
|
||||
}
|
||||
|
||||
async getUidByUinV1(Uin: string) {
|
||||
// 通用转换开始尝试
|
||||
let uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||
if (!uid) {
|
||||
const unveifyUid = (await this.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
|
||||
if (unveifyUid.indexOf('*') == -1) {
|
||||
uid = unveifyUid;
|
||||
}
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
|
||||
async getUinByUidV1(Uid: string) {
|
||||
const ret = await this.core.eventWrapper.callNoListenerEvent<(Uin: string[]) => Promise<{
|
||||
uinInfo: Map<string, string>
|
||||
}>>
|
||||
('NodeIKernelUixConvertService/getUin', 5000, [Uid]);
|
||||
let uin = ret.uinInfo.get(Uid);
|
||||
if (!uin) {
|
||||
uin = (await this.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;
|
||||
}
|
||||
|
||||
async getRecentContactListSnapShot(count: number) {
|
||||
return await this.context.session.getRecentContactService().getRecentContactListSnapShot(count);
|
||||
}
|
||||
@@ -312,12 +238,7 @@ export class NTQQUserApi {
|
||||
|
||||
async getUserDetailInfoByUinV2(Uin: string) {
|
||||
return await this.core.eventWrapper.callNoListenerEvent<(Uin: string) => Promise<UserDetailInfoByUinV2>>
|
||||
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
|
||||
}
|
||||
|
||||
async getUserDetailInfoByUin(Uin: string) {
|
||||
return this.core.eventWrapper.callNoListenerEvent<(Uin: string) => Promise<UserDetailInfoByUin>>
|
||||
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
|
||||
('NodeIKernelProfileService/getUserDetailInfoByUin', Uin);
|
||||
}
|
||||
|
||||
async forceFetchClientKey() {
|
||||
|
@@ -20,14 +20,13 @@ export class NTQQWebApi {
|
||||
|
||||
async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${
|
||||
new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
group_code: groupCode,
|
||||
msg_seq: msgSeq,
|
||||
msg_random: msgRandom,
|
||||
target_group_code: targetGroupCode,
|
||||
}).toString()
|
||||
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
group_code: groupCode,
|
||||
msg_seq: msgSeq,
|
||||
msg_random: msgRandom,
|
||||
target_group_code: targetGroupCode,
|
||||
}).toString()
|
||||
}`;
|
||||
try {
|
||||
return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
@@ -38,13 +37,12 @@ export class NTQQWebApi {
|
||||
|
||||
async getGroupEssenceMsg(GroupCode: string, page_start: string) {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${
|
||||
new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
group_code: GroupCode,
|
||||
page_start,
|
||||
page_limit: '20',
|
||||
}).toString()
|
||||
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
group_code: GroupCode,
|
||||
page_start,
|
||||
page_limit: '20',
|
||||
}).toString()
|
||||
}`;
|
||||
let ret;
|
||||
try {
|
||||
@@ -65,14 +63,13 @@ export class NTQQWebApi {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
const retList: Promise<WebApiGroupMemberRet>[] = [];
|
||||
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>
|
||||
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${
|
||||
new URLSearchParams({
|
||||
st: '0',
|
||||
end: '40',
|
||||
sort: '1',
|
||||
gc: GroupCode,
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
}).toString()
|
||||
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
|
||||
st: '0',
|
||||
end: '40',
|
||||
sort: '1',
|
||||
gc: GroupCode,
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
}).toString()
|
||||
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
|
||||
return [];
|
||||
@@ -86,14 +83,13 @@ export class NTQQWebApi {
|
||||
//遍历批量请求
|
||||
for (let i = 2; i <= PageNum; i++) {
|
||||
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>
|
||||
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${
|
||||
new URLSearchParams({
|
||||
st: ((i - 1) * 40).toString(),
|
||||
end: (i * 40).toString(),
|
||||
sort: '1',
|
||||
gc: GroupCode,
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
}).toString()
|
||||
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
|
||||
st: ((i - 1) * 40).toString(),
|
||||
end: (i * 40).toString(),
|
||||
sort: '1',
|
||||
gc: GroupCode,
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
}).toString()
|
||||
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
retList.push(ret);
|
||||
}
|
||||
@@ -127,15 +123,14 @@ export class NTQQWebApi {
|
||||
let ret: any = undefined;
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetJson<any>
|
||||
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${
|
||||
new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
qid: GroupCode,
|
||||
text: Content,
|
||||
pinned: '0',
|
||||
type: '1',
|
||||
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
|
||||
}).toString()
|
||||
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
qid: GroupCode,
|
||||
text: Content,
|
||||
pinned: '0',
|
||||
type: '1',
|
||||
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
|
||||
}).toString()
|
||||
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
return ret;
|
||||
} catch (e) {
|
||||
@@ -147,15 +142,10 @@ export class NTQQWebApi {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
let ret: WebApiGroupNoticeRet | undefined = undefined;
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(`https://web.qun.qq.com/cgi-bin/announce/get_t_list?${
|
||||
new URLSearchParams({
|
||||
bkn: this.getBknFromCookie(cookieObject),
|
||||
qid: GroupCode,
|
||||
type: '1',
|
||||
start: '0',
|
||||
num: '1',
|
||||
}).toString()
|
||||
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
const url = 'https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=' +
|
||||
this.getBknFromCookie(cookieObject) + '&qid=' + GroupCode + '&ft=23&ni=1&n=1&i=1&log_read=1&platform=1&s=-1&n=20';
|
||||
|
||||
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
if (ret?.ec !== 0) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -168,16 +158,15 @@ export class NTQQWebApi {
|
||||
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
|
||||
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||
const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => {
|
||||
const url = `https://qun.qq.com/interactive/honorlist?${
|
||||
new URLSearchParams({
|
||||
gc: Internal_groupCode,
|
||||
type: Internal_type.toString(),
|
||||
}).toString()
|
||||
const url = `https://qun.qq.com/interactive/honorlist?${new URLSearchParams({
|
||||
gc: Internal_groupCode,
|
||||
type: Internal_type.toString(),
|
||||
}).toString()
|
||||
}`;
|
||||
let resJson;
|
||||
try {
|
||||
const res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||
const match = res.match(/window\.__INITIAL_STATE__=(.*?);/);
|
||||
const match = /window\.__INITIAL_STATE__=(.*?);/.exec(res);
|
||||
if (match) {
|
||||
resJson = JSON.parse(match[1].trim());
|
||||
}
|
||||
|
@@ -3,8 +3,8 @@ import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import { InstanceContext } from './wrapper';
|
||||
import { proxiedListenerOf } from '@/common/utils/proxy-handler';
|
||||
import { GroupListener, MsgListener, ProfileListener } from './listeners';
|
||||
import { GroupMember, SelfInfo, SelfStatusInfo } from './entities';
|
||||
import { NodeIKernelMsgListener, NodeIKernelGroupListener, NodeIKernelProfileListener } from './listeners';
|
||||
import { DataSource, GroupMember, SelfInfo } from './entities';
|
||||
import { LegacyNTEventWrapper } from '@/common/framework/event-legacy';
|
||||
import { NTQQFileApi, NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQSystemApi, NTQQUserApi, NTQQWebApi } from './apis';
|
||||
import os from 'node:os';
|
||||
@@ -44,8 +44,8 @@ export class NapCatCore {
|
||||
constructor(context: InstanceContext, selfInfo: SelfInfo) {
|
||||
this.selfInfo = selfInfo;
|
||||
this.context = context;
|
||||
this.util = new this.context.wrapper.NodeQQNTWrapperUtil();
|
||||
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session);
|
||||
this.util = this.context.wrapper.NodeQQNTWrapperUtil;
|
||||
this.eventWrapper = new LegacyNTEventWrapper(context.session);
|
||||
this.apis = {
|
||||
FileApi: new NTQQFileApi(this.context, this),
|
||||
SystemApi: new NTQQSystemApi(this.context, this),
|
||||
@@ -79,7 +79,7 @@ export class NapCatCore {
|
||||
}
|
||||
|
||||
get dataPath(): string {
|
||||
let result = this.util.getNTUserDataInfoConfig();
|
||||
let result = this.context.wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
|
||||
if (!result) {
|
||||
result = path.resolve(os.homedir(), './.config/QQ');
|
||||
fs.mkdirSync(result, { recursive: true });
|
||||
@@ -89,32 +89,35 @@ export class NapCatCore {
|
||||
|
||||
// Renamed from 'InitDataListener'
|
||||
async initNapCatCoreListeners() {
|
||||
const msgListener = new MsgListener();
|
||||
msgListener.onRecvMsg = (msg) => {
|
||||
//console.log('RecvMsg', msg);
|
||||
const msgListener = new NodeIKernelMsgListener();
|
||||
msgListener.onRecvMsg = (msgs) => {
|
||||
msgs.forEach(msg => this.context.logger.logMessage(msg, this.selfInfo));
|
||||
};
|
||||
msgListener.onAddSendMsg = (msg) => {
|
||||
this.context.logger.logMessage(msg, this.selfInfo);
|
||||
};
|
||||
//await sleep(2500);
|
||||
this.context.session.getMsgService().addKernelMsgListener(
|
||||
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)),
|
||||
proxiedListenerOf(msgListener, this.context.logger) as any
|
||||
);
|
||||
|
||||
const profileListener = new ProfileListener();
|
||||
const profileListener = new NodeIKernelProfileListener();
|
||||
profileListener.onProfileDetailInfoChanged = (profile) => {
|
||||
if (profile.uid === this.selfInfo.uid) {
|
||||
Object.assign(this.selfInfo, profile);
|
||||
}
|
||||
};
|
||||
profileListener.onSelfStatusChanged = (Info: SelfStatusInfo) => {
|
||||
profileListener.onSelfStatusChanged = (/* Info: SelfStatusInfo */) => {
|
||||
// if (Info.status == 20) {
|
||||
// log("账号状态变更为离线")
|
||||
// }
|
||||
};
|
||||
this.context.session.getProfileService().addKernelProfileListener(
|
||||
new this.context.wrapper.NodeIKernelProfileListener(proxiedListenerOf(profileListener, this.context.logger)),
|
||||
proxiedListenerOf(profileListener, this.context.logger),
|
||||
);
|
||||
|
||||
// 群相关
|
||||
const groupListener = new GroupListener();
|
||||
const groupListener = new NodeIKernelGroupListener();
|
||||
groupListener.onGroupListUpdate = (updateType, groupList) => {
|
||||
// console.log("onGroupListUpdate", updateType, groupList)
|
||||
groupList.map(g => {
|
||||
@@ -128,12 +131,12 @@ export class NapCatCore {
|
||||
// 获取群成员
|
||||
}
|
||||
const sceneId = this.context.session.getGroupService().createMemberListScene(g.groupCode, 'groupMemberList_MainWindow');
|
||||
this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, 3000).then(r => {
|
||||
this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, 3000).then( /* r => {
|
||||
// console.log(`get group ${g.groupCode} members`, r);
|
||||
// r.result.infos.forEach(member => {
|
||||
// });
|
||||
// groupMembers.set(g.groupCode, r.result.infos);
|
||||
});
|
||||
} */);
|
||||
});
|
||||
};
|
||||
groupListener.onMemberListChange = (arg) => {
|
||||
@@ -161,9 +164,9 @@ export class NapCatCore {
|
||||
}
|
||||
// console.log('onMemberListChange', groupCode, arg);
|
||||
};
|
||||
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
|
||||
groupListener.onMemberInfoChange = (groupCode, dataSource, members) => {
|
||||
//console.log('onMemberInfoChange', groupCode, changeType, members);
|
||||
if (changeType === 0 && members.get(this.selfInfo.uid)?.isDelete) {
|
||||
if (dataSource === DataSource.LOCAL && members.get(this.selfInfo.uid)?.isDelete) {
|
||||
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
|
||||
setTimeout(() => {
|
||||
this.apis.GroupApi.groupCache.delete(groupCode);
|
||||
@@ -193,10 +196,13 @@ export class NapCatCore {
|
||||
this.apis.GroupApi.groupMemberCache.set(groupCode, members);
|
||||
}
|
||||
};
|
||||
this.context.session.getGroupService().addKernelGroupListener(
|
||||
proxiedListenerOf(groupListener, this.context.logger) as any
|
||||
);
|
||||
}
|
||||
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
|
||||
if (memberNew.role !== memberOld?.role) {
|
||||
this.context.logger.log(`群 ${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
|
||||
this.context.logger.logDebug(`群 ${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@@ -1,5 +1,21 @@
|
||||
import { QQLevel, Sex } from './user';
|
||||
|
||||
import { QQLevel, Sex, User } from './user';
|
||||
export interface KickMemberInfo {
|
||||
optFlag: number,
|
||||
optOperate: number,
|
||||
optMemberUid: string,
|
||||
optBytesMsg: string,
|
||||
}
|
||||
export interface KickMemberV2Req{
|
||||
groupCode: string,
|
||||
kickFlag: number,
|
||||
kickList: Array<KickMemberInfo>,
|
||||
kickListUids: Array<string>,
|
||||
kickMsg: string
|
||||
}
|
||||
export enum DataSource {
|
||||
LOCAL,
|
||||
REMOTE
|
||||
}
|
||||
export enum GroupListUpdateType {
|
||||
REFRESHALL,
|
||||
GETALL,
|
||||
@@ -65,6 +81,7 @@ export interface GroupMember {
|
||||
uin: string; // QQ号
|
||||
isRobot: boolean;
|
||||
sex?: Sex;
|
||||
age?: number;
|
||||
qqLevel?: QQLevel;
|
||||
isChangeRole: boolean;
|
||||
joinTime: string;
|
||||
|
@@ -26,36 +26,70 @@ export interface GetFileListParam {
|
||||
|
||||
export enum ElementType {
|
||||
UNKNOWN = 0,
|
||||
|
||||
TEXT = 1,
|
||||
|
||||
PIC = 2,
|
||||
|
||||
FILE = 3,
|
||||
|
||||
PTT = 4,
|
||||
|
||||
VIDEO = 5,
|
||||
|
||||
FACE = 6,
|
||||
|
||||
REPLY = 7,
|
||||
|
||||
WALLET = 9,
|
||||
GreyTip = 8,//Poke别叫戳一搓了 官方名字拍一拍 戳一戳是另一个名字
|
||||
|
||||
/**
|
||||
* “小灰条”,包括拍一拍 (Poke)、撤回提示等
|
||||
*/
|
||||
GreyTip = 8,
|
||||
|
||||
ARK = 10,
|
||||
|
||||
MFACE = 11,
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -328,6 +362,7 @@ export interface SendPicElement {
|
||||
}
|
||||
|
||||
export interface ReplyElement {
|
||||
sourceMsgIdInRecords?: string;
|
||||
replayMsgSeq: string;
|
||||
replayMsgId: string;
|
||||
senderUin: string;
|
||||
@@ -376,7 +411,7 @@ export interface ShareLocationElement {
|
||||
ext: string;
|
||||
}
|
||||
|
||||
export interface sendShareLocationElement {
|
||||
export interface SendShareLocationElement {
|
||||
elementType: ElementType.SHARELOCATION;
|
||||
elementId: string;
|
||||
shareLocationElement?: ShareLocationElement;
|
||||
@@ -426,7 +461,7 @@ export interface SendMarkdownElement {
|
||||
|
||||
export type SendMessageElement = SendTextElement | SendPttElement |
|
||||
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement |
|
||||
SendVideoElement | SendArkElement | SendMarkdownElement | sendShareLocationElement;
|
||||
SendVideoElement | SendArkElement | SendMarkdownElement | SendShareLocationElement;
|
||||
|
||||
export interface TextElement {
|
||||
content: string;
|
||||
@@ -442,7 +477,7 @@ export interface MessageElement {
|
||||
extBufForUI: string,//"0x",
|
||||
textElement?: TextElement;
|
||||
faceElement?: FaceElement,
|
||||
marketFaceElement?: MarkdownElement,
|
||||
marketFaceElement?: MarketFaceElement,
|
||||
replyElement?: ReplyElement,
|
||||
picElement?: PicElement,
|
||||
pttElement?: PttElement,
|
||||
@@ -476,17 +511,8 @@ export enum AtType {
|
||||
atAll = 1,
|
||||
atUser = 2
|
||||
}
|
||||
|
||||
export enum ChatType {
|
||||
friend = 1,
|
||||
group = 2,
|
||||
chatDevice = 8, //移动设备?
|
||||
temp = 100
|
||||
|
||||
}
|
||||
|
||||
// 来自Android分析
|
||||
export enum ChatType2 {
|
||||
export enum ChatType {
|
||||
KCHATTYPEADELIE = 42,
|
||||
KCHATTYPEBUDDYNOTIFY = 5,
|
||||
KCHATTYPEC2C = 1,
|
||||
@@ -580,13 +606,28 @@ export interface PicElement {
|
||||
originImageUrl?: string; // http url, 没有host,host是https://gchat.qpic.cn/, 带download参数的是https://multimedia.nt.qq.com.cn
|
||||
}
|
||||
|
||||
export enum GrayTipElementSubType {
|
||||
INVITE_NEW_MEMBER = 12,
|
||||
MEMBER_NEW_TITLE = 17
|
||||
export enum NTGrayTipElementSubTypeV2 {
|
||||
GRAYTIP_ELEMENT_SUBTYPE_AIOOP = 15,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_BLOCK = 14,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_BUDDY = 5,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_BUDDYNOTIFY = 9,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_EMOJIREPLY = 3,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_ESSENCE = 7,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_FEED = 6,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_FEEDCHANNELMSG = 11,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_FILE = 10,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_GROUP = 4,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_GROUPNOTIFY = 8,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_JSON = 17,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_LOCALMSG = 13,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_PROCLAMATION = 2,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_REVOKE = 1,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_UNKNOWN = 0,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_WALLET = 16,
|
||||
GRAYTIP_ELEMENT_SUBTYPE_XMLMSG = 12,
|
||||
}
|
||||
|
||||
export interface GrayTipElement {
|
||||
subElementType: GrayTipElementSubType;
|
||||
subElementType: NTGrayTipElementSubTypeV2;
|
||||
revokeElement: {
|
||||
operatorRole: string;
|
||||
operatorUid: string;
|
||||
@@ -807,93 +848,79 @@ export interface MultiForwardMsgElement {
|
||||
resId: string;
|
||||
fileName: string;
|
||||
}
|
||||
export enum NTSubMsgType {
|
||||
KMSGSUBTYPEARKGROUPANNOUNCE = 3,
|
||||
KMSGSUBTYPEARKGROUPANNOUNCECONFIRMREQUIRED = 4,
|
||||
KMSGSUBTYPEARKGROUPGIFTATME = 5,
|
||||
KMSGSUBTYPEARKGROUPTASKATALL = 6,
|
||||
KMSGSUBTYPEARKMULTIMSG = 7,
|
||||
KMSGSUBTYPEARKNORMAL = 0,
|
||||
KMSGSUBTYPEARKTENCENTDOCFROMMINIAPP = 1,
|
||||
KMSGSUBTYPEARKTENCENTDOCFROMPLUSPANEL = 2,
|
||||
KMSGSUBTYPEEMOTICON = 15,
|
||||
KMSGSUBTYPEFILEAPP = 11,
|
||||
KMSGSUBTYPEFILEAUDIO = 3,
|
||||
KMSGSUBTYPEFILEDOC = 4,
|
||||
KMSGSUBTYPEFILEEXCEL = 6,
|
||||
KMSGSUBTYPEFILEFOLDER = 13,
|
||||
KMSGSUBTYPEFILEHTML = 10,
|
||||
KMSGSUBTYPEFILEIPA = 14,
|
||||
KMSGSUBTYPEFILENORMAL = 0,
|
||||
KMSGSUBTYPEFILEPDF = 7,
|
||||
KMSGSUBTYPEFILEPIC = 1,
|
||||
KMSGSUBTYPEFILEPPT = 5,
|
||||
KMSGSUBTYPEFILEPSD = 12,
|
||||
KMSGSUBTYPEFILETXT = 8,
|
||||
KMSGSUBTYPEFILEVIDEO = 2,
|
||||
KMSGSUBTYPEFILEZIP = 9,
|
||||
KMSGSUBTYPELINK = 5,
|
||||
KMSGSUBTYPEMARKETFACE = 1,
|
||||
KMSGSUBTYPEMIXEMOTICON = 7,
|
||||
KMSGSUBTYPEMIXFACE = 3,
|
||||
KMSGSUBTYPEMIXMARKETFACE = 2,
|
||||
KMSGSUBTYPEMIXPIC = 1,
|
||||
KMSGSUBTYPEMIXREPLY = 4,
|
||||
KMSGSUBTYPEMIXTEXT = 0,
|
||||
KMSGSUBTYPETENCENTDOC = 6
|
||||
export enum SendStatusType {
|
||||
KSEND_STATUS_FAILED = 0,
|
||||
KSEND_STATUS_SENDING = 1,
|
||||
KSEND_STATUS_SUCCESS = 2,
|
||||
KSEND_STATUS_SUCCESS_NOSEQ = 3
|
||||
}
|
||||
export interface RawMessage {
|
||||
parentMsgPeer: Peer;
|
||||
parentMsgIdList: string[];
|
||||
id?: number;//扩展字段 用于处理OB11 ID
|
||||
guildId: string;
|
||||
msgRandom: string;
|
||||
|
||||
parentMsgIdList: string[];
|
||||
|
||||
/**
|
||||
* 扩展字段,与 Ob11 msg ID 有关
|
||||
*/
|
||||
id?: number;
|
||||
|
||||
guildId: string;
|
||||
|
||||
msgRandom: string;
|
||||
|
||||
msgId: string;
|
||||
|
||||
// 时间戳,秒
|
||||
/**
|
||||
* 消息时间戳(秒)
|
||||
*/
|
||||
msgTime: string;
|
||||
|
||||
msgSeq: string;
|
||||
|
||||
msgType: NTMsgType;
|
||||
subMsgType: NTSubMsgType;
|
||||
|
||||
subMsgType: number;
|
||||
|
||||
senderUid: string;
|
||||
senderUin: string; // 发送者QQ号
|
||||
peerUid: string; // 群号 或者 QQ uid
|
||||
peerUin: string; // 群号 或者 发送者QQ号
|
||||
|
||||
/**
|
||||
* 发送者 QQ 号
|
||||
*/
|
||||
senderUin: string;
|
||||
|
||||
/**
|
||||
* 群号 / 用户 UID
|
||||
*/
|
||||
peerUid: string;
|
||||
|
||||
/**
|
||||
* 群号 / 用户 QQ 号
|
||||
*/
|
||||
peerUin: string;
|
||||
|
||||
/**
|
||||
* 发送者昵称(如果是好友消息)
|
||||
*/
|
||||
sendNickName: string;
|
||||
sendMemberName?: string; // 发送者群名片
|
||||
|
||||
/**
|
||||
* 发送者群名片(如果是群消息)
|
||||
*/
|
||||
sendMemberName?: string;
|
||||
|
||||
chatType: ChatType;
|
||||
sendStatus?: number; // 消息状态,别人发的2是已撤回,自己发的2是已发送
|
||||
recallTime: string; // 撤回时间, "0"是没有撤回
|
||||
|
||||
/**
|
||||
* 消息状态,别人发的 2 是已撤回,自己发的 2 是已发送
|
||||
*/
|
||||
sendStatus?: SendStatusType;
|
||||
|
||||
/**
|
||||
* 撤回时间,"0" 是没有撤回
|
||||
*/
|
||||
recallTime: string;
|
||||
|
||||
records: RawMessage[];
|
||||
elements: {
|
||||
elementId: string;
|
||||
elementType: ElementType;
|
||||
replyElement: {
|
||||
sourceMsgIdInRecords: string;
|
||||
senderUid: string; // 原消息发送者QQ号
|
||||
sourceMsgIsIncPic: boolean; // 原消息是否有图片
|
||||
sourceMsgText: string;
|
||||
replayMsgSeq: string; // 源消息的msgSeq,可以通过这个找到源消息的msgId
|
||||
};
|
||||
textElement: {
|
||||
atType: AtType;
|
||||
atUid: string; // QQ号
|
||||
content: string;
|
||||
atNtUid: string; // uid号
|
||||
};
|
||||
picElement: PicElement;
|
||||
pttElement: PttElement;
|
||||
arkElement: ArkElement;
|
||||
grayTipElement: GrayTipElement;
|
||||
faceElement: FaceElement;
|
||||
videoElement: VideoElement;
|
||||
fileElement: FileElement;
|
||||
marketFaceElement: MarketFaceElement;
|
||||
inlineKeyboardElement: InlineKeyboardElement;
|
||||
markdownElement: MarkdownElement;
|
||||
multiForwardMsgElement: MultiForwardMsgElement;
|
||||
}[];
|
||||
}
|
||||
|
||||
elements: MessageElement[];
|
||||
}
|
@@ -1,40 +1,58 @@
|
||||
export enum GroupNotifyTypes {
|
||||
INVITE_ME = 1,
|
||||
INVITED_JOIN = 4, // 有人接受了邀请入群
|
||||
JOIN_REQUEST = 7,
|
||||
ADMIN_SET = 8,
|
||||
KICK_MEMBER = 9,
|
||||
MEMBER_EXIT = 11, // 主动退出
|
||||
ADMIN_UNSET = 12,
|
||||
ADMIN_UNSET_OTHER = 13, // 其他人取消管理员
|
||||
export enum GroupNotifyMsgType {
|
||||
UN_SPECIFIED,
|
||||
INVITED_BY_MEMBER,
|
||||
REFUSE_INVITED,
|
||||
REFUSED_BY_ADMINI_STRATOR,
|
||||
AGREED_TOJOIN_DIRECT,// 有人接受了邀请入群
|
||||
INVITED_NEED_ADMINI_STRATOR_PASS,
|
||||
AGREED_TO_JOIN_BY_ADMINI_STRATOR,
|
||||
REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS,
|
||||
SET_ADMIN,
|
||||
KICK_MEMBER_NOTIFY_ADMIN,
|
||||
KICK_MEMBER_NOTIFY_KICKED,
|
||||
MEMBER_LEAVE_NOTIFY_ADMIN,// 主动退出
|
||||
CANCEL_ADMIN_NOTIFY_CANCELED,
|
||||
CANCEL_ADMIN_NOTIFY_ADMIN,// 其他人取消管理员
|
||||
TRANSFER_GROUP_NOTIFY_OLDOWNER,
|
||||
TRANSFER_GROUP_NOTIFY_ADMIN
|
||||
}
|
||||
|
||||
export interface GroupNotifies {
|
||||
doubt: boolean;
|
||||
nextStartSeq: string;
|
||||
notifies: GroupNotify[];
|
||||
}
|
||||
|
||||
export enum GroupNotifyStatus {
|
||||
IGNORE = 0,
|
||||
WAIT_HANDLE = 1,
|
||||
APPROVE = 2,
|
||||
REJECT = 3
|
||||
export enum GroupNotifyMsgStatus {
|
||||
KINIT,//初始化
|
||||
KUNHANDLE,//未处理
|
||||
KAGREED,//同意
|
||||
KREFUSED,//拒绝
|
||||
KIGNORED//忽略
|
||||
}
|
||||
export enum GroupInviteStatus {
|
||||
INIT,
|
||||
WAIT_TO_APPROVE,
|
||||
JOINED,
|
||||
REFUSED_BY_ADMINI_STRATOR
|
||||
}
|
||||
export enum GroupInviteType {
|
||||
BYBUDDY,
|
||||
BYGROUPMEMBER,
|
||||
BYDISCUSSMEMBER
|
||||
}
|
||||
|
||||
export interface GroupNotify {
|
||||
time: number; // 自己添加的字段,时间戳,毫秒, 用于判断收到短时间内收到重复的notify
|
||||
seq: string; // 唯一标识符,转成数字再除以1000应该就是时间戳?
|
||||
type: GroupNotifyTypes;
|
||||
status: GroupNotifyStatus; // 0是已忽略?,1是未处理,2是已同意
|
||||
seq: string; // 通知序列号
|
||||
type: GroupNotifyMsgType;
|
||||
status: GroupNotifyMsgStatus;
|
||||
group: { groupCode: string; groupName: string };
|
||||
user1: { uid: string; nickName: string }; // 被设置管理员的人
|
||||
user2: { uid: string; nickName: string }; // 操作者
|
||||
actionUser: { uid: string; nickName: string }; //未知
|
||||
actionTime: string;
|
||||
invitationExt: {
|
||||
srcType: number; // 0?未知
|
||||
groupCode: string; waitStatus: number
|
||||
srcType: GroupInviteType; // 邀请来源
|
||||
groupCode: string;
|
||||
waitStatus: GroupInviteStatus
|
||||
};
|
||||
postscript: string; // 加群用户填写的验证信息
|
||||
repeatSeqs: [];
|
||||
@@ -64,6 +82,7 @@ export enum BuddyReqType {
|
||||
}
|
||||
|
||||
export interface FriendRequest {
|
||||
isBuddy?: boolean;
|
||||
isInitiator?: boolean;
|
||||
isDecide: boolean;
|
||||
friendUid: string;
|
||||
|
@@ -231,6 +231,7 @@ export interface User {
|
||||
longNick?: string; // 签名
|
||||
remark?: string;
|
||||
sex?: Sex;
|
||||
age?: number;
|
||||
qqLevel?: QQLevel;
|
||||
qid?: string;
|
||||
birthday_year?: number;
|
||||
|
70
src/core/external/appid.json
vendored
70
src/core/external/appid.json
vendored
@@ -1,58 +1,30 @@
|
||||
{
|
||||
"3.1.2-13107": {
|
||||
"appid": 537146866,
|
||||
"qua": "V1_LNX_NQ_3.1.2-13107_RDM_B"
|
||||
"3.2.12-27187": {
|
||||
"appid": 537240645,
|
||||
"qua": "V1_LNX_NQ_3.2.12_27187_GW_B"
|
||||
},
|
||||
"3.2.10-25765": {
|
||||
"appid": 537234773,
|
||||
"qua": "V1_LNX_NQ_3.2.10_25765_GW_B"
|
||||
"3.2.12-27206": {
|
||||
"appid": 537240645,
|
||||
"qua": "V1_LNX_NQ_3.2.12_27206_GW_B"
|
||||
},
|
||||
"3.2.12-26702": {
|
||||
"appid": 537237950,
|
||||
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
|
||||
"3.2.12-27254":{
|
||||
"appid": 537240795,
|
||||
"qua": "V1_LNX_NQ_3.2.12_27254_GW_B"
|
||||
},
|
||||
"3.2.12-26740": {
|
||||
"appid": 537237950,
|
||||
"qua": "V1_WIN_NQ_9.9.15_26740_GW_B"
|
||||
"9.9.15-27187":{
|
||||
"appid": 537240610,
|
||||
"qua": "V1_WIN_NQ_9.9.15_27187_GW_B"
|
||||
},
|
||||
"3.2.12-26909": {
|
||||
"appid": 537237923,
|
||||
"qua": "V1_LNX_NQ_3.2.12_26909_GW_B"
|
||||
"9.9.15-27206":{
|
||||
"appid": 537240610,
|
||||
"qua": "V1_WIN_NQ_9.9.15_27206_GW_B"
|
||||
},
|
||||
"9.9.11-24815": {
|
||||
"appid": 537226656,
|
||||
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
|
||||
"9.9.15-27254":{
|
||||
"appid": 537240709,
|
||||
"qua": "V1_WIN_NQ_9.9.15_27254_GW_B"
|
||||
},
|
||||
"9.9.12-25493": {
|
||||
"appid": 537231759,
|
||||
"qua": "V1_WIN_NQ_9.9.12_25493_GW_B"
|
||||
},
|
||||
"9.9.12-25765": {
|
||||
"appid": 537234702,
|
||||
"qua": "V1_WIN_NQ_9.9.12_25765_GW_B"
|
||||
},
|
||||
"9.9.12-26299": {
|
||||
"appid": 537234826,
|
||||
"qua": "V1_WIN_NQ_9.9.12_26299_GW_B"
|
||||
},
|
||||
"9.9.12-26339": {
|
||||
"appid": 537234826,
|
||||
"qua": "V1_WIN_NQ_9.9.12_26339_GW_B"
|
||||
},
|
||||
"9.9.12-26466": {
|
||||
"appid": 537234826,
|
||||
"qua": "V1_WIN_NQ_9.9.12_26466_GW_B"
|
||||
},
|
||||
"9.9.15-26702": {
|
||||
"appid": 537237765,
|
||||
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
||||
},
|
||||
"9.9.15-26740": {
|
||||
"appid": 537237765,
|
||||
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
||||
},
|
||||
"9.9.15-26909": {
|
||||
"appid": 537237802,
|
||||
"qua": "V1_WIN_NQ_9.9.15_26909_GW_B"
|
||||
"9.9.15-27333": {
|
||||
"appid": 537240709,
|
||||
"qua": "V1_WIN_NQ_9.9.15_27333_GW_B"
|
||||
}
|
||||
}
|
@@ -1,53 +1,9 @@
|
||||
import { BuddyCategoryType, FriendRequestNotify } from '@/core/entities';
|
||||
|
||||
export type OnBuddyChangeParams = BuddyCategoryType[]
|
||||
export type OnBuddyChangeParams = BuddyCategoryType[];
|
||||
|
||||
interface IBuddyListener {
|
||||
onBuddyListChangedV2(arg: unknown): void,//V2版本 还没兼容
|
||||
|
||||
onBuddyListChange(arg: OnBuddyChangeParams): void,
|
||||
|
||||
onBuddyInfoChange(arg: unknown): void,
|
||||
|
||||
onBuddyDetailInfoChange(arg: unknown): void,
|
||||
|
||||
onNickUpdated(arg: unknown): void,
|
||||
|
||||
onBuddyRemarkUpdated(arg: unknown): void,
|
||||
|
||||
onAvatarUrlUpdated(arg: unknown): void,
|
||||
|
||||
onBuddyReqChange(arg: FriendRequestNotify): void,
|
||||
|
||||
onBuddyReqUnreadCntChange(arg: unknown): void,
|
||||
|
||||
onCheckBuddySettingResult(arg: unknown): void,
|
||||
|
||||
onAddBuddyNeedVerify(arg: unknown): void,
|
||||
|
||||
onSmartInfos(arg: unknown): void,
|
||||
|
||||
onSpacePermissionInfos(arg: unknown): void,
|
||||
|
||||
onDoubtBuddyReqChange(arg: unknown): void,
|
||||
|
||||
onDoubtBuddyReqUnreadNumChange(arg: unknown): void,
|
||||
|
||||
onBlockChanged(arg: unknown): void,
|
||||
|
||||
onAddMeSettingChanged(arg: unknown): void,
|
||||
|
||||
onDelBatchBuddyInfos(arg: unknown): void
|
||||
}
|
||||
|
||||
export interface NodeIKernelBuddyListener extends IBuddyListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(listener: IBuddyListener): NodeIKernelBuddyListener;
|
||||
}
|
||||
|
||||
export class BuddyListener implements IBuddyListener {
|
||||
export class NodeIKernelBuddyListener {
|
||||
onBuddyListChangedV2(arg: unknown): void {
|
||||
//throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
onAddBuddyNeedVerify(arg: unknown) {
|
||||
|
@@ -1,21 +1,5 @@
|
||||
export interface IKernelFileAssistantListener {
|
||||
onFileStatusChanged(...args: unknown[]): unknown;
|
||||
|
||||
onSessionListChanged(...args: unknown[]): unknown;
|
||||
|
||||
onSessionChanged(...args: unknown[]): unknown;
|
||||
|
||||
onFileListChanged(...args: unknown[]): unknown;
|
||||
|
||||
onFileSearch(...args: unknown[]): unknown;
|
||||
}
|
||||
|
||||
export interface NodeIKernelFileAssistantListener extends IKernelFileAssistantListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(adapter: IKernelFileAssistantListener): NodeIKernelFileAssistantListener;
|
||||
}
|
||||
|
||||
export class KernelFileAssistantListener implements IKernelFileAssistantListener {
|
||||
export class NodeIKernelFileAssistantListener {
|
||||
onFileStatusChanged(...args: unknown[]) {
|
||||
}
|
||||
|
||||
|
@@ -1,68 +1,6 @@
|
||||
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
|
||||
import { DataSource, Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
|
||||
|
||||
interface IGroupListener {
|
||||
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
|
||||
|
||||
onGroupExtListUpdate(...args: unknown[]): void;
|
||||
|
||||
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]): void;
|
||||
|
||||
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]): void;
|
||||
|
||||
onGroupNotifiesUnreadCountUpdated(...args: unknown[]): void;
|
||||
|
||||
onGroupDetailInfoChange(...args: unknown[]): void;
|
||||
|
||||
onGroupAllInfoChange(...args: unknown[]): void;
|
||||
|
||||
onGroupsMsgMaskResult(...args: unknown[]): void;
|
||||
|
||||
onGroupConfMemberChange(...args: unknown[]): void;
|
||||
|
||||
onGroupBulletinChange(...args: unknown[]): void;
|
||||
|
||||
onGetGroupBulletinListResult(...args: unknown[]): void;
|
||||
|
||||
onMemberListChange(arg: {
|
||||
sceneId: string,
|
||||
ids: string[],
|
||||
infos: Map<string, GroupMember>,
|
||||
finish: boolean,
|
||||
hasRobot: boolean
|
||||
}): void;
|
||||
|
||||
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>): void;
|
||||
|
||||
onSearchMemberChange(...args: unknown[]): void;
|
||||
|
||||
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]): void;
|
||||
|
||||
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]): void;
|
||||
|
||||
onGroupStatisticInfoChange(...args: unknown[]): void;
|
||||
|
||||
onJoinGroupNotify(...args: unknown[]): void;
|
||||
|
||||
onShutUpMemberListChanged(...args: unknown[]): void;
|
||||
|
||||
onGroupBulletinRemindNotify(...args: unknown[]): void;
|
||||
|
||||
onGroupFirstBulletinNotify(...args: unknown[]): void;
|
||||
|
||||
onJoinGroupNoVerifyFlag(...args: unknown[]): void;
|
||||
|
||||
onGroupArkInviteStateResult(...args: unknown[]): void;
|
||||
|
||||
// 发现于Win 9.9.9 23159
|
||||
onGroupMemberLevelInfoChange(...args: unknown[]): void;
|
||||
}
|
||||
|
||||
export interface NodeIKernelGroupListener extends IGroupListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(listener: IGroupListener): NodeIKernelGroupListener;
|
||||
}
|
||||
|
||||
export class GroupListener implements IGroupListener {
|
||||
export class NodeIKernelGroupListener {
|
||||
// 发现于Win 9.9.9 23159
|
||||
onGroupMemberLevelInfoChange(...args: unknown[]): void {
|
||||
|
||||
@@ -125,7 +63,7 @@ export class GroupListener implements IGroupListener {
|
||||
onJoinGroupNoVerifyFlag(...args: unknown[]) {
|
||||
}
|
||||
|
||||
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>) {
|
||||
onMemberInfoChange(groupCode: string, dateSource: DataSource, members: Map<string, GroupMember>) {
|
||||
}
|
||||
|
||||
onMemberListChange(arg: {
|
||||
@@ -142,102 +80,4 @@ export class GroupListener implements IGroupListener {
|
||||
|
||||
onShutUpMemberListChanged(...args: unknown[]) {
|
||||
}
|
||||
}
|
||||
|
||||
export class DebugGroupListener implements IGroupListener {
|
||||
onGroupMemberLevelInfoChange(...args: unknown[]): void {
|
||||
console.log('onGroupMemberLevelInfoChange:', ...args);
|
||||
}
|
||||
|
||||
onGetGroupBulletinListResult(...args: unknown[]) {
|
||||
console.log('onGetGroupBulletinListResult:', ...args);
|
||||
}
|
||||
|
||||
onGroupAllInfoChange(...args: unknown[]) {
|
||||
console.log('onGroupAllInfoChange:', ...args);
|
||||
}
|
||||
|
||||
onGroupBulletinChange(...args: unknown[]) {
|
||||
console.log('onGroupBulletinChange:', ...args);
|
||||
}
|
||||
|
||||
onGroupBulletinRemindNotify(...args: unknown[]) {
|
||||
console.log('onGroupBulletinRemindNotify:', ...args);
|
||||
}
|
||||
|
||||
onGroupArkInviteStateResult(...args: unknown[]) {
|
||||
console.log('onGroupArkInviteStateResult:', ...args);
|
||||
}
|
||||
|
||||
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]) {
|
||||
console.log('onGroupBulletinRichMediaDownloadComplete:', ...args);
|
||||
}
|
||||
|
||||
onGroupConfMemberChange(...args: unknown[]) {
|
||||
console.log('onGroupConfMemberChange:', ...args);
|
||||
}
|
||||
|
||||
onGroupDetailInfoChange(...args: unknown[]) {
|
||||
console.log('onGroupDetailInfoChange:', ...args);
|
||||
}
|
||||
|
||||
onGroupExtListUpdate(...args: unknown[]) {
|
||||
console.log('onGroupExtListUpdate:', ...args);
|
||||
}
|
||||
|
||||
onGroupFirstBulletinNotify(...args: unknown[]) {
|
||||
console.log('onGroupFirstBulletinNotify:', ...args);
|
||||
}
|
||||
|
||||
onGroupListUpdate(...args: unknown[]) {
|
||||
console.log('onGroupListUpdate:', ...args);
|
||||
}
|
||||
|
||||
onGroupNotifiesUpdated(...args: unknown[]) {
|
||||
console.log('onGroupNotifiesUpdated:', ...args);
|
||||
}
|
||||
|
||||
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]) {
|
||||
console.log('onGroupBulletinRichMediaProgressUpdate:', ...args);
|
||||
}
|
||||
|
||||
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) {
|
||||
console.log('onGroupNotifiesUnreadCountUpdated:', ...args);
|
||||
}
|
||||
|
||||
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]) {
|
||||
console.log('onGroupSingleScreenNotifies:');
|
||||
}
|
||||
|
||||
onGroupsMsgMaskResult(...args: unknown[]) {
|
||||
console.log('onGroupsMsgMaskResult:', ...args);
|
||||
}
|
||||
|
||||
onGroupStatisticInfoChange(...args: unknown[]) {
|
||||
console.log('onGroupStatisticInfoChange:', ...args);
|
||||
}
|
||||
|
||||
onJoinGroupNotify(...args: unknown[]) {
|
||||
console.log('onJoinGroupNotify:', ...args);
|
||||
}
|
||||
|
||||
onJoinGroupNoVerifyFlag(...args: unknown[]) {
|
||||
console.log('onJoinGroupNoVerifyFlag:', ...args);
|
||||
}
|
||||
|
||||
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>) {
|
||||
console.log('onMemberInfoChange:', groupCode, changeType, members);
|
||||
}
|
||||
|
||||
onMemberListChange(...args: unknown[]) {
|
||||
console.log('onMemberListChange:', ...args);
|
||||
}
|
||||
|
||||
onSearchMemberChange(...args: unknown[]) {
|
||||
console.log('onSearchMemberChange:', ...args);
|
||||
}
|
||||
|
||||
onShutUpMemberListChanged(...args: unknown[]) {
|
||||
console.log('onShutUpMemberListChanged:', ...args);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,45 +1,5 @@
|
||||
export interface IKernelLoginListener {
|
||||
onLoginConnected(...args: any[]): void;
|
||||
|
||||
onLoginDisConnected(...args: any[]): void;
|
||||
|
||||
onLoginConnecting(...args: any[]): void;
|
||||
|
||||
onQRCodeGetPicture(...args: any[]): void;
|
||||
|
||||
onQRCodeLoginPollingStarted(...args: any[]): void;
|
||||
|
||||
onQRCodeSessionUserScaned(...args: any[]): void;
|
||||
|
||||
onQRCodeLoginSucceed(...args: any[]): void;
|
||||
|
||||
onQRCodeSessionFailed(...args: any[]): void;
|
||||
|
||||
onLoginFailed(...args: any[]): void;
|
||||
|
||||
onLogoutSucceed(...args: any[]): void;
|
||||
|
||||
onLogoutFailed(...args: any[]): void;
|
||||
|
||||
onUserLoggedIn(...args: any[]): void;
|
||||
|
||||
onQRCodeSessionQuickLoginFailed(...args: any[]): void;
|
||||
|
||||
onPasswordLoginFailed(...args: any[]): void;
|
||||
|
||||
OnConfirmUnusualDeviceFailed(...args: any[]): void;
|
||||
|
||||
onQQLoginNumLimited(...args: any[]): void;
|
||||
|
||||
onLoginState(...args: any[]): void;
|
||||
}
|
||||
|
||||
export interface NodeIKernelLoginListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(listener: IKernelLoginListener): NodeIKernelLoginListener;
|
||||
}
|
||||
|
||||
export class LoginListener implements IKernelLoginListener {
|
||||
export class NodeIKernelLoginListener {
|
||||
onLoginConnected(...args: any[]): void {
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ export interface OnRichMediaDownloadCompleteParams {
|
||||
userUsedSpacePerDay: unknown | null
|
||||
}
|
||||
|
||||
export interface onGroupFileInfoUpdateParamType {
|
||||
export interface GroupFileInfoUpdateParamType {
|
||||
retCode: number;
|
||||
retMsg: string;
|
||||
clientWording: string;
|
||||
@@ -52,199 +52,7 @@ export interface TempOnRecvParams {
|
||||
|
||||
}
|
||||
|
||||
export interface IKernelMsgListener {
|
||||
onAddSendMsg(msgRecord: RawMessage): void;
|
||||
|
||||
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown): void;
|
||||
|
||||
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown): void;
|
||||
|
||||
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown): void;
|
||||
|
||||
onContactUnreadCntUpdate(hashMap: unknown): void;
|
||||
|
||||
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown): void;
|
||||
|
||||
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown): void;
|
||||
|
||||
onEmojiDownloadComplete(emojiNotifyInfo: unknown): void;
|
||||
|
||||
onEmojiResourceUpdate(emojiResourceInfo: unknown): void;
|
||||
|
||||
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
|
||||
|
||||
onFileMsgCome(arrayList: unknown): void;
|
||||
|
||||
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
|
||||
|
||||
onFirstViewGroupGuildMapping(arrayList: unknown): void;
|
||||
|
||||
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown): void;
|
||||
|
||||
onGroupFileInfoAdd(groupItem: unknown): void;
|
||||
|
||||
onGroupFileInfoUpdate(groupFileListResult: onGroupFileInfoUpdateParamType): void;
|
||||
|
||||
onGroupGuildUpdate(groupGuildNotifyInfo: unknown): void;
|
||||
|
||||
onGroupTransferInfoAdd(groupItem: unknown): void;
|
||||
|
||||
onGroupTransferInfoUpdate(groupFileListResult: unknown): void;
|
||||
|
||||
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown): void;
|
||||
|
||||
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown): void;
|
||||
|
||||
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown): void;
|
||||
|
||||
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown): void;
|
||||
|
||||
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown): void;
|
||||
|
||||
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown): void;
|
||||
|
||||
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown): void;
|
||||
|
||||
onInputStatusPush(inputStatusInfo: {
|
||||
chatType: number;
|
||||
eventType: number;
|
||||
fromUin: string;
|
||||
interval: string;
|
||||
showTime: string;
|
||||
statusText: string;
|
||||
timestamp: string;
|
||||
toUin: string;
|
||||
}): void;
|
||||
|
||||
onKickedOffLine(kickedInfo: unknown): void;
|
||||
|
||||
onLineDev(arrayList: unknown): void;
|
||||
|
||||
onLogLevelChanged(j2: unknown): void;
|
||||
|
||||
onMsgAbstractUpdate(arrayList: unknown): void;
|
||||
|
||||
onMsgBoxChanged(arrayList: unknown): void;
|
||||
|
||||
onMsgDelete(contact: unknown, arrayList: unknown): void;
|
||||
|
||||
onMsgEventListUpdate(hashMap: unknown): void;
|
||||
|
||||
onMsgInfoListAdd(arrayList: unknown): void;
|
||||
|
||||
onMsgInfoListUpdate(msgList: RawMessage[]): void;
|
||||
|
||||
onMsgQRCodeStatusChanged(i2: unknown): void;
|
||||
|
||||
onMsgRecall(i2: unknown, str: unknown, j2: unknown): void;
|
||||
|
||||
onMsgSecurityNotify(msgRecord: unknown): void;
|
||||
|
||||
onMsgSettingUpdate(msgSetting: unknown): void;
|
||||
|
||||
onNtFirstViewMsgSyncEnd(): void;
|
||||
|
||||
onNtMsgSyncEnd(): void;
|
||||
|
||||
onNtMsgSyncStart(): void;
|
||||
|
||||
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
|
||||
|
||||
onRecvGroupGuildFlag(i2: unknown): void;
|
||||
|
||||
onRecvMsg(...arrayList: unknown[]): void;
|
||||
|
||||
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown): void;
|
||||
|
||||
onRecvOnlineFileMsg(arrayList: unknown): void;
|
||||
|
||||
onRecvS2CMsg(arrayList: unknown): void;
|
||||
|
||||
onRecvSysMsg(arrayList: unknown): void;
|
||||
|
||||
onRecvUDCFlag(i2: unknown): void;
|
||||
|
||||
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): void;
|
||||
|
||||
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown): void;
|
||||
|
||||
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void;
|
||||
|
||||
onSearchGroupFileInfoUpdate(searchGroupFileResult:
|
||||
{
|
||||
result: {
|
||||
retCode: number,
|
||||
retMsg: string,
|
||||
clientWording: string
|
||||
},
|
||||
syncCookie: string,
|
||||
totalMatchCount: number,
|
||||
ownerMatchCount: number,
|
||||
isEnd: boolean,
|
||||
reqId: number,
|
||||
item: Array<{
|
||||
groupCode: string,
|
||||
groupName: string,
|
||||
uploaderUin: string,
|
||||
uploaderName: string,
|
||||
matchUin: string,
|
||||
matchWords: Array<unknown>,
|
||||
fileNameHits: Array<{
|
||||
start: number,
|
||||
end: number
|
||||
}>,
|
||||
fileModelId: string,
|
||||
fileId: string,
|
||||
fileName: string,
|
||||
fileSize: string,
|
||||
busId: number,
|
||||
uploadTime: number,
|
||||
modifyTime: number,
|
||||
deadTime: number,
|
||||
downloadTimes: number,
|
||||
localPath: string
|
||||
}>
|
||||
}): void;
|
||||
|
||||
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void;
|
||||
|
||||
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown): void;
|
||||
|
||||
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams): void;
|
||||
|
||||
onUnreadCntAfterFirstView(hashMap: unknown): void;
|
||||
|
||||
onUnreadCntUpdate(hashMap: unknown): void;
|
||||
|
||||
onUserChannelTabStatusChanged(z: unknown): void;
|
||||
|
||||
onUserOnlineStatusChanged(z: unknown): void;
|
||||
|
||||
onUserTabStatusChanged(arrayList: unknown): void;
|
||||
|
||||
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void;
|
||||
|
||||
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void;
|
||||
|
||||
// 第一次发现于Linux
|
||||
onUserSecQualityChanged(...args: unknown[]): void;
|
||||
|
||||
onMsgWithRichLinkInfoUpdate(...args: unknown[]): void;
|
||||
|
||||
onRedTouchChanged(...args: unknown[]): void;
|
||||
|
||||
// 第一次发现于Win 9.9.9 23159
|
||||
onBroadcastHelperProgerssUpdate(...args: unknown[]): void;
|
||||
|
||||
}
|
||||
|
||||
export interface NodeIKernelMsgListener extends IKernelMsgListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(listener: IKernelMsgListener): NodeIKernelMsgListener;
|
||||
}
|
||||
|
||||
|
||||
export class MsgListener implements IKernelMsgListener {
|
||||
export class NodeIKernelMsgListener {
|
||||
onAddSendMsg(msgRecord: RawMessage) {
|
||||
|
||||
}
|
||||
@@ -305,7 +113,7 @@ export class MsgListener implements IKernelMsgListener {
|
||||
|
||||
}
|
||||
|
||||
onGroupFileInfoUpdate(groupFileListResult: onGroupFileInfoUpdateParamType) {
|
||||
onGroupFileInfoUpdate(groupFileListResult: GroupFileInfoUpdateParamType) {
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,25 +1,6 @@
|
||||
import { User, UserDetailInfoListenerArg } from '@/core/entities';
|
||||
|
||||
interface IProfileListener {
|
||||
onProfileSimpleChanged(...args: unknown[]): void;
|
||||
|
||||
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void;
|
||||
|
||||
onProfileDetailInfoChanged(profile: User): void;
|
||||
|
||||
onStatusUpdate(...args: unknown[]): void;
|
||||
|
||||
onSelfStatusChanged(...args: unknown[]): void;
|
||||
|
||||
onStrangerRemarkChanged(...args: unknown[]): void;
|
||||
}
|
||||
|
||||
export interface NodeIKernelProfileListener extends IProfileListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(listener: IProfileListener): NodeIKernelProfileListener;
|
||||
}
|
||||
|
||||
export class ProfileListener implements IProfileListener {
|
||||
export class NodeIKernelProfileListener {
|
||||
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void {
|
||||
|
||||
}
|
||||
@@ -43,4 +24,48 @@ export class ProfileListener implements IProfileListener {
|
||||
onStrangerRemarkChanged(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onMemberListChange(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onMemberInfoChange(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupListUpdate(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupAllInfoChange(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupDetailInfoChange(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupConfMemberChange(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupExtListUpdate(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupNotifiesUpdated(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupMemberLevelInfoChange(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
||||
onGroupBulletinChange(...args: unknown[]) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +1,4 @@
|
||||
interface IKernelRecentContactListener {
|
||||
onDeletedContactsNotify(...args: unknown[]): unknown;
|
||||
|
||||
onRecentContactNotification(...args: unknown[]): unknown;
|
||||
|
||||
onMsgUnreadCountUpdate(...args: unknown[]): unknown;
|
||||
|
||||
onGuildDisplayRecentContactListChanged(...args: unknown[]): unknown;
|
||||
|
||||
onRecentContactListChanged(...args: unknown[]): unknown;
|
||||
|
||||
onRecentContactListChangedVer2(...args: unknown[]): unknown;
|
||||
}
|
||||
|
||||
export interface NodeIKernelRecentContactListener extends IKernelRecentContactListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(listener: IKernelRecentContactListener): NodeIKernelRecentContactListener;
|
||||
}
|
||||
|
||||
export class KernelRecentContactListener implements IKernelRecentContactListener {
|
||||
export class NodeIKernelRecentContactListener {
|
||||
onDeletedContactsNotify(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
@@ -1,17 +1,4 @@
|
||||
export interface IKernelRobotListener {
|
||||
onRobotFriendListChanged(...args: unknown[]): void;
|
||||
|
||||
onRobotListChanged(...args: unknown[]): void;
|
||||
|
||||
onRobotProfileChanged(...args: unknown[]): void;
|
||||
}
|
||||
|
||||
export interface NodeIKernelRobotListener extends IKernelRobotListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(adapter: IKernelRobotListener): NodeIKernelRobotListener;
|
||||
}
|
||||
|
||||
export class KernelRobotListener implements IKernelRobotListener {
|
||||
export class NodeIKernelRobotListener{
|
||||
onRobotFriendListChanged(...args: unknown[]) {
|
||||
|
||||
}
|
||||
|
@@ -1,23 +1,4 @@
|
||||
export interface ISessionListener {
|
||||
onNTSessionCreate(args: unknown): void;
|
||||
|
||||
onGProSessionCreate(args: unknown): void;
|
||||
|
||||
onSessionInitComplete(args: unknown): void;
|
||||
|
||||
onOpentelemetryInit(args: unknown): void;
|
||||
|
||||
onUserOnlineResult(args: unknown): void;
|
||||
|
||||
onGetSelfTinyId(args: unknown): void;
|
||||
}
|
||||
|
||||
export interface NodeIKernelSessionListener extends ISessionListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(adapter: ISessionListener): NodeIKernelSessionListener;
|
||||
}
|
||||
|
||||
export class SessionListener implements ISessionListener {
|
||||
export class NodeIKernelSessionListener {
|
||||
onNTSessionCreate(args: unknown) {
|
||||
|
||||
}
|
||||
|
@@ -1,22 +1,4 @@
|
||||
export interface IStorageCleanListener {
|
||||
onCleanCacheProgressChanged(args: unknown): void;
|
||||
|
||||
onScanCacheProgressChanged(args: unknown): void;
|
||||
|
||||
onCleanCacheStorageChanged(args: unknown): void;
|
||||
|
||||
onFinishScan(args: unknown): void;
|
||||
|
||||
onChatCleanDone(args: unknown): void;
|
||||
|
||||
}
|
||||
|
||||
export interface NodeIKernelStorageCleanListener extends IStorageCleanListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(adapter: IStorageCleanListener): NodeIKernelStorageCleanListener;
|
||||
}
|
||||
|
||||
export class StorageCleanListener implements IStorageCleanListener {
|
||||
export class NodeIKernelStorageCleanListener {
|
||||
onCleanCacheProgressChanged(args: unknown) {
|
||||
|
||||
}
|
||||
|
@@ -1,10 +1,2 @@
|
||||
export interface IKernelTicketListener {
|
||||
}
|
||||
|
||||
export interface NodeIKernelTicketListener extends IKernelTicketListener {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(adapter: IKernelTicketListener): NodeIKernelTicketListener;
|
||||
}
|
||||
|
||||
export class KernelTicketListener implements IKernelTicketListener {
|
||||
export class NodeIKernelTicketListener {
|
||||
}
|
||||
|
@@ -61,11 +61,11 @@ export interface NodeIKernelBuddyService {
|
||||
|
||||
getBuddyReqUnreadCnt(): number;
|
||||
|
||||
getBuddyReq(): unknown;
|
||||
getBuddyReq(): Promise<GeneralCallResult>;
|
||||
|
||||
delBuddyReq(uid: number): void;
|
||||
|
||||
clearBuddyReqUnreadCnt(): void;
|
||||
clearBuddyReqUnreadCnt(): Promise<GeneralCallResult>;
|
||||
|
||||
reqToAddFriends(uid: number, msg: string): void;
|
||||
|
||||
|
@@ -1,16 +1,20 @@
|
||||
import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
|
||||
import { IGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
|
||||
import {
|
||||
GroupExtParam,
|
||||
GroupMember,
|
||||
GroupMemberRole,
|
||||
GroupNotifyTypes,
|
||||
GroupNotifyMsgType,
|
||||
GroupRequestOperateTypes,
|
||||
KickMemberV2Req
|
||||
} from '@/core/entities';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
|
||||
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
|
||||
|
||||
export interface NodeIKernelGroupService {
|
||||
kickMemberV2(param: KickMemberV2Req): Promise<GeneralCallResult>;
|
||||
quitGroupV2(param: { groupCode: string; needDeleteLocalMsg: boolean; }): Promise<GeneralCallResult>;
|
||||
|
||||
getMemberCommonInfo(Req: {
|
||||
groupCode: string,
|
||||
startUin: string,
|
||||
@@ -96,7 +100,7 @@ export interface NodeIKernelGroupService {
|
||||
uid: string,
|
||||
index: number//0
|
||||
}>,
|
||||
infos: {},
|
||||
infos: unknown,
|
||||
finish: true,
|
||||
hasRobot: false
|
||||
}
|
||||
@@ -104,7 +108,7 @@ export interface NodeIKernelGroupService {
|
||||
|
||||
setHeader(uid: string, path: string): unknown;
|
||||
|
||||
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
|
||||
addKernelGroupListener(listener: IGroupListener): number;
|
||||
|
||||
removeKernelGroupListener(listenerId: unknown): void;
|
||||
|
||||
@@ -195,7 +199,7 @@ export interface NodeIKernelGroupService {
|
||||
operateType: GroupRequestOperateTypes, // 2 拒绝
|
||||
targetMsg: {
|
||||
seq: string, // 通知序列号
|
||||
type: GroupNotifyTypes,
|
||||
type: GroupNotifyMsgType,
|
||||
groupCode: string,
|
||||
postscript: string
|
||||
}
|
||||
@@ -205,7 +209,7 @@ export interface NodeIKernelGroupService {
|
||||
|
||||
getGroupBulletin(groupCode: string): unknown;
|
||||
|
||||
deleteGroupBulletin(groupCode: string, seq: string): void;
|
||||
deleteGroupBulletin(groupCode: string, seq: string, feedId: string): void;
|
||||
|
||||
publishGroupBulletin(groupCode: string, pskey: string, data: any): Promise<GeneralCallResult>;
|
||||
|
||||
|
@@ -9,7 +9,7 @@ export interface LoginInitConfig {
|
||||
hostName: string;
|
||||
}
|
||||
|
||||
export interface passwordLoginRetType {
|
||||
export interface PasswordLoginRetType {
|
||||
result: string,
|
||||
loginErrorInfo: {
|
||||
step: number;
|
||||
@@ -23,7 +23,7 @@ export interface passwordLoginRetType {
|
||||
}
|
||||
}
|
||||
|
||||
export interface passwordLoginArgType {
|
||||
export interface PasswordLoginArgType {
|
||||
uin: string;
|
||||
passwordMd5: string;//passwMD5
|
||||
step: number;//猜测是需要二次认证 参数 一次为0
|
||||
@@ -77,7 +77,7 @@ export interface NodeIKernelLoginService {
|
||||
|
||||
quickLoginWithUin(uin: string): Promise<QuickLoginResult>;
|
||||
|
||||
passwordLogin(param: passwordLoginArgType): Promise<any>;
|
||||
passwordLogin(param: PasswordLoginArgType): Promise<any>;
|
||||
|
||||
getQRCodePicture(): boolean;
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ export interface NodeIKernelMsgService {
|
||||
|
||||
recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;
|
||||
|
||||
addKernelMsgImportToolListener(arg: Object): unknown;
|
||||
addKernelMsgImportToolListener(arg: unknown): unknown;
|
||||
|
||||
removeKernelMsgListener(args: unknown): unknown;
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface NodeIKernelMsgService {
|
||||
|
||||
getOnLineDev(): void;
|
||||
|
||||
kickOffLine(DevInfo: Object): unknown;
|
||||
kickOffLine(DevInfo: unknown): unknown;
|
||||
|
||||
setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise<GeneralCallResult>;
|
||||
|
||||
@@ -80,11 +80,11 @@ export interface NodeIKernelMsgService {
|
||||
// this.voipToken = bArr2;
|
||||
// this.profileId = str;
|
||||
|
||||
setToken(arg: Object): unknown;
|
||||
setToken(arg: unknown): unknown;
|
||||
|
||||
switchForeGround(): unknown;
|
||||
|
||||
switchBackGround(arg: Object): unknown;
|
||||
switchBackGround(arg: unknown): unknown;
|
||||
|
||||
//hex
|
||||
setTokenForMqq(token: string): unknown;
|
||||
@@ -384,7 +384,7 @@ export interface NodeIKernelMsgService {
|
||||
getFileThumbSavePath(...args: unknown[]): unknown;
|
||||
|
||||
//猜测居多
|
||||
translatePtt2Text(MsgId: string, Peer: {}, MsgElement: {}): unknown;
|
||||
translatePtt2Text(MsgId: string, Peer: Peer, MsgElement: unknown): unknown;
|
||||
|
||||
setPttPlayedState(...args: unknown[]): unknown;
|
||||
|
||||
@@ -668,7 +668,7 @@ export interface NodeIKernelMsgService {
|
||||
|
||||
recordEmoji(...args: unknown[]): unknown;
|
||||
|
||||
fetchGetHitEmotionsByWord(args: Object): Promise<unknown>;//表情推荐?
|
||||
fetchGetHitEmotionsByWord(args: unknown): Promise<unknown>;//表情推荐?
|
||||
|
||||
deleteAllRoamMsgs(...args: unknown[]): unknown;//漫游消息?
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { AnyCnameRecord } from 'node:dns';
|
||||
import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
|
||||
import { NodeIKernelProfileListener } from '../listeners';
|
||||
import { NodeIKernelProfileListener } from '../listeners';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
|
||||
export enum UserDetailSource {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { forceFetchClientKeyRetType } from './common';
|
||||
import { ForceFetchClientKeyRetType } from './common';
|
||||
|
||||
export interface NodeIKernelTicketService {
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface NodeIKernelTicketService {
|
||||
|
||||
removeKernelTicketListener(listenerId: unknown): void;
|
||||
|
||||
forceFetchClientKey(arg: string): Promise<forceFetchClientKeyRetType>;
|
||||
forceFetchClientKey(arg: string): Promise<ForceFetchClientKeyRetType>;
|
||||
|
||||
isNull(): boolean;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ export interface GeneralCallResult {
|
||||
errMsg: string
|
||||
}
|
||||
|
||||
export interface forceFetchClientKeyRetType extends GeneralCallResult {
|
||||
export interface ForceFetchClientKeyRetType extends GeneralCallResult {
|
||||
url: string;
|
||||
keyIndex: string;
|
||||
clientKey: string;
|
||||
|
@@ -1,11 +1,6 @@
|
||||
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '../adapters';
|
||||
import {
|
||||
NodeIKernelBuddyListener,
|
||||
NodeIKernelGroupListener,
|
||||
NodeIKernelLoginListener,
|
||||
NodeIKernelMsgListener,
|
||||
NodeIKernelProfileListener,
|
||||
NodeIKernelSessionListener,
|
||||
NodeIKernelSessionListener
|
||||
} from '../listeners';
|
||||
import {
|
||||
NodeIKernelAvatarService,
|
||||
@@ -35,6 +30,7 @@ import { NodeIkernelTestPerformanceService } from '../services/NodeIkernelTestPe
|
||||
import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService';
|
||||
|
||||
export interface NodeQQNTWrapperUtil {
|
||||
get(): unknown;
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||
new(): NodeQQNTWrapperUtil;
|
||||
|
||||
@@ -68,9 +64,9 @@ export interface NodeQQNTWrapperUtil {
|
||||
|
||||
genFileShaAndMd5Hex(path: string, unknown: number): unknown; //可能是错的
|
||||
|
||||
setTraceInfo(unknown: Object): unknown;
|
||||
setTraceInfo(unknown: unknown): unknown;
|
||||
|
||||
encodeOffLine(unknown: Object): unknown;
|
||||
encodeOffLine(unknown: unknown): unknown;
|
||||
|
||||
decodeOffLine(arg: string): unknown; //可能是错的 传递hex
|
||||
|
||||
@@ -88,7 +84,7 @@ export interface NodeQQNTWrapperUtil {
|
||||
|
||||
runProcessArgs(arg0: string, arg1: { [key: string]: string }, arg2: boolean): unknown;
|
||||
|
||||
calcThumbSize(arg0: number, arg1: number, arg2: Object): unknown;
|
||||
calcThumbSize(arg0: number, arg1: number, arg2: unknown): unknown;
|
||||
|
||||
fullWordToHalfWord(arg0: string): unknown;
|
||||
|
||||
@@ -277,20 +273,11 @@ export interface NodeIQQNTWrapperEngine {
|
||||
export interface WrapperNodeApi {
|
||||
[key: string]: any;
|
||||
|
||||
NodeIKernelBuddyListener: NodeIKernelBuddyListener;
|
||||
NodeIKernelGroupListener: NodeIKernelGroupListener;
|
||||
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
|
||||
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
|
||||
NodeIKernelMsgListener: NodeIKernelMsgListener;
|
||||
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
|
||||
NodeIGlobalAdapter: NodeIGlobalAdapter;
|
||||
NodeIDependsAdapter: NodeIDependsAdapter;
|
||||
NodeIDispatcherAdapter: NodeIDispatcherAdapter;
|
||||
NodeIKernelSessionListener: NodeIKernelSessionListener;
|
||||
NodeIKernelLoginService: NodeIKernelLoginService;
|
||||
NodeIKernelLoginListener: NodeIKernelLoginListener;
|
||||
NodeIKernelProfileService: NodeIKernelProfileService;
|
||||
NodeIKernelProfileListener: NodeIKernelProfileListener;
|
||||
}
|
||||
|
||||
export enum PlatformType {
|
||||
|
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@ import { QQBasicInfoWrapper } from '@/common/utils/QQBasicInfo';
|
||||
import { loadQQWrapper, NapCatCore, NapCatCoreWorkingEnv } from '@/core/core';
|
||||
import { InstanceContext } from '@/core';
|
||||
import { SelfInfo } from '@/core/entities';
|
||||
import { LoginListener } from '@/core/listeners';
|
||||
import { NodeIKernelLoginListener } from '@/core/listeners';
|
||||
import { NodeIKernelLoginService } from '@/core/services';
|
||||
import { NodeIQQNTWrapperSession, WrapperNodeApi } from '@/core/wrapper/wrapper';
|
||||
import { InitWebUi, WebUiConfig } from '@/webui';
|
||||
@@ -13,7 +13,7 @@ import { NapCatOneBot11Adapter } from '@/onebot';
|
||||
|
||||
//Framework ES入口文件
|
||||
export async function getWebUiUrl() {
|
||||
let WebUiConfigData = (await WebUiConfig.GetWebUIConfig());
|
||||
const WebUiConfigData = (await WebUiConfig.GetWebUIConfig());
|
||||
return "http://127.0.0.1:" + WebUiConfigData.port + '/webui/?token=' + WebUiConfigData.token;
|
||||
}
|
||||
export async function NCoreInitFramework(
|
||||
@@ -29,7 +29,7 @@ export async function NCoreInitFramework(
|
||||
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
|
||||
//直到登录成功后,执行下一步
|
||||
const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
|
||||
const loginListener = new LoginListener();
|
||||
const loginListener = new NodeIKernelLoginListener();
|
||||
loginListener.onQRCodeLoginSucceed = async (loginResult) => {
|
||||
await new Promise<void>(resolvePendingInit => {
|
||||
registerInitCallback(() => resolvePendingInit());
|
||||
@@ -41,8 +41,7 @@ export async function NCoreInitFramework(
|
||||
online: true,
|
||||
});
|
||||
};
|
||||
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener(
|
||||
proxiedListenerOf(loginListener, logger)));
|
||||
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
|
||||
});
|
||||
// 过早进入会导致addKernelMsgListener等Listener添加失败
|
||||
// await sleep(2500);
|
||||
|
@@ -1,14 +1,38 @@
|
||||
|
||||
export const onSettingWindowCreated = async (view) => {
|
||||
view.style.width = "100%";
|
||||
view.style.height = "100%";
|
||||
//添加iframe
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.src = await window.napcat.getWebUiUrl();
|
||||
iframe.width = "100%";
|
||||
iframe.height = "100%";
|
||||
iframe.style.border = "none";
|
||||
//去掉iframe滚动条
|
||||
//iframe.scrolling = "no";
|
||||
//有滚动条何尝不是一种美
|
||||
view.appendChild(iframe);
|
||||
|
||||
// view.style.width = "100%";
|
||||
// view.style.height = "100%";
|
||||
// //添加iframe
|
||||
// const iframe = document.createElement("iframe");
|
||||
// iframe.src = await window.napcat.getWebUiUrl();
|
||||
// iframe.width = "100%";
|
||||
// iframe.height = "100%";
|
||||
// iframe.style.border = "none";
|
||||
// //去掉iframe滚动条
|
||||
// //iframe.scrolling = "no";
|
||||
// //有滚动条何尝不是一种美
|
||||
// view.appendChild(iframe);
|
||||
let webui = await window.napcat.getWebUiUrl();
|
||||
let panel = `
|
||||
<setting-section data-title="">
|
||||
<setting-panel>
|
||||
<setting-list data-direction="column">
|
||||
<setting-item>
|
||||
<setting-button data-type="primary" class="nc_openwebui">打开配置页面</setting-button>
|
||||
</setting-item>
|
||||
<setting-item>
|
||||
<div>
|
||||
<setting-text class="nc_webui">WebUi</setting-text>
|
||||
</div>
|
||||
</setting-item>
|
||||
</setting-list>
|
||||
</setting-panel>
|
||||
</setting-section>
|
||||
`;
|
||||
view.innerHTML = panel;
|
||||
view.querySelector(".nc_openwebui").addEventListener("click", () => {
|
||||
window.open(webui, "_blank");
|
||||
});
|
||||
view.querySelector(".nc_webui").innerText = webui;
|
||||
};
|
@@ -5,7 +5,7 @@ import { ActionName } from '../types';
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
count: { type: 'number' },
|
||||
count: { type: ['number', 'string'] },
|
||||
},
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
@@ -17,7 +17,7 @@ export class FetchCustomFace extends BaseAction<Payload, string[]> {
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
//48 可能正好是QQ需要的一个页面的数量 Tagged Mlikiowa
|
||||
const ret = await this.CoreContext.apis.MsgApi.fetchFavEmojiList(payload.count || 48);
|
||||
const ret = await this.CoreContext.apis.MsgApi.fetchFavEmojiList(+(payload.count ?? 48));
|
||||
return ret.emojiInfoList.map(e => e.url);
|
||||
}
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ const SchemaData = {
|
||||
emojiId: { type: 'string' },
|
||||
emojiType: { type: 'string' },
|
||||
message_id: { type: ['string', 'number'] },
|
||||
count: { type: 'number' },
|
||||
count: { type: ['string', 'number'] },
|
||||
},
|
||||
required: ['emojiId', 'emojiType', 'message_id'],
|
||||
} as const satisfies JSONSchema;
|
||||
@@ -22,12 +22,11 @@ type Payload = FromSchema<typeof SchemaData>;
|
||||
export class FetchEmojiLike extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.FetchEmojiLike;
|
||||
PayloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
|
||||
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||
if (!msgIdPeer) throw new Error('消息不存在');
|
||||
const msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
|
||||
return await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer, msg.msgSeq, payload.emojiId, payload.emojiType, payload.count);
|
||||
return await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer, msg.msgSeq, payload.emojiId, payload.emojiType, +(payload.count ?? 20));
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
category: { type: 'number' },
|
||||
count: { type: 'number' },
|
||||
category: { type: ['number', 'string'] },
|
||||
count: { type: ['number', 'string'] },
|
||||
},
|
||||
required: ['category', 'count'],
|
||||
} as const satisfies JSONSchema;
|
||||
@@ -16,9 +16,8 @@ type Payload = FromSchema<typeof SchemaData>;
|
||||
export class GetCollectionList extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.GetCollectionList;
|
||||
PayloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQCollectionApi = this.CoreContext.apis.CollectionApi;
|
||||
return await NTQQCollectionApi.getAllCollection(payload.category, payload.count);
|
||||
return await NTQQCollectionApi.getAllCollection(parseInt(payload.category.toString()), +(payload.count ?? 1));
|
||||
}
|
||||
}
|
||||
|
@@ -1,19 +1,13 @@
|
||||
import { OB11Constructor } from '@/onebot/helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
|
||||
export class GetFriendWithCategory extends BaseAction<void, any> {
|
||||
actionName = ActionName.GetFriendsWithCategory;
|
||||
|
||||
async _handle(payload: void) {
|
||||
if (this.CoreContext.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||
//全新逻辑
|
||||
return (await this.CoreContext.apis.FriendApi.getBuddyV2ExWithCate(true)).map(category => ({
|
||||
...category,
|
||||
buddyList: OB11Constructor.friendsV2(category.buddyList),
|
||||
}));
|
||||
} else {
|
||||
throw new Error('this ntqq version not support, must be 26702 or later');
|
||||
}
|
||||
return (await this.CoreContext.apis.FriendApi.getBuddyV2ExWithCate(true)).map(category => ({
|
||||
...category,
|
||||
buddyList: OB11Constructor.friendsV2(category.buddyList),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ export class GetProfileLike extends BaseAction<void, any> {
|
||||
const ret = await NTQQUserApi.getProfileLike(this.CoreContext.selfInfo.uid);
|
||||
const listdata: any[] = ret.info.userLikeInfos[0].favoriteInfo.userInfos;
|
||||
for (let i = 0; i < listdata.length; i++) {
|
||||
listdata[i].uin = parseInt((await NTQQUserApi.getUinByUid(listdata[i].uid)) || '');
|
||||
listdata[i].uin = parseInt((await NTQQUserApi.getUinByUidV2(listdata[i].uid)) || '');
|
||||
}
|
||||
return listdata;
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ export class GetRobotUinRange extends BaseAction<void, Array<any>> {
|
||||
actionName = ActionName.GetRobotUinRange;
|
||||
|
||||
async _handle(payload: void) {
|
||||
// console.log(await NTQQUserApi.getRobotUinRange());
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
return await NTQQUserApi.getRobotUinRange();
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||
import fs from 'fs';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
44
src/onebot/action/extends/SetInputStatus.ts
Normal file
44
src/onebot/action/extends/SetInputStatus.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { ChatType, Peer } from '@/core';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
eventType: { type: 'string' },
|
||||
group_id: { type: 'string' },
|
||||
user_id: { type: 'string' }
|
||||
},
|
||||
required: ['eventType'],
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class SetInputStatus extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.SetInputStatus;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
|
||||
let peer: Peer;
|
||||
if (payload.group_id) {
|
||||
peer = {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: payload.group_id
|
||||
};
|
||||
} else if (payload.user_id) {
|
||||
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id);
|
||||
if (!uid) throw new Error('uid is empty');
|
||||
peer = {
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: uid
|
||||
};
|
||||
} else {
|
||||
throw new Error('请指定 group_id 或 user_id');
|
||||
}
|
||||
|
||||
const ret = await NTQQMsgApi.sendShowInputStatusReq(peer, parseInt(payload.eventType));
|
||||
return ret;
|
||||
}
|
||||
}
|
@@ -6,9 +6,9 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
status: { type: 'number' },
|
||||
extStatus: { type: 'number' },
|
||||
batteryStatus: { type: 'number' },
|
||||
status: { type: ['number', 'string'] },
|
||||
extStatus: { type: ['number', 'string'] },
|
||||
batteryStatus: { type: ['number', 'string'] },
|
||||
},
|
||||
required: ['status', 'extStatus', 'batteryStatus'],
|
||||
} as const satisfies JSONSchema;
|
||||
@@ -20,14 +20,12 @@ export class SetOnlineStatus extends BaseAction<Payload, null> {
|
||||
PayloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
// 可设置状态
|
||||
// { status: 10, extStatus: 1027, batteryStatus: 0 }
|
||||
// { status: 30, extStatus: 0, batteryStatus: 0 }
|
||||
// { status: 50, extStatus: 0, batteryStatus: 0 }
|
||||
// { status: 60, extStatus: 0, batteryStatus: 0 }
|
||||
// { status: 70, extStatus: 0, batteryStatus: 0 }
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
const ret = await NTQQUserApi.setSelfOnlineStatus(payload.status, payload.extStatus, payload.batteryStatus);
|
||||
const ret = await NTQQUserApi.setSelfOnlineStatus(
|
||||
parseInt(payload.status.toString()),
|
||||
parseInt(payload.extStatus.toString()),
|
||||
parseInt(payload.batteryStatus.toString())
|
||||
);
|
||||
if (ret.result !== 0) {
|
||||
throw new Error('设置在线状态失败');
|
||||
}
|
||||
|
@@ -1,32 +0,0 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
nick: { type: 'string' },
|
||||
longNick: { type: 'string' },
|
||||
sex: { type: 'number' },//传Sex值?建议传0
|
||||
},
|
||||
required: ['nick', 'longNick', 'sex'],
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class SetSelfProfile extends BaseAction<Payload, any | null> {
|
||||
actionName = ActionName.SetSelfProfile;
|
||||
PayloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
const ret = await NTQQUserApi.modifySelfProfile({
|
||||
nick: payload.nick,
|
||||
longNick: payload.longNick,
|
||||
sex: payload.sex,
|
||||
birthday: { birthday_year: '', birthday_month: '', birthday_day: '' },
|
||||
location: undefined,
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
}
|
@@ -28,19 +28,6 @@ const GetFileBase_PayloadSchema = {
|
||||
export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
PayloadSchema: any = GetFileBase_PayloadSchema;
|
||||
|
||||
private getElement(msg: RawMessage): { id: string, element: VideoElement | FileElement } {
|
||||
let element = msg.elements.find(e => e.fileElement);
|
||||
if (!element) {
|
||||
element = msg.elements.find(e => e.videoElement);
|
||||
if (element) {
|
||||
return { id: element.elementId, element: element.videoElement };
|
||||
} else {
|
||||
throw new Error('找不到文件');
|
||||
}
|
||||
}
|
||||
return { id: element.elementId, element: element.fileElement };
|
||||
}
|
||||
|
||||
async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
|
||||
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
@@ -60,15 +47,15 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
let peer: Peer | undefined;
|
||||
//识别Peer
|
||||
if (isGroup) {
|
||||
peer = { chatType: ChatType.group, peerUid: peerUin };
|
||||
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: peerUin };
|
||||
}
|
||||
const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin);
|
||||
if (PeerUid) {
|
||||
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
|
||||
if (isBuddy) {
|
||||
peer = { chatType: ChatType.friend, peerUid: PeerUid };
|
||||
peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: PeerUid };
|
||||
} else {
|
||||
peer = { chatType: ChatType.temp, peerUid: PeerUid };
|
||||
peer = { chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: PeerUid };
|
||||
}
|
||||
}
|
||||
if (!peer) {
|
||||
@@ -92,7 +79,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
file_size: fileSize,
|
||||
file_name: fileName,
|
||||
};
|
||||
if (true/*enableLocalFile2Url*/) {
|
||||
if (true/*enableLocalFile2Url*/ && downloadPath) {
|
||||
try {
|
||||
res.base64 = await fs.readFile(downloadPath, 'base64');
|
||||
} catch (e) {
|
||||
@@ -103,15 +90,15 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
return res;
|
||||
}
|
||||
} catch {
|
||||
|
||||
this.CoreContext.context.logger.logDebug('GetFileBase Mode - 1 Error');
|
||||
}
|
||||
|
||||
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems;
|
||||
if (NTSearchNameResult.length !== 0) {
|
||||
const MsgId = NTSearchNameResult[0].msgId;
|
||||
let peer: Peer | undefined = undefined;
|
||||
if (NTSearchNameResult[0].chatType == ChatType.group) {
|
||||
peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
|
||||
if (NTSearchNameResult[0].chatType == ChatType.KCHATTYPEGROUP) {
|
||||
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
|
||||
}
|
||||
if (!peer) {
|
||||
throw new Error('chattype not support');
|
||||
@@ -132,7 +119,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||
file_size: NTSearchNameResult[0].fileSize.toString(),
|
||||
file_name: NTSearchNameResult[0].fileName,
|
||||
};
|
||||
if (true/*enableLocalFile2Url*/) {
|
||||
if (true/*enableLocalFile2Url*/ && downloadPath) {
|
||||
try {
|
||||
res.base64 = await fs.readFile(downloadPath, 'base64');
|
||||
} catch (e) {
|
||||
|
@@ -13,7 +13,7 @@ interface FileResponse {
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
thread_count: { type: 'number' },
|
||||
thread_count: { type: ['number', 'string'] },
|
||||
url: { type: 'string' },
|
||||
base64: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '@/onebot';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
@@ -39,15 +38,17 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
|
||||
throw Error('找不到相关的聊天记录' + data?.errMsg);
|
||||
}
|
||||
const msgList = data.msgList;
|
||||
const messages = await Promise.all(msgList.map(async msg => {
|
||||
const resMsg = await OB11Constructor.message(this.CoreContext, msg, 'array');
|
||||
const messages = (await Promise.all(msgList.map(async msg => {
|
||||
const resMsg = await this.OneBotContext.apiContext.MsgApi
|
||||
.parseMessage(msg);
|
||||
if (!resMsg) return;
|
||||
resMsg.message_id = MessageUnique.createMsg({
|
||||
guildId: '',
|
||||
chatType: msg.chatType,
|
||||
peerUid: msg.peerUid,
|
||||
}, msg.msgId)!;
|
||||
return resMsg;
|
||||
}));
|
||||
}))).filter(msg => !!msg);
|
||||
messages.map(msg => {
|
||||
(<OB11ForwardMessage>msg).content = msg.message;
|
||||
delete (<any>msg).message;
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { OB11Message } from '../../types';
|
||||
import { OB11Message } from '@/onebot';
|
||||
import { ActionName } from '../types';
|
||||
import { ChatType, RawMessage } from '@/core/entities';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
|
||||
@@ -15,7 +14,7 @@ const SchemaData = {
|
||||
properties: {
|
||||
user_id: { type: ['number', 'string'] },
|
||||
message_seq: { type: 'number' },
|
||||
count: { type: 'number' },
|
||||
count: { type: ['number', 'string'] },
|
||||
reverseOrder: { type: 'boolean' },
|
||||
},
|
||||
required: ['user_id'],
|
||||
@@ -33,11 +32,11 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
||||
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
|
||||
//处理参数
|
||||
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
|
||||
const MsgCount = payload.count || 20;
|
||||
const MsgCount = +(payload.count ?? 20);
|
||||
const isReverseOrder = payload.reverseOrder || true;
|
||||
if (!uid) throw `记录${payload.user_id}不存在`;
|
||||
const friend = await NTQQFriendApi.isBuddy(uid);
|
||||
const peer = { chatType: friend ? ChatType.friend : ChatType.temp, peerUid: uid };
|
||||
const peer = { chatType: friend ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: uid };
|
||||
|
||||
//拉取消息
|
||||
let msgList: RawMessage[];
|
||||
@@ -53,7 +52,9 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
||||
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||
}));
|
||||
//转换消息
|
||||
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(this.CoreContext, msg, 'array')));
|
||||
const ob11MsgList = (await Promise.all(
|
||||
msgList.map(msg => this.OneBotContext.apiContext.MsgApi.parseMessage(msg)))
|
||||
).filter(msg => msg !== undefined);
|
||||
return { 'messages': ob11MsgList };
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { OB11Message } from '../../types';
|
||||
import { OB11Message } from '@/onebot';
|
||||
import { ActionName } from '../types';
|
||||
import { ChatType, Peer, RawMessage } from '@/core/entities';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
|
||||
@@ -15,7 +14,7 @@ const SchemaData = {
|
||||
properties: {
|
||||
group_id: { type: ['number', 'string'] },
|
||||
message_seq: { type: 'number' },
|
||||
count: { type: 'number' },
|
||||
count: { type: ['number', 'string'] },
|
||||
reverseOrder: { type: 'boolean' },
|
||||
},
|
||||
required: ['group_id'],
|
||||
@@ -31,8 +30,8 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
|
||||
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
|
||||
//处理参数
|
||||
const isReverseOrder = payload.reverseOrder || true;
|
||||
const MsgCount = payload.count || 20;
|
||||
const peer: Peer = { chatType: ChatType.group, peerUid: payload.group_id.toString() };
|
||||
const MsgCount = +(payload.count ?? 20);
|
||||
const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
|
||||
//拉取消息
|
||||
let msgList: RawMessage[];
|
||||
if (!payload.message_seq || payload.message_seq == 0) {
|
||||
@@ -48,7 +47,9 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
|
||||
}));
|
||||
|
||||
//转换消息
|
||||
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(this.CoreContext, msg, 'array')));
|
||||
const ob11MsgList = (await Promise.all(
|
||||
msgList.map(msg => this.OneBotContext.apiContext.MsgApi.parseMessage(msg)))
|
||||
).filter(msg => msg !== undefined);
|
||||
return { 'messages': ob11MsgList };
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { OB11User, OB11UserSex } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { calcQQLevel } from '@/common/utils/helper';
|
||||
@@ -21,19 +21,22 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
|
||||
async _handle(payload: Payload): Promise<OB11User> {
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
const user_id = payload.user_id.toString();
|
||||
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
|
||||
const extendData = await NTQQUserApi.getUserDetailInfoByUinV2(user_id);
|
||||
const uid = (await NTQQUserApi.getUidByUinV2(user_id))!;
|
||||
if (!uid || uid.indexOf('*') != -1) {
|
||||
const ret = {
|
||||
...extendData,
|
||||
user_id: parseInt(extendData.info.uin) || 0,
|
||||
nickname: extendData.info.nick,
|
||||
...extendData.detail.simpleInfo.coreInfo,
|
||||
...extendData.detail.commonExt,
|
||||
...extendData.detail.simpleInfo.baseInfo,
|
||||
...extendData.detail.simpleInfo.relationFlags,
|
||||
user_id: parseInt(extendData.detail.uin) || 0,
|
||||
nickname: extendData.detail.simpleInfo.coreInfo.nick,
|
||||
sex: OB11UserSex.unknown,
|
||||
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
|
||||
qid: extendData.info.qid,
|
||||
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
|
||||
age: extendData.detail.simpleInfo.baseInfo.age || 0,
|
||||
qid: extendData.detail.simpleInfo.baseInfo.qid,
|
||||
level: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? 0) || 0,
|
||||
login_days: 0,
|
||||
uid: '',
|
||||
uid: ''
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
@@ -62,8 +62,6 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
|
||||
if (PublishGroupBulletinResult.result != 0) {
|
||||
throw `设置群公告失败,错误信息:${PublishGroupBulletinResult.errMsg}`;
|
||||
}
|
||||
// 下面实现扬了
|
||||
//await WebApi.setGroupNotice(payload.group_id, payload.content) ;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -7,18 +7,18 @@ import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||
|
||||
interface Payload {
|
||||
file: string,
|
||||
groupCode: string
|
||||
group_id: number
|
||||
}
|
||||
|
||||
export default class SetGroupHeader extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.SetGroupHeader;
|
||||
export default class SetGroupPortrait extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.SetGroupPortrait;
|
||||
|
||||
// 用不着复杂检测
|
||||
protected async check(payload: Payload): Promise<BaseCheckResult> {
|
||||
if (!payload.file || typeof payload.file != 'string' || !payload.groupCode || typeof payload.groupCode != 'string') {
|
||||
if (!payload.file || typeof payload.file != 'string' || !payload.group_id || typeof payload.group_id != 'number') {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'file和groupCode字段不能为空或者类型错误',
|
||||
message: 'file和group_id字段不能为空或者类型错误',
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -34,7 +34,7 @@ export default class SetGroupHeader extends BaseAction<Payload, any> {
|
||||
}
|
||||
if (path) {
|
||||
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||
const ret = await NTQQGroupApi.setGroupAvatar(payload.groupCode, path);
|
||||
const ret = await NTQQGroupApi.setGroupAvatar(payload.group_id.toString(), path) as any;
|
||||
if (!isLocal) {
|
||||
fs.unlink(path, () => {
|
||||
});
|
||||
@@ -43,11 +43,11 @@ export default class SetGroupHeader extends BaseAction<Payload, any> {
|
||||
throw `头像${payload.file}设置失败,api无返回`;
|
||||
}
|
||||
// log(`头像设置返回:${JSON.stringify(ret)}`)
|
||||
// if (ret['result'] == 1004022) {
|
||||
// throw `头像${payload.file}设置失败,文件可能不是图片格式`;
|
||||
// } else if (ret['result'] != 0) {
|
||||
// throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||
// }
|
||||
if (ret['result'] == 1004022) {
|
||||
throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`;
|
||||
} else if (ret['result'] != 0) {
|
||||
throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
if (!isLocal) {
|
10
src/onebot/action/go-cqhttp/SetModelShow.ts
Normal file
10
src/onebot/action/go-cqhttp/SetModelShow.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
|
||||
export default class SetModelShow extends BaseAction<null, null> {
|
||||
actionName = ActionName.SetModelShow;
|
||||
|
||||
async _handle(payload: null): Promise<null> {
|
||||
return null;
|
||||
}
|
||||
}
|
34
src/onebot/action/go-cqhttp/SetQQProfile.ts
Normal file
34
src/onebot/action/go-cqhttp/SetQQProfile.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
nickname: { type: 'string' },
|
||||
personal_note: { type: 'string' },
|
||||
sex: { type: ['number', 'string'] },//传Sex值?建议传0
|
||||
},
|
||||
required: ['nickname'],
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class SetQQProfile extends BaseAction<Payload, any | null> {
|
||||
actionName = ActionName.SetQQProfile;
|
||||
PayloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
const self = this.CoreContext.selfInfo;
|
||||
const OldProfile = await NTQQUserApi.getUserDetailInfo(self.uid);
|
||||
const ret = await NTQQUserApi.modifySelfProfile({
|
||||
nick: payload.nickname,
|
||||
longNick: (payload?.personal_note ?? OldProfile?.longNick) || '',
|
||||
sex: parseInt(payload?.sex ? payload?.sex.toString() : OldProfile?.sex!.toString()),
|
||||
birthday: { birthday_year: OldProfile?.birthday_year!.toString(), birthday_month: OldProfile?.birthday_month!.toString(), birthday_day: OldProfile?.birthday_day!.toString() },
|
||||
location: undefined,
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { ChatType, SendFileElement } from '@/core/entities';
|
||||
import { ChatType } from '@/core/entities';
|
||||
import fs from 'fs';
|
||||
import { sendMsg } from '@/onebot/action/msg/SendMsg';
|
||||
import { uri2local } from '@/common/utils/file';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
@@ -34,9 +33,9 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
|
||||
if (!downloadResult.success) {
|
||||
throw new Error(downloadResult.errMsg);
|
||||
}
|
||||
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name, payload.folder_id);
|
||||
const sendFileEle = await this.CoreContext.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name, payload.folder_id);
|
||||
await sendMsg(this.CoreContext, {
|
||||
chatType: ChatType.group,
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: payload.group_id.toString(),
|
||||
}, [sendFileEle], [], true);
|
||||
return null;
|
||||
|
@@ -5,7 +5,6 @@ import fs from 'fs';
|
||||
import { sendMsg } from '@/onebot/action/msg/SendMsg';
|
||||
import { uri2local } from '@/common/utils/file';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
@@ -32,9 +31,9 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
|
||||
throw `私聊${payload.user_id}不存在`;
|
||||
}
|
||||
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
|
||||
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid };
|
||||
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
|
||||
}
|
||||
throw '缺少参数 user_id';
|
||||
throw new Error( '缺少参数 user_id');
|
||||
}
|
||||
|
||||
async _handle(payload: Payload): Promise<null> {
|
||||
@@ -47,7 +46,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
|
||||
if (!downloadResult.success) {
|
||||
throw new Error(downloadResult.errMsg);
|
||||
}
|
||||
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name);
|
||||
const sendFileEle: SendFileElement = await this.CoreContext.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name);
|
||||
await sendMsg(this.CoreContext, peer, [sendFileEle], [], true);
|
||||
return null;
|
||||
}
|
@@ -20,9 +20,7 @@ export default class DelEssenceMsg extends BaseAction<Payload, any> {
|
||||
async _handle(payload: Payload): Promise<any> {
|
||||
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
|
||||
const msg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||
if (!msg) {
|
||||
throw new Error('msg not found');
|
||||
}
|
||||
if (!msg) throw new Error('msg not found');
|
||||
return await NTQQGroupApi.removeGroupEssence(
|
||||
msg.Peer.peerUid,
|
||||
msg.MsgId,
|
||||
|
28
src/onebot/action/group/DelGroupNotice.ts
Normal file
28
src/onebot/action/group/DelGroupNotice.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { WebApiGroupNoticeFeed } from '@/core';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
group_id: { type: ['number', 'string'] },
|
||||
feed_id: { type: 'string' },
|
||||
},
|
||||
required: ['group_id','feed_id'],
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class DelGroupNotice extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.DelGroupNotice;
|
||||
PayloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
|
||||
const group = payload.group_id.toString();
|
||||
const feedId = payload.feed_id;
|
||||
return await NTQQGroupApi.deleteGroupBulletin(group, feedId);
|
||||
}
|
||||
}
|
@@ -9,7 +9,7 @@ const SchemaData = {
|
||||
group_id: { type: ['number', 'string'] },
|
||||
pages: { type: 'number' },
|
||||
},
|
||||
required: ['group_id', 'pages'],
|
||||
required: ['group_id'],
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
@@ -20,7 +20,7 @@ export class GetGroupEssence extends BaseAction<Payload, GroupEssenceMsgRet> {
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQWebApi = this.CoreContext.apis.WebApi;
|
||||
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString(), payload.pages.toString());
|
||||
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString(), (payload.pages || "0").toString());
|
||||
if (!ret) {
|
||||
throw new Error('获取失败');
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { OB11Group } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
@@ -21,11 +21,8 @@ class GetGroupInfo extends BaseAction<Payload, OB11Group> {
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
|
||||
const group = (await NTQQGroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString());
|
||||
if (group) {
|
||||
return OB11Constructor.group(group);
|
||||
} else {
|
||||
throw `群${payload.group_id}不存在`;
|
||||
}
|
||||
if (!group) throw `群${payload.group_id}不存在`;
|
||||
return OB11Constructor.group(group);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { OB11Group } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { Group } from '@/core/entities';
|
||||
@@ -20,7 +20,7 @@ class GetGroupList extends BaseAction<Payload, OB11Group[]> {
|
||||
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
|
||||
const groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
|
||||
const groupList: Group[] = await NTQQGroupApi.getGroups(typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache);
|
||||
return OB11Constructor.groups(groupList);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { OB11GroupMember } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { GroupMember } from '@/core';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
@@ -23,56 +24,25 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
|
||||
const NTQQWebApi = this.CoreContext.apis.WebApi;
|
||||
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||
const isNocache = typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache;
|
||||
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
|
||||
if (!uid) {
|
||||
throw (`Uin2Uid Error ${payload.user_id}不存在`);
|
||||
}
|
||||
const member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
|
||||
if (!member) {
|
||||
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||
}
|
||||
try {
|
||||
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
||||
this.CoreContext.context.logger.logDebug('群成员详细信息结果', info);
|
||||
Object.assign(member, info);
|
||||
} catch (e) {
|
||||
this.CoreContext.context.logger.logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||
if (!uid) throw new Error (`Uin2Uid Error ${payload.user_id}不存在`);
|
||||
const [member, info] = await Promise.allSettled([
|
||||
NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache),
|
||||
NTQQUserApi.getUserDetailInfo(uid),
|
||||
]);
|
||||
if (member.status !== 'fulfilled') throw new Error (`群(${payload.group_id})成员${payload.user_id}不存在 ${member.reason}`);
|
||||
if (info.status === 'fulfilled') {
|
||||
this.CoreContext.context.logger.logDebug("群成员详细信息结果", info.value);
|
||||
Object.assign(member, info.value);
|
||||
} else {
|
||||
this.CoreContext.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息 ${info.reason}`);
|
||||
}
|
||||
const date = Math.round(Date.now() / 1000);
|
||||
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||
if (!this.CoreContext.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||
const SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), this.CoreContext.selfInfo.uid, isNocache);
|
||||
let isPrivilege = false;
|
||||
if (SelfInfoInGroup) {
|
||||
isPrivilege = SelfInfoInGroup.role === 3 || SelfInfoInGroup.role === 4;
|
||||
}
|
||||
if (isPrivilege) {
|
||||
const webGroupMembers = await NTQQWebApi.getGroupMembers(payload.group_id.toString());
|
||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||
retMember.qage = webGroupMembers[i]?.qage;
|
||||
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const LastestMsgList = await NTQQGroupApi.getLatestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||
const last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||
retMember.last_sent_time = parseInt(last_send_time);
|
||||
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Mlikiowa V2.0.4 Refactor Todo
|
||||
// retMember.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString());
|
||||
// retMember.join_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString());
|
||||
}
|
||||
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member.value as GroupMember);
|
||||
const Member = await this.CoreContext.apis.GroupApi.getGroupMember(payload.group_id.toString(), retMember.user_id);
|
||||
retMember.last_sent_time = parseInt(Member?.lastSpeakTime || date.toString());
|
||||
retMember.join_time = parseInt(Member?.joinTime || date.toString());
|
||||
return retMember;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { OB11GroupMember } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Constructor } from '@/onebot/helper/converter';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
@@ -22,23 +22,16 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
||||
async _handle(payload: Payload) {
|
||||
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
|
||||
const NTQQWebApi = this.CoreContext.apis.WebApi;
|
||||
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||
// const GroupList = await NTQQGroupApi.getGroups(isNocache);
|
||||
// const group = GroupList.find(item => item.groupCode == payload.group_id);
|
||||
// if (!group) {
|
||||
// throw (`群${payload.group_id}不存在`);
|
||||
// }
|
||||
const groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
|
||||
const groupMembersArr = Array.from(groupMembers.values());
|
||||
const groupMembersUids = groupMembersArr.map(e => e.uid);
|
||||
|
||||
let _groupMembers = groupMembersArr.map(item => {
|
||||
return OB11Constructor.groupMember(payload.group_id.toString(), item);
|
||||
});
|
||||
|
||||
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
|
||||
// 转为Map 方便索引
|
||||
const GroupMemberUids: string[] = [];
|
||||
const date = Math.round(Date.now() / 1000);
|
||||
|
||||
for (let i = 0, len = _groupMembers.length; i < len; i++) {
|
||||
// 保证基础数据有这个 同时避免群管插件过于依赖这个杀了
|
||||
_groupMembers[i].join_time = date;
|
||||
@@ -46,59 +39,31 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
||||
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
||||
}
|
||||
|
||||
if (!this.CoreContext.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||
const selfRole = groupMembers.get(this.CoreContext.selfInfo.uid)?.role;
|
||||
const isPrivilege = selfRole === 3 || selfRole === 4;
|
||||
const selfRole = groupMembers.get(this.CoreContext.selfInfo.uid)?.role;
|
||||
const isPrivilege = selfRole === 3 || selfRole === 4;
|
||||
|
||||
if (isPrivilege) {
|
||||
const webGroupMembers = await NTQQWebApi.getGroupMembers(payload.group_id.toString());
|
||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||
if (!webGroupMembers[i]?.uin) {
|
||||
continue;
|
||||
}
|
||||
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
|
||||
if (MemberData) {
|
||||
MemberData.join_time = webGroupMembers[i]?.join_time;
|
||||
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||
MemberData.qage = webGroupMembers[i]?.qage;
|
||||
MemberData.level = webGroupMembers[i]?.lv.level.toString();
|
||||
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
||||
}
|
||||
_groupMembers.forEach(item => {
|
||||
item.last_sent_time = date;
|
||||
item.join_time = date;
|
||||
});
|
||||
|
||||
if (isPrivilege) {
|
||||
const webGroupMembers = await NTQQWebApi.getGroupMembers(payload.group_id.toString());
|
||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||
if (!webGroupMembers[i]?.uin) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (isNocache) {
|
||||
const DateMap = await NTQQGroupApi.getGroupMemberLatestSendTimeCache(payload.group_id.toString(), groupMembersUids);//开始从本地拉取
|
||||
for (const DateUin of DateMap.keys()) {
|
||||
const MemberData = MemberMap.get(parseInt(DateUin));
|
||||
if (MemberData) {
|
||||
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);
|
||||
//join_time 有基础数据兜底
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_groupMembers.forEach(item => {
|
||||
item.last_sent_time = date;
|
||||
item.join_time = date;
|
||||
});
|
||||
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
|
||||
if (MemberData) {
|
||||
MemberData.join_time = webGroupMembers[i]?.join_time;
|
||||
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||
MemberData.qage = webGroupMembers[i]?.qage;
|
||||
MemberData.level = webGroupMembers[i]?.lv.level.toString();
|
||||
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Mlikiowa V2.0.4 Refactor Todo
|
||||
// _groupMembers.forEach(async item => {
|
||||
// item.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.lastSpeakTime || date.toString());
|
||||
// item.join_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.joinTime || date.toString());
|
||||
// });
|
||||
}
|
||||
// 还原索引到Array 一同返回
|
||||
|
||||
// let retData: any[] = [];
|
||||
// for (let retMem of MemberMap.values()) {
|
||||
// retMem.level = TypeConvert.toString(retMem.level) as any;
|
||||
// retData.push(retMem)
|
||||
// }
|
||||
|
||||
// _groupMembers = Array.from(retData);
|
||||
|
||||
|
||||
_groupMembers = Array.from(MemberMap.values());
|
||||
return _groupMembers;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
interface GroupNotice {
|
||||
sender_id: number;
|
||||
publish_time: number;
|
||||
feed_id: string;
|
||||
message: {
|
||||
text: string
|
||||
image: Array<{
|
||||
@@ -44,7 +45,8 @@ export class GetGroupNotice extends BaseAction<Payload, GroupNotice[]> {
|
||||
for (const key in ret.feeds) {
|
||||
const retApiNotice: WebApiGroupNoticeFeed = ret.feeds[key];
|
||||
const retNotice: GroupNotice = {
|
||||
// ...ret.feeds[key],
|
||||
//...ret.feeds[key],
|
||||
feed_id: retApiNotice.fid,
|
||||
sender_id: retApiNotice.u,
|
||||
publish_time: retApiNotice.pubt,
|
||||
message: {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { GroupNotifyMsgStatus } from '@/core';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
@@ -24,22 +25,22 @@ export class GetGroupSystemMsg extends BaseAction<void, any> {
|
||||
if (SSNotify.type == 1) {
|
||||
retData.InvitedRequest.push({
|
||||
request_id: SSNotify.seq,
|
||||
invitor_uin: await NTQQUserApi.getUinByUid(SSNotify.user1?.uid),
|
||||
invitor_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
|
||||
invitor_nick: SSNotify.user1?.nickName,
|
||||
group_id: SSNotify.group?.groupCode,
|
||||
group_name: SSNotify.group?.groupName,
|
||||
checked: SSNotify.status === 1 ? false : true,
|
||||
actor: await NTQQUserApi.getUinByUid(SSNotify.user2?.uid) || 0,
|
||||
checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true,
|
||||
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
|
||||
});
|
||||
} else if (SSNotify.type == 7) {
|
||||
retData.join_requests.push({
|
||||
request_id: SSNotify.seq,
|
||||
requester_uin: await NTQQUserApi.getUinByUid(SSNotify.user1?.uid),
|
||||
requester_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
|
||||
requester_nick: SSNotify.user1?.nickName,
|
||||
group_id: SSNotify.group?.groupCode,
|
||||
group_name: SSNotify.group?.groupName,
|
||||
checked: SSNotify.status === 1 ? false : true,
|
||||
actor: await NTQQUserApi.getUinByUid(SSNotify.user2?.uid) || 0,
|
||||
checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true,
|
||||
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ export default class SetGroupAddRequest extends BaseAction<Payload, null> {
|
||||
const approve = payload.approve?.toString() !== 'false';
|
||||
await NTQQGroupApi.handleGroupRequest(flag,
|
||||
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
||||
payload.reason || ' ',
|
||||
payload.reason ?? ' ',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ const SchemaData = {
|
||||
properties: {
|
||||
group_id: { type: ['number', 'string'] },
|
||||
user_id: { type: ['number', 'string'] },
|
||||
enable: { type: 'boolean' },
|
||||
enable: { type: ['boolean', 'string'] },
|
||||
},
|
||||
required: ['group_id', 'user_id'],
|
||||
} as const satisfies JSONSchema;
|
||||
@@ -20,11 +20,12 @@ export default class SetGroupAdmin extends BaseAction<Payload, null> {
|
||||
PayloadSchema = SchemaData;
|
||||
|
||||
async _handle(payload: Payload): Promise<null> {
|
||||
const enable = typeof payload.enable === 'string' ? payload.enable === 'true' : !!payload.enable;
|
||||
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
|
||||
const NTQQUserApi = this.CoreContext.apis.UserApi;
|
||||
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
|
||||
if (!uid) throw new Error('get Uid Error');
|
||||
await NTQQGroupApi.setMemberRole(payload.group_id.toString(), uid, payload.enable ? GroupMemberRole.admin : GroupMemberRole.normal);
|
||||
await NTQQGroupApi.setMemberRole(payload.group_id.toString(), uid, enable ? GroupMemberRole.admin : GroupMemberRole.normal);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
group_id: { type: ['number', 'string'] },
|
||||
is_dismiss: { type: 'boolean' },
|
||||
is_dismiss: { type: ['boolean', 'string'] },
|
||||
},
|
||||
required: ['group_id'],
|
||||
} as const satisfies JSONSchema;
|
||||
|
10
src/onebot/action/guild/GetGuildProfile.ts
Normal file
10
src/onebot/action/guild/GetGuildProfile.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
|
||||
export default class GetGuildProfile extends BaseAction<null, null> {
|
||||
actionName = ActionName.GetGuildProfile;
|
||||
|
||||
async _handle(payload: null): Promise<null> {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -23,7 +23,7 @@ import GoCQHTTPGetStrangerInfo from './go-cqhttp/GetStrangerInfo';
|
||||
import SendLike from './user/SendLike';
|
||||
import SetGroupAddRequest from './group/SetGroupAddRequest';
|
||||
import SetGroupLeave from './group/SetGroupLeave';
|
||||
import GetGuildList from './group/GetGuildList';
|
||||
import GetGuildList from './guild/GetGuildList';
|
||||
import SetFriendAddRequest from './user/SetFriendAddRequest';
|
||||
import SetGroupWholeBan from './group/SetGroupWholeBan';
|
||||
import SetGroupName from './group/SetGroupName';
|
||||
@@ -61,7 +61,7 @@ import { TranslateEnWordToZn } from './extends/TranslateEnWordToZn';
|
||||
import { SetGroupFileFolder } from './file/SetGroupFileFolder';
|
||||
import { DelGroupFile } from './file/DelGroupFile';
|
||||
import { DelGroupFileFolder } from './file/DelGroupFileFolder';
|
||||
import { SetSelfProfile } from './extends/SetSelfProfile';
|
||||
import { SetQQProfile } from './go-cqhttp/SetQQProfile';
|
||||
import { ShareGroupEx, SharePeer } from './extends/ShareContact';
|
||||
import { CreateCollection } from './extends/CreateCollection';
|
||||
import { SetLongNick } from './extends/SetLongNick';
|
||||
@@ -69,13 +69,18 @@ import DelEssenceMsg from './group/DelEssenceMsg';
|
||||
import SetEssenceMsg from './group/SetEssenceMsg';
|
||||
import GetRecentContact from './user/GetRecentContact';
|
||||
import { GetProfileLike } from './extends/GetProfileLike';
|
||||
import SetGroupHeader from './extends/SetGroupHeader';
|
||||
import SetGroupPortrait from './go-cqhttp/SetGroupPortrait';
|
||||
import { FetchCustomFace } from './extends/FetchCustomFace';
|
||||
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile';
|
||||
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivateFile';
|
||||
import { FetchEmojiLike } from './extends/FetchEmojiLike';
|
||||
import { NapCatCore } from '@/core';
|
||||
|
||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||
import GetGuildProfile from './guild/GetGuildProfile';
|
||||
import SetModelShow from './go-cqhttp/SetModelShow';
|
||||
import { SetInputStatus } from './extends/SetInputStatus';
|
||||
import { GetCSRF } from './system/GetCSRF';
|
||||
import { DelGroupNotice } from './group/DelGroupNotice';
|
||||
|
||||
export type ActionMap = Map<string, BaseAction<any, any>>;
|
||||
|
||||
@@ -83,7 +88,7 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
|
||||
const actionHandlers = [
|
||||
new FetchEmojiLike(onebotContext, coreContext),
|
||||
new GetFile(onebotContext, coreContext),
|
||||
new SetSelfProfile(onebotContext, coreContext),
|
||||
new SetQQProfile(onebotContext, coreContext),
|
||||
new ShareGroupEx(onebotContext, coreContext),
|
||||
new SharePeer(onebotContext, coreContext),
|
||||
new CreateCollection(onebotContext, coreContext),
|
||||
@@ -158,9 +163,14 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
|
||||
new GetRecentContact(onebotContext, coreContext),
|
||||
new MarkAllMsgAsRead(onebotContext, coreContext),
|
||||
new GetProfileLike(onebotContext, coreContext),
|
||||
new SetGroupHeader(onebotContext, coreContext),
|
||||
new SetGroupPortrait(onebotContext, coreContext),
|
||||
new FetchCustomFace(onebotContext, coreContext),
|
||||
new GoCQHTTPUploadPrivateFile(onebotContext, coreContext),
|
||||
new GetGuildProfile(onebotContext, coreContext),
|
||||
new SetModelShow(onebotContext, coreContext),
|
||||
new SetInputStatus(onebotContext, coreContext),
|
||||
new GetCSRF(onebotContext, coreContext),
|
||||
new DelGroupNotice(onebotContext, coreContext),
|
||||
];
|
||||
const actionMap = new Map();
|
||||
for (const action of actionHandlers) {
|
||||
|
@@ -7,7 +7,7 @@ import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
message_id: { type: 'number' },
|
||||
message_id: { type: ['number', 'string'] },
|
||||
group_id: { type: ['number', 'string'] },
|
||||
user_id: { type: ['number', 'string'] },
|
||||
},
|
||||
@@ -24,14 +24,14 @@ class ForwardSingleMsg extends BaseAction<Payload, null> {
|
||||
if (!peerUid) {
|
||||
throw new Error(`无法找到私聊对象${payload.user_id}`);
|
||||
}
|
||||
return { chatType: ChatType.friend, peerUid };
|
||||
return { chatType: ChatType.KCHATTYPEC2C, peerUid };
|
||||
}
|
||||
return { chatType: ChatType.group, peerUid: payload.group_id!.toString() };
|
||||
return { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id!.toString() };
|
||||
}
|
||||
|
||||
async _handle(payload: Payload): Promise<null> {
|
||||
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
|
||||
const msg = MessageUnique.getMsgIdAndPeerByShortId(payload.message_id);
|
||||
const msg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||
if (!msg) {
|
||||
throw new Error(`无法找到消息${payload.message_id}`);
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { OB11Message } from '../../types';
|
||||
import { OB11Constructor } from '../../helper/data';
|
||||
import { OB11Message } from '@/onebot';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
@@ -28,21 +27,23 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
|
||||
if (!payload.message_id) {
|
||||
throw Error('参数message_id不能为空');
|
||||
}
|
||||
const MsgShortId = await MessageUnique.getShortIdByMsgId(payload.message_id.toString());
|
||||
const msgIdWithPeer = await MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString()));
|
||||
const MsgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString());
|
||||
const msgIdWithPeer = MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString()));
|
||||
if (!msgIdWithPeer) {
|
||||
throw ('消息不存在');
|
||||
throw new Error('消息不存在');
|
||||
}
|
||||
const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType };
|
||||
const msg = await NTQQMsgApi.getMsgsByMsgId(
|
||||
peer,
|
||||
[msgIdWithPeer?.MsgId || payload.message_id.toString()]);
|
||||
const retMsg = await OB11Constructor.message(this.CoreContext, msg.msgList[0], 'array');
|
||||
const retMsg = await this.OneBotContext.apiContext.MsgApi.parseMessage(msg.msgList[0], 'array');
|
||||
if (!retMsg) throw Error('消息为空');
|
||||
try {
|
||||
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!;
|
||||
retMsg.message_seq = retMsg.message_id;
|
||||
retMsg.real_id = retMsg.message_id;
|
||||
} catch (e) {
|
||||
// ignored
|
||||
}
|
||||
return retMsg;
|
||||
}
|
||||
|
@@ -23,12 +23,12 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
|
||||
throw `私聊${payload.user_id}不存在`;
|
||||
}
|
||||
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
|
||||
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid };
|
||||
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
|
||||
}
|
||||
if (!payload.group_id) {
|
||||
throw '缺少参数 group_id 或 user_id';
|
||||
throw new Error( '缺少参数 group_id 或 user_id');
|
||||
}
|
||||
return { chatType: ChatType.group, peerUid: payload.group_id.toString() };
|
||||
return { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
|
||||
}
|
||||
|
||||
async _handle(payload: PlayloadType): Promise<null> {
|
||||
@@ -36,7 +36,7 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
|
||||
// 调用API
|
||||
const ret = await NTQQMsgApi.setMsgRead(await this.getPeer(payload));
|
||||
if (ret.result != 0) {
|
||||
throw ('设置已读失败,' + ret.errMsg);
|
||||
throw new Error('设置已读失败,' + ret.errMsg);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
321
src/onebot/action/msg/SendMsg.ts
Normal file
321
src/onebot/action/msg/SendMsg.ts
Normal file
@@ -0,0 +1,321 @@
|
||||
import {
|
||||
OB11MessageData,
|
||||
OB11MessageDataType,
|
||||
OB11MessageMixType,
|
||||
OB11MessageNode,
|
||||
OB11PostSendMsg,
|
||||
} from '@/onebot/types';
|
||||
import { ActionName, BaseCheckResult } from '@/onebot/action/types';
|
||||
import fs from 'node:fs';
|
||||
import fsPromise from 'node:fs/promises';
|
||||
import { decodeCQCode } from '@/onebot/helper/cqcode';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendMessageElement } from '@/core';
|
||||
import BaseAction from '../BaseAction';
|
||||
|
||||
export interface ReturnDataType {
|
||||
message_id: number;
|
||||
}
|
||||
|
||||
export enum ContextMode {
|
||||
Normal = 0,
|
||||
Private = 1,
|
||||
Group = 2
|
||||
}
|
||||
|
||||
// Normalizes a mixed type (CQCode/a single segment/segment array) into a segment array.
|
||||
export function normalize(message: OB11MessageMixType, autoEscape = false): OB11MessageData[] {
|
||||
return typeof message === 'string' ? (
|
||||
autoEscape ?
|
||||
[{ type: OB11MessageDataType.text, data: { text: message } }] :
|
||||
decodeCQCode(message)
|
||||
) : Array.isArray(message) ? message : [message];
|
||||
}
|
||||
|
||||
export async function sendMsg(coreContext: NapCatCore, peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[], waitComplete = true) {
|
||||
const NTQQMsgApi = coreContext.apis.MsgApi;
|
||||
const logger = coreContext.context.logger;
|
||||
if (!sendElements.length) {
|
||||
throw new Error('消息体无法解析, 请检查是否发送了不支持的消息类型');
|
||||
}
|
||||
let totalSize = 0;
|
||||
let timeout = 10000;
|
||||
try {
|
||||
for (const fileElement of sendElements) {
|
||||
if (fileElement.elementType === ElementType.PTT) {
|
||||
totalSize += fs.statSync(fileElement.pttElement.filePath).size;
|
||||
}
|
||||
if (fileElement.elementType === ElementType.FILE) {
|
||||
totalSize += fs.statSync(fileElement.fileElement.filePath).size;
|
||||
}
|
||||
if (fileElement.elementType === ElementType.VIDEO) {
|
||||
totalSize += fs.statSync(fileElement.videoElement.filePath).size;
|
||||
}
|
||||
if (fileElement.elementType === ElementType.PIC) {
|
||||
totalSize += fs.statSync(fileElement.picElement.sourcePath).size;
|
||||
}
|
||||
}
|
||||
//且 PredictTime ((totalSize / 1024 / 512) * 1000)不等于Nan
|
||||
const PredictTime = totalSize / 1024 / 256 * 1000;
|
||||
if (!Number.isNaN(PredictTime)) {
|
||||
timeout += PredictTime;// 10S Basic Timeout + PredictTime( For File 512kb/s )
|
||||
}
|
||||
} catch (e) {
|
||||
logger.logError('发送消息计算预计时间异常', e);
|
||||
}
|
||||
const returnMsg = await NTQQMsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
|
||||
try {
|
||||
returnMsg!.id = MessageUnique.createMsg({
|
||||
chatType: peer.chatType,
|
||||
guildId: '',
|
||||
peerUid: peer.peerUid,
|
||||
}, returnMsg!.msgId);
|
||||
} catch (e: any) {
|
||||
logger.logDebug('发送消息id获取失败', e);
|
||||
returnMsg!.id = 0;
|
||||
}
|
||||
deleteAfterSentFiles.map((f) => {
|
||||
fsPromise.unlink(f).then().catch(e => logger.logError('发送消息删除文件失败', e));
|
||||
});
|
||||
return returnMsg;
|
||||
}
|
||||
|
||||
async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg, contextMode: ContextMode): Promise<Peer> {
|
||||
// This function determines the type of message by the existence of user_id / group_id,
|
||||
// not message_type.
|
||||
// This redundant design of Ob11 here should be blamed.
|
||||
const NTQQFriendApi = coreContext.apis.FriendApi;
|
||||
const NTQQUserApi = coreContext.apis.UserApi;
|
||||
const NTQQMsgApi = coreContext.apis.MsgApi;
|
||||
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
|
||||
return {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: payload.group_id.toString(),
|
||||
};
|
||||
}
|
||||
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
|
||||
const Uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
|
||||
if (!Uid) throw new Error('无法获取用户信息');
|
||||
const isBuddy = await NTQQFriendApi.isBuddy(Uid);
|
||||
if (!isBuddy) {
|
||||
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, Uid);
|
||||
if (ret.tmpChatInfo?.groupCode) {
|
||||
return {
|
||||
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
|
||||
peerUid: Uid,
|
||||
guildId: '',
|
||||
};
|
||||
}
|
||||
if (payload.group_id) {
|
||||
return {
|
||||
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
|
||||
peerUid: Uid,
|
||||
guildId: payload.group_id.toString(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: Uid!,
|
||||
guildId: '',
|
||||
};
|
||||
}
|
||||
return {
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: Uid!,
|
||||
guildId: '',
|
||||
};
|
||||
}
|
||||
throw new Error('请指定 group_id 或 user_id');
|
||||
}
|
||||
|
||||
function getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType): number {
|
||||
if (Array.isArray(payload.message)) {
|
||||
return payload.message.filter(msg => msg.type == msgType).length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
actionName = ActionName.SendMsg;
|
||||
contextMode = ContextMode.Normal;
|
||||
|
||||
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
|
||||
const messages = normalize(payload.message);
|
||||
const nodeElementLength = getSpecialMsgNum(payload, OB11MessageDataType.node);
|
||||
if (nodeElementLength > 0 && nodeElementLength != messages.length) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素',
|
||||
};
|
||||
}
|
||||
if (payload.user_id && payload.message_type !== 'group') {
|
||||
// const uid = await this.CoreContext.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||
// const isBuddy = await NTQQFriendApi.isBuddy(uid!);
|
||||
// if (!isBuddy) { }
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
||||
if (payload.message_type === 'group') this.contextMode = ContextMode.Group;
|
||||
if (payload.message_type === 'private') this.contextMode = ContextMode.Private;
|
||||
const peer = await createContext(this.CoreContext, payload, this.contextMode);
|
||||
|
||||
const messages = normalize(
|
||||
payload.message,
|
||||
typeof payload.auto_escape === 'string' ? payload.auto_escape === 'true' : !!payload.auto_escape
|
||||
);
|
||||
|
||||
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
||||
const returnMsg = await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
|
||||
if (returnMsg) {
|
||||
const msgShortId = MessageUnique.createMsg({
|
||||
guildId: '',
|
||||
peerUid: peer.peerUid,
|
||||
chatType: peer.chatType,
|
||||
}, returnMsg!.msgId);
|
||||
return { message_id: msgShortId! };
|
||||
} else {
|
||||
throw Error('发送转发消息失败');
|
||||
}
|
||||
} else {
|
||||
// if (getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
||||
// const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic;
|
||||
// if (music) {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// log("send msg:", peer, sendElements)
|
||||
|
||||
const { sendElements, deleteAfterSentFiles } = await this.OneBotContext.apiContext.MsgApi
|
||||
.createSendElements(messages, peer);
|
||||
const returnMsg = await sendMsg(this.CoreContext, peer, sendElements, deleteAfterSentFiles);
|
||||
return { message_id: returnMsg!.id! };
|
||||
}
|
||||
|
||||
private async handleForwardedNodes(destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> {
|
||||
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
|
||||
const selfPeer = {
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: this.CoreContext.selfInfo.uid,
|
||||
};
|
||||
let nodeMsgIds: string[] = [];
|
||||
const logger = this.CoreContext.context.logger;
|
||||
for (const messageNode of messageNodes) {
|
||||
const nodeId = messageNode.data.id;
|
||||
if (nodeId) {
|
||||
//对Mgsid和OB11ID混用情况兜底
|
||||
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
|
||||
if (!nodeMsg) {
|
||||
logger.logError('转发消息失败,未找到消息', nodeId);
|
||||
continue;
|
||||
}
|
||||
nodeMsgIds.push(nodeMsg.MsgId);
|
||||
} else {
|
||||
// 自定义的消息
|
||||
try {
|
||||
const OB11Data = normalize(messageNode.data.content);
|
||||
//筛选node消息
|
||||
const isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息
|
||||
if (isNodeMsg !== 0) {
|
||||
if (isNodeMsg !== OB11Data.length) {
|
||||
logger.logError('子消息中包含非node消息 跳过不合法部分');
|
||||
continue;
|
||||
}
|
||||
const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
|
||||
if (nodeMsg) {
|
||||
nodeMsgIds.push(nodeMsg.msgId);
|
||||
MessageUnique.createMsg(selfPeer, nodeMsg.msgId);
|
||||
}
|
||||
//完成子卡片生成跳过后续
|
||||
continue;
|
||||
}
|
||||
const { sendElements } = await this.OneBotContext.apiContext.MsgApi
|
||||
.createSendElements(OB11Data, destPeer);
|
||||
//拆分消息
|
||||
const MixElement = sendElements.filter(element => element.elementType !== ElementType.FILE && element.elementType !== ElementType.VIDEO);
|
||||
const SingleElement = sendElements.filter(element => element.elementType === ElementType.FILE || element.elementType === ElementType.VIDEO).map(e => [e]);
|
||||
const AllElement: SendMessageElement[][] = [MixElement, ...SingleElement].filter(e => e !== undefined && e.length !== 0);
|
||||
const MsgNodeList: Promise<RawMessage | undefined>[] = [];
|
||||
for (const sendElementsSplitElement of AllElement) {
|
||||
MsgNodeList.push(sendMsg(this.CoreContext, selfPeer, sendElementsSplitElement, [], true).catch(_ => undefined));
|
||||
}
|
||||
(await Promise.allSettled(MsgNodeList)).map((result) => {
|
||||
if (result.status === 'fulfilled' && result.value) {
|
||||
nodeMsgIds.push(result.value.msgId);
|
||||
MessageUnique.createMsg(selfPeer, result.value.msgId);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
logger.logDebug('生成转发消息节点失败', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
const nodeMsgArray: Array<RawMessage> = [];
|
||||
let srcPeer: Peer | undefined = undefined;
|
||||
let needSendSelf = false;
|
||||
//检测是否处于同一个Peer 不在同一个peer则全部消息由自身发送
|
||||
for (const msgId of nodeMsgIds) {
|
||||
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId);
|
||||
if (!nodeMsgPeer) {
|
||||
logger.logError('转发消息失败,未找到消息', msgId);
|
||||
continue;
|
||||
}
|
||||
const nodeMsg = (await NTQQMsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
|
||||
srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
|
||||
if (srcPeer.peerUid !== nodeMsg.peerUid) {
|
||||
needSendSelf = true;
|
||||
}
|
||||
nodeMsgArray.push(nodeMsg);
|
||||
}
|
||||
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
|
||||
let retMsgIds: string[] = [];
|
||||
if (needSendSelf) {
|
||||
for (const [, msg] of nodeMsgArray.entries()) {
|
||||
if (msg.peerUid === this.CoreContext.selfInfo.uid) {
|
||||
retMsgIds.push(msg.msgId);
|
||||
continue;
|
||||
}
|
||||
const ClonedMsg = await this.cloneMsg(msg);
|
||||
if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId);
|
||||
}
|
||||
} else {
|
||||
retMsgIds = nodeMsgIds;
|
||||
}
|
||||
if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
|
||||
try {
|
||||
logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds);
|
||||
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds);
|
||||
} catch (e) {
|
||||
logger.logError('forward failed', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
|
||||
const selfPeer = {
|
||||
chatType: ChatType.KCHATTYPEC2C,
|
||||
peerUid: this.CoreContext.selfInfo.uid,
|
||||
};
|
||||
const logger = this.CoreContext.context.logger;
|
||||
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
|
||||
//msg 为待克隆消息
|
||||
|
||||
const sendElements: SendMessageElement[] = [];
|
||||
|
||||
for (const element of msg.elements) {
|
||||
sendElements.push(element as SendMessageElement);
|
||||
}
|
||||
|
||||
if (sendElements.length === 0) {
|
||||
logger.logDebug('需要clone的消息无法解析,将会忽略掉', msg);
|
||||
}
|
||||
try {
|
||||
return await NTQQMsgApi.sendMsg(selfPeer, sendElements, true);
|
||||
} catch (e) {
|
||||
logger.logError(e, '克隆转发消息失败,将忽略本条消息', msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SendMsg;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user