mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f43c1eadd7 | ||
![]() |
ae4b4e7ff9 | ||
![]() |
68b9771fa9 | ||
![]() |
114c98f4cf | ||
![]() |
685dc74742 | ||
![]() |
56966961dc | ||
![]() |
75b8002616 | ||
![]() |
6cd2d14e85 | ||
![]() |
d11ad0585b | ||
![]() |
ccbb641a8e | ||
![]() |
5695d10a86 | ||
![]() |
185f167c5f | ||
![]() |
9cf38a439b | ||
![]() |
de65cd810c |
@@ -5,7 +5,7 @@ root = true
|
|||||||
|
|
||||||
# Unix-style newlines with a newline ending every file
|
# Unix-style newlines with a newline ending every file
|
||||||
[*]
|
[*]
|
||||||
end_of_line = lf
|
end_of_line = lf|crlf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
# Matches multiple files with brace expansion notation
|
# Matches multiple files with brace expansion notation
|
||||||
@@ -15,10 +15,7 @@ charset = utf-8
|
|||||||
# 2 space indentation
|
# 2 space indentation
|
||||||
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
|
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
|
|
||||||
[*.bat]
|
|
||||||
charset = latin1
|
|
||||||
|
|
||||||
# Unfortunately, EditorConfig doesn't support space configuration inside import braces directly.
|
# Unfortunately, EditorConfig doesn't support space configuration inside import braces directly.
|
||||||
# You'll need to rely on your linter/formatter like ESLint or Prettier for that.
|
# You'll need to rely on your linter/formatter like ESLint or Prettier for that.
|
1
.env.development
Normal file
1
.env.development
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VITE_BUILD_TYPE = Development
|
@@ -1,2 +0,0 @@
|
|||||||
VITE_BUILD_TYPE = Production
|
|
||||||
VITE_BUILD_PLATFORM = Framework
|
|
@@ -1,2 +1 @@
|
|||||||
VITE_BUILD_TYPE = Production
|
VITE_BUILD_TYPE = Production
|
||||||
VITE_BUILD_PLATFORM = Shell
|
|
68
.eslintrc.cjs
Normal file
68
.eslintrc.cjs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
module.exports = {
|
||||||
|
'env': {
|
||||||
|
'browser': true,
|
||||||
|
'es2021': true,
|
||||||
|
'node': true
|
||||||
|
},
|
||||||
|
'ignorePatterns': ['src/core/', 'src/core.lib/','src/proto/'],
|
||||||
|
'extends': [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended'
|
||||||
|
],
|
||||||
|
'overrides': [
|
||||||
|
{
|
||||||
|
'env': {
|
||||||
|
'node': true
|
||||||
|
},
|
||||||
|
'files': [
|
||||||
|
'.eslintrc.{js,cjs}'
|
||||||
|
],
|
||||||
|
'parserOptions': {
|
||||||
|
'sourceType': 'script'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'parser': '@typescript-eslint/parser',
|
||||||
|
'parserOptions': {
|
||||||
|
'ecmaVersion': 'latest',
|
||||||
|
'sourceType': 'module'
|
||||||
|
},
|
||||||
|
'plugins': [
|
||||||
|
'@typescript-eslint',
|
||||||
|
'import'
|
||||||
|
],
|
||||||
|
'settings': {
|
||||||
|
'import/parsers': {
|
||||||
|
'@typescript-eslint/parser': ['.ts']
|
||||||
|
},
|
||||||
|
'import/resolver': {
|
||||||
|
'typescript': {
|
||||||
|
'alwaysTryTypes': true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'rules': {
|
||||||
|
'indent': [
|
||||||
|
'error',
|
||||||
|
2
|
||||||
|
],
|
||||||
|
'linebreak-style': [
|
||||||
|
'error',
|
||||||
|
'unix'
|
||||||
|
],
|
||||||
|
'quotes': [
|
||||||
|
'error',
|
||||||
|
'single'
|
||||||
|
],
|
||||||
|
'semi': [
|
||||||
|
'error',
|
||||||
|
'always'
|
||||||
|
],
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'no-async-promise-executor': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
}
|
||||||
|
};
|
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -10,7 +10,6 @@ body:
|
|||||||
在提交新的 Bug 反馈前,请确保您:
|
在提交新的 Bug 反馈前,请确保您:
|
||||||
* 已经搜索了现有的 issues,并且没有找到可以解决您问题的方法
|
* 已经搜索了现有的 issues,并且没有找到可以解决您问题的方法
|
||||||
* 不与现有的某一 issue 重复
|
* 不与现有的某一 issue 重复
|
||||||
* 不涉及[已经停止维护的特性](https://github.com/NapNeko/NapCatQQ?tab=readme-ov-file#挥别昨日),例如 CQ 码
|
|
||||||
- type: input
|
- type: input
|
||||||
id: system-version
|
id: system-version
|
||||||
attributes:
|
attributes:
|
||||||
|
48
.github/workflows/build.yml
vendored
48
.github/workflows/build.yml
vendored
@@ -1,15 +1,21 @@
|
|||||||
name: "Build Action"
|
name: "Build"
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build-LiteLoader:
|
build-linux:
|
||||||
|
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target_platform: [linux]
|
||||||
|
target_arch: [x64, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Main Repository
|
- name: Clone Main Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -22,24 +28,26 @@ jobs:
|
|||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
- name: Build NuCat Framework
|
- name: Build NuCat Linux
|
||||||
run: |
|
run: |
|
||||||
npm i
|
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
cd napcat.webui
|
npm run build:prod
|
||||||
npm i
|
|
||||||
cd ..
|
|
||||||
npm run build:framework
|
|
||||||
cd dist
|
cd dist
|
||||||
npm i --omit=dev
|
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
rm package-lock.json
|
|
||||||
cd ..
|
cd ..
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: NapCat.Framework
|
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||||
path: dist
|
path: dist
|
||||||
Build-Shell:
|
build-win32:
|
||||||
|
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target_platform: [win32]
|
||||||
|
target_arch: [x64,ia32]
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Main Repository
|
- name: Clone Main Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -52,19 +60,15 @@ jobs:
|
|||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
- name: Build NuCat LiteLoader
|
- name: Build NuCat Linux
|
||||||
run: |
|
run: |
|
||||||
npm i
|
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
cd napcat.webui
|
npm run build:prod
|
||||||
npm i
|
|
||||||
cd ..
|
|
||||||
npm run build:shell
|
|
||||||
cd dist
|
cd dist
|
||||||
npm i --omit=dev
|
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
rm package-lock.json
|
|
||||||
cd ..
|
cd ..
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: NapCat.Shell
|
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||||
path: dist
|
path: dist
|
||||||
|
88
.github/workflows/release.yml
vendored
88
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Build Release"
|
name: "release"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -30,9 +30,14 @@ jobs:
|
|||||||
ls
|
ls
|
||||||
node ./script/checkVersion.cjs
|
node ./script/checkVersion.cjs
|
||||||
sh ./checkVersion.sh
|
sh ./checkVersion.sh
|
||||||
Build-LiteLoader:
|
build-linux:
|
||||||
needs: [check-version]
|
needs: [check-version]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target_platform: [linux]
|
||||||
|
target_arch: [x64, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Main Repository
|
- name: Clone Main Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -46,24 +51,28 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
|
|
||||||
- name: Build NuCat Framework
|
- name: Build NuCat Linux
|
||||||
run: |
|
run: |
|
||||||
npm i
|
export NAPCAT_BUILDSYS=${{ matrix.target_platform }}
|
||||||
cd napcat.webui
|
export NAPCAT_BUILDARCH=${{ matrix.target_arch }}
|
||||||
npm i
|
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
cd ..
|
npm run build:prod
|
||||||
npm run build:framework
|
|
||||||
cd dist
|
cd dist
|
||||||
npm i --omit=dev
|
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
cd ..
|
cd ..
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: NapCat.Framework
|
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||||
path: dist
|
path: dist
|
||||||
Build-Shell:
|
build-win32:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [check-version]
|
needs: [check-version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target_platform: [win32]
|
||||||
|
target_arch: [x64,ia32]
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Main Repository
|
- name: Clone Main Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -78,61 +87,36 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
|
|
||||||
- name: Build NuCat Shell
|
- name: Build NuCat Linux
|
||||||
run: |
|
run: |
|
||||||
npm i
|
export NAPCAT_BUILDSYS=${{ matrix.target_platform }}
|
||||||
cd napcat.webui
|
export NAPCAT_BUILDARCH=${{ matrix.target_arch }}
|
||||||
npm i
|
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
cd ..
|
npm run build:prod
|
||||||
npm run build:shell
|
|
||||||
cd dist
|
cd dist
|
||||||
npm i --omit=dev
|
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: NapCat.Shell
|
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||||
path: dist
|
path: dist
|
||||||
|
|
||||||
release-napcat:
|
release-napcat:
|
||||||
needs: [Build-LiteLoader,Build-Shell]
|
needs: [build-win32,build-linux]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Clone Main Repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: 'NapNeko/NapCatQQ'
|
|
||||||
submodules: true
|
|
||||||
ref: main
|
|
||||||
token: ${{ secrets.NAPCAT_BUILD }}
|
|
||||||
|
|
||||||
- name: Download All Artifact
|
- name: Download All Artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
- name: Compress subdirectories
|
- name: Compress subdirectories
|
||||||
run: |
|
run: |
|
||||||
cd ./NapCat.Shell/
|
for dir in */; do
|
||||||
zip -q -r NapCat.Shell.zip *
|
base=$(basename "$dir")
|
||||||
cd ..
|
zip -r "${base}.zip" "$dir"
|
||||||
cd ./NapCat.Framework/
|
done
|
||||||
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 ./
|
|
||||||
|
|
||||||
mkdir ./NapCat.Framework.Windows.Once
|
|
||||||
unzip -q ./external/LiteLoaderWrapper.zip -d ./NapCat.Framework.Windows.Once
|
|
||||||
cd ./NapCat.Framework.Windows.Once
|
|
||||||
ls
|
|
||||||
mkdir -p ./LL/plugins/NapCatQQ
|
|
||||||
unzip -q ../NapCat.Framework.zip -d ./LL/plugins/NapCatQQ
|
|
||||||
zip -q -r NapCat.Framework.Windows.Once.zip *
|
|
||||||
cd ..
|
|
||||||
mv ./NapCat.Framework.Windows.Once/NapCat.Framework.Windows.Once.zip ./
|
|
||||||
- name: Extract version from tag
|
- name: Extract version from tag
|
||||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
|
||||||
@@ -146,7 +130,9 @@ jobs:
|
|||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
body_path: CHANGELOG.md
|
body_path: CHANGELOG.md
|
||||||
files: |
|
files: |
|
||||||
NapCat.Framework.zip
|
NapCat.win32.x64.zip
|
||||||
NapCat.Shell.zip
|
NapCat.linux.x64.zip
|
||||||
NapCat.Framework.Windows.Once.zip
|
NapCat.linux.arm64.zip
|
||||||
|
# NapCat.darwin.x64.zip
|
||||||
|
# NapCat.darwin.arm64.zip
|
||||||
draft: true
|
draft: true
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,4 +15,3 @@ dist/
|
|||||||
# Build
|
# Build
|
||||||
*.db
|
*.db
|
||||||
checkVersion.sh
|
checkVersion.sh
|
||||||
bun.lockb
|
|
||||||
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[submodule "src/core"]
|
||||||
|
path = src/core
|
||||||
|
url = https://github.com/NapNeko/core.git
|
||||||
|
branch = master
|
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"tabWidth": 4,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"arrowParens": "always",
|
|
||||||
"printWidth": 120,
|
|
||||||
"endOfLine": "auto"
|
|
||||||
}
|
|
356
LICENSE
356
LICENSE
@@ -1,343 +1,21 @@
|
|||||||
GNU GENERAL PUBLIC Without Social media promotion LICENSE
|
MIT License
|
||||||
Version 2, June 1991
|
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
Copyright (c) 2024 NapCatQQ
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
The above copyright notice and this permission notice shall be included in all
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
copies or substantial portions of the Software.
|
||||||
License is intended to guarantee your freedom to share and change free
|
|
||||||
software--to make sure the software is free for all its users. This
|
|
||||||
General Public License applies to most of the Free Software
|
|
||||||
Foundation's software and to any other program whose authors commit to
|
|
||||||
using it. (Some other Free Software Foundation software is covered by
|
|
||||||
the GNU Lesser General Public License instead.) You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
have the freedom to distribute copies of free software (and charge for
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
this service if you wish), that you receive source code or can get it
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
if you want it, that you can change the software or use pieces of it
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
in new free programs; and that you know you can do these things.
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.
|
|
||||||
These restrictions translate to certain responsibilities for you if you
|
|
||||||
distribute copies of the software, or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must give the recipients all the rights that
|
|
||||||
you have. You must make sure that they, too, receive or can get the
|
|
||||||
source code. And you must show them these terms so they know their
|
|
||||||
rights.
|
|
||||||
|
|
||||||
We protect your rights with two steps: (1) copyright the software, and
|
|
||||||
(2) offer you this license which gives you legal permission to copy,
|
|
||||||
distribute and/or modify the software.
|
|
||||||
|
|
||||||
Also, for each author's protection and ours, we want to make certain
|
|
||||||
that everyone understands that there is no warranty for this free
|
|
||||||
software. If the software is modified by someone else and passed on, we
|
|
||||||
want its recipients to know that what they have is not the original, so
|
|
||||||
that any problems introduced by others will not reflect on the original
|
|
||||||
authors' reputations.
|
|
||||||
|
|
||||||
Finally, any free program is threatened constantly by software
|
|
||||||
patents. We wish to avoid the danger that redistributors of a free
|
|
||||||
program will individually obtain patent licenses, in effect making the
|
|
||||||
program proprietary. To prevent this, we have made it clear that any
|
|
||||||
patent must be licensed for everyone's free use or not licensed at all.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License applies to any program or other work which contains
|
|
||||||
a notice placed by the copyright holder saying it may be distributed
|
|
||||||
under the terms of this General Public License. The "Program", below,
|
|
||||||
refers to any such program or work, and a "work based on the Program"
|
|
||||||
means either the Program or any derivative work under copyright law:
|
|
||||||
that is to say, a work containing the Program or a portion of it,
|
|
||||||
either verbatim or with modifications and/or translated into another
|
|
||||||
language. (Hereinafter, translation is included without limitation in
|
|
||||||
the term "modification".) Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running the Program is not restricted, and the output from the Program
|
|
||||||
is covered only if its contents constitute a work based on the
|
|
||||||
Program (independent of having been made by running the Program).
|
|
||||||
Whether that is true depends on what the Program does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Program's
|
|
||||||
source code as you receive it, in any medium, provided that you
|
|
||||||
conspicuously and appropriately publish on each copy an appropriate
|
|
||||||
copyright notice and disclaimer of warranty; keep intact all the
|
|
||||||
notices that refer to this License and to the absence of any warranty;
|
|
||||||
and give any other recipients of the Program a copy of this License
|
|
||||||
along with the Program.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy, and
|
|
||||||
you may at your option offer warranty protection in exchange for a fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Program or any portion
|
|
||||||
of it, thus forming a work based on the Program, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) You must cause the modified files to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
b) You must cause any work that you distribute or publish, that in
|
|
||||||
whole or in part contains or is derived from the Program or any
|
|
||||||
part thereof, to be licensed as a whole at no charge to all third
|
|
||||||
parties under the terms of this License.
|
|
||||||
|
|
||||||
c) If the modified program normally reads commands interactively
|
|
||||||
when run, you must cause it, when started running for such
|
|
||||||
interactive use in the most ordinary way, to print or display an
|
|
||||||
announcement including an appropriate copyright notice and a
|
|
||||||
notice that there is no warranty (or else, saying that you provide
|
|
||||||
a warranty) and that users may redistribute the program under
|
|
||||||
these conditions, and telling the user how to view a copy of this
|
|
||||||
License. (Exception: if the Program itself is interactive but
|
|
||||||
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
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Program, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Program.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Program
|
|
||||||
with the Program (or with a work based on the Program) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may copy and distribute the Program (or a work based on it,
|
|
||||||
under Section 2) in object code or executable form under the terms of
|
|
||||||
Sections 1 and 2 above provided that you also do one of the following:
|
|
||||||
|
|
||||||
a) Accompany it with the complete corresponding machine-readable
|
|
||||||
source code, which must be distributed under the terms of Sections
|
|
||||||
1 and 2 above on a medium customarily used for software interchange; or,
|
|
||||||
|
|
||||||
b) Accompany it with a written offer, valid for at least three
|
|
||||||
years, to give any third party, for a charge no more than your
|
|
||||||
cost of physically performing source distribution, a complete
|
|
||||||
machine-readable copy of the corresponding source code, to be
|
|
||||||
distributed under the terms of Sections 1 and 2 above on a medium
|
|
||||||
customarily used for software interchange; or,
|
|
||||||
|
|
||||||
c) Accompany it with the information you received as to the offer
|
|
||||||
to distribute corresponding source code. (This alternative is
|
|
||||||
allowed only for noncommercial distribution and only if you
|
|
||||||
received the program in object code or executable form with such
|
|
||||||
an offer, in accord with Subsection b above.)
|
|
||||||
|
|
||||||
The source code for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For an executable work, complete source
|
|
||||||
code means all the source code for all modules it contains, plus any
|
|
||||||
associated interface definition files, plus the scripts used to
|
|
||||||
control compilation and installation of the executable. However, as a
|
|
||||||
special exception, the source code distributed need not include
|
|
||||||
anything that is normally distributed (in either source or binary
|
|
||||||
form) with the major components (compiler, kernel, and so on) of the
|
|
||||||
operating system on which the executable runs, unless that component
|
|
||||||
itself accompanies the executable.
|
|
||||||
|
|
||||||
If distribution of executable or object code is made by offering
|
|
||||||
access to copy from a designated place, then offering equivalent
|
|
||||||
access to copy the source code from the same place counts as
|
|
||||||
distribution of the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program
|
|
||||||
except as expressly provided under this License. Any attempt
|
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is
|
|
||||||
void, and will automatically terminate your rights under this License.
|
|
||||||
However, parties who have received copies, or rights, from you under
|
|
||||||
this License will not have their licenses terminated so long as such
|
|
||||||
parties remain in full compliance.
|
|
||||||
|
|
||||||
5. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Program or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Program (or any work based on the
|
|
||||||
Program), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Program or works based on it.
|
|
||||||
|
|
||||||
6. Each time you redistribute the Program (or any work based on the
|
|
||||||
Program), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute or modify the Program subject to
|
|
||||||
these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties to
|
|
||||||
this License.
|
|
||||||
|
|
||||||
7. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Program at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Program by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Program.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under
|
|
||||||
any particular circumstance, the balance of the section is intended to
|
|
||||||
apply and the section as a whole is intended to apply in other
|
|
||||||
circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system, which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
8. If the distribution and/or use of the Program is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Program under this License
|
|
||||||
may add an explicit geographical distribution limitation excluding
|
|
||||||
those countries, so that distribution is permitted only in or among
|
|
||||||
countries not thus excluded. In such case, this License incorporates
|
|
||||||
the limitation as if written in the body of this License.
|
|
||||||
|
|
||||||
9. The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Program
|
|
||||||
specifies a version number of this License which applies to it and "any
|
|
||||||
later version", you have the option of following the terms and conditions
|
|
||||||
either of that version or of any later version published by the Free
|
|
||||||
Software Foundation. If the Program does not specify a version number of
|
|
||||||
this License, you may choose any version ever published by the Free Software
|
|
||||||
Foundation.
|
|
||||||
|
|
||||||
10. If you wish to incorporate parts of the Program into other free
|
|
||||||
programs whose distribution conditions are different, write to the author
|
|
||||||
to ask for permission. For software which is copyrighted by the Free
|
|
||||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
|
||||||
make exceptions for this. Our decision will be guided by the two goals
|
|
||||||
of preserving the free status of all derivatives of our free software and
|
|
||||||
of promoting the sharing and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
|
||||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
|
||||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
|
||||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
|
||||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
|
||||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
|
||||||
REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
|
||||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
|
||||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
|
||||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
|
||||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
|
||||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program is interactive, make it output a short notice like this
|
|
||||||
when it starts in an interactive mode:
|
|
||||||
|
|
||||||
Gnomovision version 69, Copyright (C) year name of author
|
|
||||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, the commands you use may
|
|
||||||
be called something other than `show w' and `show c'; they could even be
|
|
||||||
mouse-clicks or menu items--whatever suits your program.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1989
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
This General Public License does not permit incorporating your program into
|
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
|
||||||
consider it more useful to permit linking proprietary applications with the
|
|
||||||
library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License.
|
|
||||||
|
52
README.md
52
README.md
@@ -1,51 +1,33 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
<img src="https://socialify.git.ci/NapNeko/NapCatQQ/image?description=1&language=1&logo=https%3A%2F%2Fraw.githubusercontent.com%2FNapNeko%2FNapCatQQ%2Fmain%2Flogo.png&name=1&stargazers=1&theme=Auto" alt="NapCatQQ" width="640" height="320" />
|
||||||

|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
---
|
## 项目介绍
|
||||||
## 欢迎回家
|
|
||||||
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现
|
|
||||||
|
|
||||||
## 碎碎叨叨
|
NapCatQQ 是基于 PC NTQQ 本体实现一套无头 Bot 框架。
|
||||||
- [x] **安装简单**:就算是笨蛋也能使用
|
|
||||||
- [x] **性能友好**:就算是低内存也能使用
|
|
||||||
- [x] **接口丰富**:就算是没有也能使用
|
|
||||||
- [x] **稳定好用**:就算是被捉也能使用
|
|
||||||
|
|
||||||
## 使用框架
|
名字寓意 瞌睡猫QQ,像睡着了一样在后台低占用运行的无需GUI界面的NTQQ。
|
||||||
|
|
||||||
|
## 如何使用
|
||||||
|
|
||||||
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||||
|
|
||||||
**首次使用**请务必查看如下文档看使用教程
|
**首次使用** 请务必前往 [官方文档](https://napneko.github.io/) 查看使用文档与教程
|
||||||
|
|
||||||
### 文档地址
|
|
||||||
|
|
||||||
[Cloudflare.Worker](https://doc.napneko.icu/)
|
## 项目声明
|
||||||
|
|
||||||
[Cloudflare.HKServer](https://napcat.napneko.icu/)
|
* 请不要在无关地方宣传NapCatQQ,本项目只是用于学习 node 相关知识,切勿用于违法用途
|
||||||
|
|
||||||
[Cloudflare.Pages](https://napneko.pages.dev/)
|
* NapCat 不会收集用户隐私信息,但是未来可能会为了更好的利于 NapCat 的优化会收集一些设备信息,如 cpu 架构,系统版本等
|
||||||
|
|
||||||
[Server.China](https://napneko.com/)
|
## 相关链接
|
||||||
|
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
|
||||||
|
|
||||||
[Server.Other](https://napcat.cyou/)
|
## 鸣谢名单
|
||||||
|
|
||||||
[Github.IO](https://napneko.github.io/)
|
[Lagrange](https://github.com/LagrangeDev/Lagrange.Core)
|
||||||
## 回家旅途
|
|
||||||
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
|
|
||||||
|
|
||||||
## 感谢他们
|
<!--
|
||||||
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot)
|
QQ群:545402644
|
||||||
|
-->
|
||||||
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权
|
|
||||||
|
|
||||||
感谢 Tencent Tdesign / Vue3 强力驱动 NapCat.WebUi
|
|
||||||
|
|
||||||
不过最最重要的 还是需要感谢屏幕前的你哦~
|
|
||||||
|
|
||||||
---
|
|
||||||
## 开源附加
|
|
||||||
|
|
||||||
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经仓库主作者授权二次分发或基于 NapCat 代码开发。**
|
|
||||||
|
12
docs/changelogs/CHANGELOG.v1.5.9.md
Normal file
12
docs/changelogs/CHANGELOG.v1.5.9.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.5.9
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化缓存问题
|
||||||
|
* 修复poke异常上报
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
17
docs/changelogs/old/CHANGELOG.v1.3.3.md
Normal file
17
docs/changelogs/old/CHANGELOG.v1.3.3.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# v1.3.3
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.9-23424 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 尝试修复多开崩溃问题
|
||||||
|
* 修复群列表更新问题
|
||||||
|
* 修复兼容性问题支持Win7
|
||||||
|
* 修复下载 http 资源缺少UA
|
||||||
|
* 优化少量消息合并转发速度
|
||||||
|
* 修复加载群通知时出现 getUserDetailInfo timeout 导致程序崩溃
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 新增设置群公告 Api: /_send_group_notice
|
||||||
|
* 新增重启实现 包括重启快速登录/普通重启 副作用: 原进程 无法清理
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
18
docs/changelogs/old/CHANGELOG.v1.3.5.md
Normal file
18
docs/changelogs/old/CHANGELOG.v1.3.5.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# v1.3.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.9-23424 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化启动脚本
|
||||||
|
* 修复非管理时群成员减少事件上报 **无法获取操作者与操作类型**
|
||||||
|
* 修复快速重启进程清理问题
|
||||||
|
* 优化配置文件格式 支持自动更新配置 但仍然建议 **备份配置**
|
||||||
|
* 修复正向反向ws多个客户端周期多次心跳问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 支持WebUi热重载
|
||||||
|
* 新增启动输出WEBUI秘钥
|
||||||
|
* 新增群荣誉信息 /get_group_honor_info
|
||||||
|
* 支持获取群系统消息 /get_group_system_msg
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.3.6.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.3.6.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.3.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.9-23424 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复戳一戳多次上报问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
15
docs/changelogs/old/CHANGELOG.v1.3.8.md
Normal file
15
docs/changelogs/old/CHANGELOG.v1.3.8.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# v1.3.8
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.9-23873 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化打包后体积问题
|
||||||
|
* 修复QQ等级获取
|
||||||
|
* 兼容 9.7.x 版本换行符 统一为 \n
|
||||||
|
* 修复处理加群请求 字段异常情况
|
||||||
|
* 修复退群通知问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.3.9.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.3.9.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.3.9
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-23873 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复QQ等级获取与兼容性问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.4.0.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.0.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.0
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 支持空间Cookies获取
|
||||||
|
* 支持获取在线设备 API /get_online_clients
|
||||||
|
* 支持图片OCR API: /.ocr_image /ocr_image
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
14
docs/changelogs/old/CHANGELOG.v1.4.1.md
Normal file
14
docs/changelogs/old/CHANGELOG.v1.4.1.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# v1.4.1
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 提高部分Api兼容性
|
||||||
|
* 优化日志膨胀问题
|
||||||
|
* 在线状态刷新问题修复
|
||||||
|
## 新增与调整
|
||||||
|
* 支持非管理群 本地记录时间数据 (建议 **备份配置 清空配置 重新配置**)
|
||||||
|
* 新增英译中接口 Api: /translate_en2zh
|
||||||
|
* 新增群文件管理相关扩展接口 Api: /get_group_file_count /get_group_file_list /set_group_file_folder /del_group_file /del_group_file_folder
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.4.2.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.2.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.2
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复获取群文件列表Api
|
||||||
|
* 修复退群通知问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.4.3.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.4.3.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.4.3
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复名片通知
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
10
docs/changelogs/old/CHANGELOG.v1.4.4.md
Normal file
10
docs/changelogs/old/CHANGELOG.v1.4.4.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# v1.4.4
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 更新
|
||||||
|
* **重大更新:**更新了版本号
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
||||||
|
|
12
docs/changelogs/old/CHANGELOG.v1.4.5.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.5.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 紧急修复二维扫码问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
||||||
|
|
12
docs/changelogs/old/CHANGELOG.v1.4.6.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.6.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化整体稳定性
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
||||||
|
|
11
docs/changelogs/old/CHANGELOG.v1.4.7.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.4.7.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.4.7
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 临时扩展 Api: GoCQHTTPUploadGroupFile folder_id字段 用于选择文件夹
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.4.8.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.8.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.8
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化Guid的生成方式
|
||||||
|
* 支持临时消息获取群来源
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.4.9.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.4.9.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.4.9
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复接口调用问题 接口标准化 API:set_group_add_request
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.5.0.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.0.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.0
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修正各Api默认值
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.5.1.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.5.1.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.5.1
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 支持 新Api: set_self_profile 可设置个性签名
|
||||||
|
* 修复 Api: get_group_system_msg
|
||||||
|
* 整理日志、添加颜色、使用统一的日志函数以提高日志可读性
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
13
docs/changelogs/old/CHANGELOG.v1.5.2.md
Normal file
13
docs/changelogs/old/CHANGELOG.v1.5.2.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# v1.5.2
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 替换Uid/Uin为内部实现
|
||||||
|
* 增加HttpApi调用稳定性
|
||||||
|
* 修复 GetMsg 兼容性
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 支持真正意义上的陌生人信息获取 Api: GoCQHTTP_GetStrangerInfo
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
15
docs/changelogs/old/CHANGELOG.v1.5.3.md
Normal file
15
docs/changelogs/old/CHANGELOG.v1.5.3.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# v1.5.3
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-23568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复引用消息id问题
|
||||||
|
* 修复添加好友的通知
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 扩展群分享Json生成
|
||||||
|
* 扩展关于收藏的一系列接口
|
||||||
|
* 支持专属群头衔获取
|
||||||
|
* 支持视频获取直链
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.5.4.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.4.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.4
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-23568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 紧急修复视频与文件问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.5.5.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.5.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-23568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 紧急修复一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.5.6.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.6.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-24568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.5.7.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.7.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.7
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-24568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
14
docs/changelogs/old/CHANGELOG.v1.5.8.md
Normal file
14
docs/changelogs/old/CHANGELOG.v1.5.8.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# v1.5.8
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-24568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复视频文件残留问题
|
||||||
|
* 重构 getcookies接口 支持大部分常见域
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 日志大小限制
|
||||||
|
* 支持 QQ音乐 卡片 无签名支持时 启用内置方法(缺点没有封面 限速1min/条)
|
||||||
|
* 支持Window X86-32机器
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
2
docs/develop/Android.md
Normal file
2
docs/develop/Android.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# 开始
|
||||||
|
jadx 跳转于 `com.tencent.qqnt.kernel.*`
|
42
docs/develop/GetMemberExt.md
Normal file
42
docs/develop/GetMemberExt.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Android
|
||||||
|
```java
|
||||||
|
GroupMemberExtReq groupMemberExtReq = new GroupMemberExtReq();
|
||||||
|
groupMemberExtReq.sourceType = MemberExtSourceType.TITLETYPE.ordinal();
|
||||||
|
groupMemberExtReq.groupCode = longOrNull.longValue();
|
||||||
|
groupMemberExtReq.beginUin = "0";
|
||||||
|
groupMemberExtReq.dataTime = "0";
|
||||||
|
Long[] lArr = new Long[1];
|
||||||
|
AppInterface a2 = dVar.a();
|
||||||
|
lArr[0] = Long.valueOf(a2 != null ? a2.getLongAccountUin() : 0L);
|
||||||
|
arrayListOf = CollectionsKt__CollectionsKt.arrayListOf(lArr);
|
||||||
|
groupMemberExtReq.uinList = arrayListOf;
|
||||||
|
MemberExtInfoFilter memberExtInfoFilter = new MemberExtInfoFilter();
|
||||||
|
memberExtInfoFilter.memberLevelInfoUin = 1;
|
||||||
|
memberExtInfoFilter.memberLevelInfoPoint = 1;
|
||||||
|
memberExtInfoFilter.memberLevelInfoActiveDay = 1;
|
||||||
|
memberExtInfoFilter.memberLevelInfoLevel = 1;
|
||||||
|
memberExtInfoFilter.levelName = 1;
|
||||||
|
memberExtInfoFilter.dataTime = 1;
|
||||||
|
memberExtInfoFilter.sysShowFlag = 1;
|
||||||
|
memberExtInfoFilter.userShowFlag = 1;
|
||||||
|
memberExtInfoFilter.userShowFlagNew = 1;
|
||||||
|
memberExtInfoFilter.levelNameNew = 1;
|
||||||
|
Unit unit = Unit.INSTANCE;
|
||||||
|
groupMemberExtReq.memberExtFilter = memberExtInfoFilter;
|
||||||
|
troopLevelFrequencyControl.f(troopUin, new TroopListRepo$fetchTroopLevelInfo$2(b2, groupMemberExtReq, troopUin, new com.tencent.qqnt.troopmemberlist.report.c("fetchTroopLevelInfo")));
|
||||||
|
```
|
||||||
|
# Win
|
||||||
|
参数解析位于 sub_181456A10(24108) -> wrapper.node(24108)+1456A10
|
||||||
|
IGroupService.GetMemberExt(param: object);
|
||||||
|
param展开如下
|
||||||
|
```
|
||||||
|
groupCode string
|
||||||
|
beginUin string
|
||||||
|
dataTime string
|
||||||
|
uinList Array<string>
|
||||||
|
uinNum string
|
||||||
|
groupType string
|
||||||
|
richCardNameVer string
|
||||||
|
sourceType number
|
||||||
|
memberExtFilter object// 参数解析位于 sub_18145A6D0(24108) -> wrapper.node(24108)+145A6D0
|
||||||
|
```
|
48
docs/develop/Image.NTAndroid.md
Normal file
48
docs/develop/Image.NTAndroid.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
public static final int C2C_PIC_DOWNLOAD = 1004;
|
||||||
|
public static final String C2C_PIC_DOWNLOAD_DOMAIN = "c2cpicdw.qpic.cn";
|
||||||
|
public static final String C2C_PIC_DOWNLOAD_QUIC_DOMAIN = "c2cpicdw.quic.qpic.cn";
|
||||||
|
public static final int FLAG_NOT_UPLOAD = 3;
|
||||||
|
public static final int FLAG_UPLOADINFO_ERROR = 4;
|
||||||
|
public static final int GROUP_PIC_DOWNLOAD = 1000;
|
||||||
|
public static final String GROUP_PIC_DOWNLOAD_DOMAIN = "gchat.qpic.cn";
|
||||||
|
public static final String GROUP_PIC_DOWNLOAD_QUIC_DOMAIN = "gchat.quic.qpic.cn";
|
||||||
|
public static final String GUILD_PIC_DOWNLOAD_DOMAIN = "gchat.qpic.cn/qmeetpic";
|
||||||
|
public static final boolean NEW_STORE_FLAG = true;
|
||||||
|
public static final String PTT_VIDEO_DOWNLOAD_DOMAIN = "grouptalk.c2c.qq.com";
|
||||||
|
|
||||||
|
protected static final int AVIF_DECODE_EXCEPTION = 4;
|
||||||
|
protected static final int AVIF_DECODE_FAIL = 1;
|
||||||
|
protected static final int AVIF_DECODE_FAIL_SO_FAIL = 2;
|
||||||
|
protected static final int AVIF_DECODE_FAIL_UNKNOWN = 6;
|
||||||
|
protected static final int AVIF_DECODE_FILETYPE_ERROR = 5;
|
||||||
|
protected static final int AVIF_DECODE_OOM = 3;
|
||||||
|
protected static final int AVIF_DECODE_RENAME_FAIL = 7;
|
||||||
|
protected static final int AVIF_DECODE_SUC = 0;
|
||||||
|
public static final String AVIF_FILE_SUFFIX = ".avif";
|
||||||
|
public static final int AVIF_REQ_APPRUNTIME_NULL = 12;
|
||||||
|
public static final int AVIF_REQ_CODEC_UNSURPPORT = 5;
|
||||||
|
protected static final int AVIF_REQ_DENSITY_UNSURPPORT = 10;
|
||||||
|
protected static final int AVIF_REQ_FLASH_PHOTO = 9;
|
||||||
|
protected static final int AVIF_REQ_HAS_TMP_AVIF = 7;
|
||||||
|
protected static final int AVIF_REQ_INVALID_MSG_RECORD = 2;
|
||||||
|
protected static final int AVIF_REQ_IS_RAW_PHOTO = 3;
|
||||||
|
protected static final int AVIF_REQ_OUTPUTSTREAM_UNSURPPORT = 11;
|
||||||
|
protected static final int AVIF_REQ_OVERSIZE = 6;
|
||||||
|
protected static final int AVIF_REQ_RETRY = 1;
|
||||||
|
public static final int AVIF_REQ_SO_DOWNLOAD_FAILED = 8;
|
||||||
|
protected static final int AVIF_REQ_SUC = 0;
|
||||||
|
public static final int AVIF_REQ_SWITCH_CLOSE = 4;
|
||||||
|
public static final String C2C_PIC_DOWNLOAD_ERROR_CODE = "C2CPicDownloadErrorCode";
|
||||||
|
static final int DOWNLOAD_ST_COMPLETE = 1;
|
||||||
|
static final int DOWNLOAD_ST_HEAD = 2;
|
||||||
|
static final int DOWNLOAD_ST_LEFT = 4;
|
||||||
|
static final int DOWNLOAD_ST_PART = 3;
|
||||||
|
private static final int ENCRYPT_APPID = 1600000226;
|
||||||
|
public static final String GROUP_PIC_DOWNLOAD_ERROR_CODE = "GroupPicDownloadErrorCode";
|
||||||
|
public static final String KEY_PIC_DOWNLOAD_ERROR_CODE = "param_detail_code";
|
||||||
|
protected static final int QUIC_FAIL_IP_LIST_EMPTY = 1;
|
||||||
|
protected static final int QUIC_FAIL_REQUEST_HTTPS = 3;
|
||||||
|
protected static final int QUIC_FAIL_REQUEST_QUIC = 2;
|
||||||
|
protected static final int QUIC_FAIL_SO_LOAD = 4;
|
||||||
|
public static final String REPORT_TAG_DIRECT_DOWNLOAD_FAIL = "report_direct_download_fail";
|
||||||
|
public static final String REQ_PARAM_AVIF = "tp=avif";
|
444
docs/develop/Msg常量NTAndroid.md
Normal file
444
docs/develop/Msg常量NTAndroid.md
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
```java
|
||||||
|
MsgConstant
|
||||||
|
int ARKSTRUCTELEMENTSUBTYPETENCENTDOCFROMMINIAPP = 1;
|
||||||
|
int ARKSTRUCTELEMENTSUBTYPETENCENTDOCFROMPLUSPANEL = 2;
|
||||||
|
int ARKSTRUCTELEMENTSUBTYPEUNKNOWN = 0;
|
||||||
|
int ATTYPEALL = 1;
|
||||||
|
int ATTYPECATEGORY = 512;
|
||||||
|
int ATTYPECHANNEL = 16;
|
||||||
|
int ATTYPEME = 4;
|
||||||
|
int ATTYPEONE = 2;
|
||||||
|
int ATTYPEONLINE = 64;
|
||||||
|
int ATTYPEROLE = 8;
|
||||||
|
int ATTYPESUMMON = 32;
|
||||||
|
int ATTYPESUMMONONLINE = 128;
|
||||||
|
int ATTYPESUMMONROLE = 256;
|
||||||
|
int ATTYPEUNKNOWN = 0;
|
||||||
|
int CALENDARELEMSUBTYPECOMMON = 3;
|
||||||
|
int CALENDARELEMSUBTYPESTRONG = 1;
|
||||||
|
int CALENDARELEMSUBTYPEUNKNOWN = 0;
|
||||||
|
int CALENDARELEMSUBTYPEWEAK = 2;
|
||||||
|
int FACEBUBBLEELEMSUBTYPENORMAL = 1;
|
||||||
|
int FACEBUBBLEELEMSUBTYPEUNKNOWN = 0;
|
||||||
|
int FETCHLONGMSGERRCODEMSGEXPIRED = 196;
|
||||||
|
int FILEELEMENTSUBTYPEAI = 16;
|
||||||
|
int FILEELEMENTSUBTYPEAPP = 11;
|
||||||
|
int FILEELEMENTSUBTYPEAUDIO = 3;
|
||||||
|
int FILEELEMENTSUBTYPEDOC = 4;
|
||||||
|
int FILEELEMENTSUBTYPEEMOTICON = 15;
|
||||||
|
int FILEELEMENTSUBTYPEEXCEL = 6;
|
||||||
|
int FILEELEMENTSUBTYPEFOLDER = 13;
|
||||||
|
int FILEELEMENTSUBTYPEHTML = 10;
|
||||||
|
int FILEELEMENTSUBTYPEIPA = 14;
|
||||||
|
int FILEELEMENTSUBTYPENORMAL = 0;
|
||||||
|
int FILEELEMENTSUBTYPEPDF = 7;
|
||||||
|
int FILEELEMENTSUBTYPEPIC = 1;
|
||||||
|
int FILEELEMENTSUBTYPEPPT = 5;
|
||||||
|
int FILEELEMENTSUBTYPEPSD = 12;
|
||||||
|
int FILEELEMENTSUBTYPETXT = 8;
|
||||||
|
int FILEELEMENTSUBTYPEVIDEO = 2;
|
||||||
|
int FILEELEMENTSUBTYPEZIP = 9;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEAIOOP = 15;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEBLOCK = 14;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEBUDDY = 5;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEBUDDYNOTIFY = 9;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEEMOJIREPLY = 3;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEESSENCE = 7;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEFEED = 6;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEFEEDCHANNELMSG = 11;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEFILE = 10;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEGROUP = 4;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEGROUPNOTIFY = 8;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEJSON = 17;
|
||||||
|
int GRAYTIPELEMENTSUBTYPELOCALMSG = 13;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEPROCLAMATION = 2;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEREVOKE = 1;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEUNKNOWN = 0;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEWALLET = 16;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEXMLMSG = 12;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLEBLUEBLACKGROUND = 4;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLEBLUEBORDER = 1;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLEGRAYBORDER = 0;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLENOBORDER = 2;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLEREDCHARACTER = 3;
|
||||||
|
int INPUTSTATUSTYPECANCEL = 2;
|
||||||
|
int INPUTSTATUSTYPESPEAK = 3;
|
||||||
|
int INPUTSTATUSTYPETEXT = 1;
|
||||||
|
int KACTIVITYMSG = 22;
|
||||||
|
int KADDLOCALMSGEXTINFOTYPEPROLOGUEMSG = 1;
|
||||||
|
int KANONYMOUSATMEMSGTYPEINMSGBOX = 1001;
|
||||||
|
int KANONYMOUSFLAGFROMOTHERPEOPLE = 1;
|
||||||
|
int KANONYMOUSFLAGFROMOWN = 2;
|
||||||
|
int KANONYMOUSFLAGINVALID = 0;
|
||||||
|
int KAPPCHANNELMSG = 16;
|
||||||
|
int KATALLMSGTYPEINMSGBOX = 2000;
|
||||||
|
int KATMEMSGTYPEINMSGBOX = 1000;
|
||||||
|
int KATTRIBUTETYPEADELIEMSG = 16;
|
||||||
|
int KATTRIBUTETYPEEXTENDBUSINESS = 13;
|
||||||
|
int KATTRIBUTETYPEFEEDBACKSTATE = 17;
|
||||||
|
int KATTRIBUTETYPEGROUPHONOR = 2;
|
||||||
|
int KATTRIBUTETYPEKINGHONOR = 3;
|
||||||
|
int KATTRIBUTETYPELONGMSG = 8;
|
||||||
|
int KATTRIBUTETYPEMEMORYSTATEMSGINFO = 18;
|
||||||
|
int KATTRIBUTETYPEMSG = 0;
|
||||||
|
int KATTRIBUTETYPEMSGBOXEVENTTYPE = 14;
|
||||||
|
int KATTRIBUTETYPEPERSONAL = 1;
|
||||||
|
int KATTRIBUTETYPEPUBLICACCOUNT = 4;
|
||||||
|
int KATTRIBUTETYPEQQCONNECT = 12;
|
||||||
|
int KATTRIBUTETYPESENDMSGRSPTRANSSVRINFO = 15;
|
||||||
|
int KATTRIBUTETYPESHAREDMSGINFO = 5;
|
||||||
|
int KATTRIBUTETYPETEMPCHATGAMESESSION = 6;
|
||||||
|
int KATTRIBUTETYPETOROBOTMSG = 9;
|
||||||
|
int KATTRIBUTETYPEUININFO = 7;
|
||||||
|
int KATTRIBUTETYPEZPLAN = 11;
|
||||||
|
int KAUTOREPLYTEXTNONEINDEX = -1;
|
||||||
|
int KAVRECORDMSG = 19;
|
||||||
|
int KBUSINESSTYPGUILD = 1;
|
||||||
|
int KBUSINESSTYPNT = 0;
|
||||||
|
int KCHATTYPEADELIE = 42;
|
||||||
|
int KCHATTYPEBUDDYNOTIFY = 5;
|
||||||
|
int KCHATTYPEC2C = 1;
|
||||||
|
int KCHATTYPECIRCLE = 113;
|
||||||
|
int KCHATTYPEDATALINE = 8;
|
||||||
|
int KCHATTYPEDATALINEMQQ = 134;
|
||||||
|
int KCHATTYPEDISC = 3;
|
||||||
|
int KCHATTYPEFAV = 41;
|
||||||
|
int KCHATTYPEGAMEMESSAGE = 105;
|
||||||
|
int KCHATTYPEGAMEMESSAGEFOLDER = 116;
|
||||||
|
int KCHATTYPEGROUP = 2;
|
||||||
|
int KCHATTYPEGROUPBLESS = 133;
|
||||||
|
int KCHATTYPEGROUPGUILD = 9;
|
||||||
|
int KCHATTYPEGROUPHELPER = 7;
|
||||||
|
int KCHATTYPEGROUPNOTIFY = 6;
|
||||||
|
int KCHATTYPEGUILD = 4;
|
||||||
|
int KCHATTYPEGUILDMETA = 16;
|
||||||
|
int KCHATTYPEMATCHFRIEND = 104;
|
||||||
|
int KCHATTYPEMATCHFRIENDFOLDER = 109;
|
||||||
|
int KCHATTYPENEARBY = 106;
|
||||||
|
int KCHATTYPENEARBYASSISTANT = 107;
|
||||||
|
int KCHATTYPENEARBYFOLDER = 110;
|
||||||
|
int KCHATTYPENEARBYHELLOFOLDER = 112;
|
||||||
|
int KCHATTYPENEARBYINTERACT = 108;
|
||||||
|
int KCHATTYPEQQNOTIFY = 132;
|
||||||
|
int KCHATTYPERELATEACCOUNT = 131;
|
||||||
|
int KCHATTYPESERVICEASSISTANT = 118;
|
||||||
|
int KCHATTYPESERVICEASSISTANTSUB = 201;
|
||||||
|
int KCHATTYPESQUAREPUBLIC = 115;
|
||||||
|
int KCHATTYPESUBSCRIBEFOLDER = 30;
|
||||||
|
int KCHATTYPETEMPADDRESSBOOK = 111;
|
||||||
|
int KCHATTYPETEMPBUSSINESSCRM = 102;
|
||||||
|
int KCHATTYPETEMPC2CFROMGROUP = 100;
|
||||||
|
int KCHATTYPETEMPC2CFROMUNKNOWN = 99;
|
||||||
|
int KCHATTYPETEMPFRIENDVERIFY = 101;
|
||||||
|
int KCHATTYPETEMPNEARBYPRO = 119;
|
||||||
|
int KCHATTYPETEMPPUBLICACCOUNT = 103;
|
||||||
|
int KCHATTYPETEMPWPA = 117;
|
||||||
|
int KCHATTYPEUNKNOWN = 0;
|
||||||
|
int KCHATTYPEWEIYUN = 40;
|
||||||
|
int KCOMMONREDENVELOPEMSGTYPEINMSGBOX = 1007;
|
||||||
|
int KDOWNSOURCETYPEAIOINNER = 1;
|
||||||
|
int KDOWNSOURCETYPEBIGSCREEN = 2;
|
||||||
|
int KDOWNSOURCETYPEHISTORY = 3;
|
||||||
|
int KDOWNSOURCETYPEUNKNOWN = 0;
|
||||||
|
int KELEMTYPEACTIVITY = 25;
|
||||||
|
int KELEMTYPEACTIVITYSTATE = 41;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPECREATEMOBATEAM = 12;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEDISBANDMOBATEAM = 11;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEFEEDSQUARE = 10001;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEFINISHGAME = 16;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEFINISHMATCHTEAM = 14;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEHOTCHAT = 10000;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEMINIGAME = 18;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEMUSICPLAY = 17;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPENEWSMOBA = 9;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPENOLIVE = 2;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPENOSCREENSHARE = 7;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPENOVOICE = 3;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEONLIVE = 1;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEONSCREENSHARE = 6;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEONVOICE = 4;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPESTARTMATCHTEAM = 13;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPETARTGAME = 15;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEUNKNOWN = 0;
|
||||||
|
int KELEMTYPEADELIEACTIONBAR = 44;
|
||||||
|
int KELEMTYPEADELIERECOMMENDEDMSG = 43;
|
||||||
|
int KELEMTYPEARKSTRUCT = 10;
|
||||||
|
int KELEMTYPEAVRECORD = 21;
|
||||||
|
int KELEMTYPECALENDAR = 19;
|
||||||
|
int KELEMTYPEFACE = 6;
|
||||||
|
int KELEMTYPEFACEBUBBLE = 27;
|
||||||
|
int KELEMTYPEFEED = 22;
|
||||||
|
int KELEMTYPEFILE = 3;
|
||||||
|
int KELEMTYPEGIPHY = 15;
|
||||||
|
int KELEMTYPEGRAYTIP = 8;
|
||||||
|
int KELEMTYPEINLINEKEYBOARD = 17;
|
||||||
|
int KELEMTYPEINTEXTGIFT = 18;
|
||||||
|
int KELEMTYPELIVEGIFT = 12;
|
||||||
|
int KELEMTYPEMARKDOWN = 14;
|
||||||
|
int KELEMTYPEMARKETFACE = 11;
|
||||||
|
int KELEMTYPEMULTIFORWARD = 16;
|
||||||
|
int KELEMTYPEONLINEFILE = 23;
|
||||||
|
int KELEMTYPEPIC = 2;
|
||||||
|
int KELEMTYPEPROLOGUE = 46;
|
||||||
|
int KELEMTYPEPTT = 4;
|
||||||
|
int KELEMTYPEREPLY = 7;
|
||||||
|
int KELEMTYPESHARELOCATION = 28;
|
||||||
|
int KELEMTYPESTRUCTLONGMSG = 13;
|
||||||
|
int KELEMTYPETASKTOPMSG = 29;
|
||||||
|
int KELEMTYPETEXT = 1;
|
||||||
|
int KELEMTYPETOFU = 26;
|
||||||
|
int KELEMTYPEUNKNOWN = 0;
|
||||||
|
int KELEMTYPEVIDEO = 5;
|
||||||
|
int KELEMTYPEWALLET = 9;
|
||||||
|
int KELEMTYPEYOLOGAMERESULT = 20;
|
||||||
|
int KENTERAIO = 1;
|
||||||
|
int KEXITAIO = 2;
|
||||||
|
int KFEEDBACKBUTTONTYPEDISLIKE = 2;
|
||||||
|
int KFEEDBACKBUTTONTYPELIKE = 1;
|
||||||
|
int KFEEDBACKBUTTONTYPEPROMPTCLICK = 5;
|
||||||
|
int KFEEDBACKBUTTONTYPEREGENERATE = 4;
|
||||||
|
int KFEEDBACKBUTTONTYPEUNKNOWN = 0;
|
||||||
|
int KFEEDBACKOPTLIKE = 1;
|
||||||
|
int KFEEDBACKOPTUNKNOWN = 0;
|
||||||
|
int KFEEDBACKOPTUNLIKE = 2;
|
||||||
|
int KFRIENDNEWADDEDMSGTYPEINMSGBOX = 1008;
|
||||||
|
int KGAMEBOXNEWMSGTYPEINMSGBOX = 3000;
|
||||||
|
int KGIFTATMEMSGTYPEINMSGBOX = 1005;
|
||||||
|
int KGROUPFILEATALLMSGTYPEINMSGBOX = 2001;
|
||||||
|
int KGROUPHOMEWORK = 20000;
|
||||||
|
int KGROUPHOMEWORKTASK = 20001;
|
||||||
|
int KGROUPKEYWORDMSGTYPEINMSGBOX = 2006;
|
||||||
|
int KGROUPMANNOUNCEATALLMSGTYPEINMSGBOX = 2004;
|
||||||
|
int KGROUPTASKATALLMSGTYPEINMSGBOX = 2003;
|
||||||
|
int KGROUPUNREADTYPEINMSGBOX = 2007;
|
||||||
|
int KGUILDCHANNELLIST = 10;
|
||||||
|
int KHIGHLIGHTWORDINTEMPCHATTYPEINMSGBOX = 1009;
|
||||||
|
int KHOMEWORKREMINDER = 10000;
|
||||||
|
int KLIKEORDISLIKESTATEDISLIKE = 2;
|
||||||
|
int KLIKEORDISLIKESTATELIKE = 1;
|
||||||
|
int KLIKEORDISLIKESTATENONESELECTED = 0;
|
||||||
|
int KMARKETFACE = 17;
|
||||||
|
int KMEMORYSTATEMSGTYPEADELIEWELCOME = 1;
|
||||||
|
int KMEMORYSTATEMSGTYPEUNKNOWN = 0;
|
||||||
|
int KMINIPROGRAMNOTICE = 114;
|
||||||
|
int KMSGSUBTYPEARKGROUPANNOUNCE = 3;
|
||||||
|
int KMSGSUBTYPEARKGROUPANNOUNCECONFIRMREQUIRED = 4;
|
||||||
|
int KMSGSUBTYPEARKGROUPGIFTATME = 5;
|
||||||
|
int KMSGSUBTYPEARKGROUPTASKATALL = 6;
|
||||||
|
int KMSGSUBTYPEARKMULTIMSG = 7;
|
||||||
|
int KMSGSUBTYPEARKNORMAL = 0;
|
||||||
|
int KMSGSUBTYPEARKTENCENTDOCFROMMINIAPP = 1;
|
||||||
|
int KMSGSUBTYPEARKTENCENTDOCFROMPLUSPANEL = 2;
|
||||||
|
int KMSGSUBTYPEEMOTICON = 15;
|
||||||
|
int KMSGSUBTYPEFILEAPP = 11;
|
||||||
|
int KMSGSUBTYPEFILEAUDIO = 3;
|
||||||
|
int KMSGSUBTYPEFILEDOC = 4;
|
||||||
|
int KMSGSUBTYPEFILEEXCEL = 6;
|
||||||
|
int KMSGSUBTYPEFILEFOLDER = 13;
|
||||||
|
int KMSGSUBTYPEFILEHTML = 10;
|
||||||
|
int KMSGSUBTYPEFILEIPA = 14;
|
||||||
|
int KMSGSUBTYPEFILENORMAL = 0;
|
||||||
|
int KMSGSUBTYPEFILEPDF = 7;
|
||||||
|
int KMSGSUBTYPEFILEPIC = 1;
|
||||||
|
int KMSGSUBTYPEFILEPPT = 5;
|
||||||
|
int KMSGSUBTYPEFILEPSD = 12;
|
||||||
|
int KMSGSUBTYPEFILETXT = 8;
|
||||||
|
int KMSGSUBTYPEFILEVIDEO = 2;
|
||||||
|
int KMSGSUBTYPEFILEZIP = 9;
|
||||||
|
int KMSGSUBTYPELINK = 5;
|
||||||
|
int KMSGSUBTYPEMARKETFACE = 1;
|
||||||
|
int KMSGSUBTYPEMIXEMOTICON = 7;
|
||||||
|
int KMSGSUBTYPEMIXFACE = 3;
|
||||||
|
int KMSGSUBTYPEMIXMARKETFACE = 2;
|
||||||
|
int KMSGSUBTYPEMIXPIC = 1;
|
||||||
|
int KMSGSUBTYPEMIXREPLY = 4;
|
||||||
|
int KMSGSUBTYPEMIXTEXT = 0;
|
||||||
|
int KMSGSUBTYPETENCENTDOC = 6;
|
||||||
|
int KMSGTYPEARKSTRUCT = 11;
|
||||||
|
int KMSGTYPEFACEBUBBLE = 24;
|
||||||
|
int KMSGTYPEFILE = 3;
|
||||||
|
int KMSGTYPEGIFT = 14;
|
||||||
|
int KMSGTYPEGIPHY = 13;
|
||||||
|
int KMSGTYPEGRAYTIPS = 5;
|
||||||
|
int KMSGTYPEMIX = 2;
|
||||||
|
int KMSGTYPEMULTIMSGFORWARD = 8;
|
||||||
|
int KMSGTYPENULL = 1;
|
||||||
|
int KMSGTYPEONLINEFILE = 21;
|
||||||
|
int KMSGTYPEONLINEFOLDER = 27;
|
||||||
|
int KMSGTYPEPROLOGUE = 29;
|
||||||
|
int KMSGTYPEPTT = 6;
|
||||||
|
int KMSGTYPEREPLY = 9;
|
||||||
|
int KMSGTYPESHARELOCATION = 25;
|
||||||
|
int KMSGTYPESTRUCT = 4;
|
||||||
|
int KMSGTYPESTRUCTLONGMSG = 12;
|
||||||
|
int KMSGTYPETEXTGIFT = 15;
|
||||||
|
int KMSGTYPEUNKNOWN = 0;
|
||||||
|
int KMSGTYPEVIDEO = 7;
|
||||||
|
int KMSGTYPEWALLET = 10;
|
||||||
|
int KNEEDCONFIRMGROUPMANNOUNCEATALLMSGTYPEINMSGBOX = 2005;
|
||||||
|
int KNOTPASSTHROUGHEVENTTYPEUPPERBOUNDARY = 9999;
|
||||||
|
int KPTTFORMATTYPEAMR = 0;
|
||||||
|
int KPTTFORMATTYPESILK = 1;
|
||||||
|
int KPTTTRANSLATESTATUSFAIL = 3;
|
||||||
|
int KPTTTRANSLATESTATUSSUC = 2;
|
||||||
|
int KPTTTRANSLATESTATUSTRANSLATING = 1;
|
||||||
|
int KPTTTRANSLATESTATUSUNKNOWN = 0;
|
||||||
|
int KPTTVIPLEVELTYPENONE = 0;
|
||||||
|
int KPTTVIPLEVELTYPEQQVIP = 0;
|
||||||
|
int KPTTVIPLEVELTYPESVIP = 0;
|
||||||
|
int KPTTVOICECHANGETYPEBEASTMACHINE = 7;
|
||||||
|
int KPTTVOICECHANGETYPEBOY = 2;
|
||||||
|
int KPTTVOICECHANGETYPECATCHCOLD = 13;
|
||||||
|
int KPTTVOICECHANGETYPEECHO = 5;
|
||||||
|
int KPTTVOICECHANGETYPEFATGUY = 16;
|
||||||
|
int KPTTVOICECHANGETYPEFLASHING = 9;
|
||||||
|
int KPTTVOICECHANGETYPEGIRL = 1;
|
||||||
|
int KPTTVOICECHANGETYPEHORRIBLE = 3;
|
||||||
|
int KPTTVOICECHANGETYPEKINDERGARTEN = 6;
|
||||||
|
int KPTTVOICECHANGETYPEMEDAROT = 15;
|
||||||
|
int KPTTVOICECHANGETYPENONE = 0;
|
||||||
|
int KPTTVOICECHANGETYPEOPTIMUSPRIME = 8;
|
||||||
|
int KPTTVOICECHANGETYPEOUTOFDATE = 14;
|
||||||
|
int KPTTVOICECHANGETYPEPAPI = 11;
|
||||||
|
int KPTTVOICECHANGETYPEQUICK = 4;
|
||||||
|
int KPTTVOICECHANGETYPESTUTTER = 10;
|
||||||
|
int KPTTVOICECHANGETYPETRAPPEDBEAST = 12;
|
||||||
|
int KPTTVOICETYPEINTERCOM = 1;
|
||||||
|
int KPTTVOICETYPESOUNDRECORD = 2;
|
||||||
|
int KPTTVOICETYPEUNKNOW = 0;
|
||||||
|
int KPTTVOICETYPEVOICECHANGE = 3;
|
||||||
|
int KPUBLICACCOUNTTIANSHUHIGHLIGHTWORDTYPEINMSGBOX = 1010;
|
||||||
|
int KREPLYABSELEMTYPEFACE = 2;
|
||||||
|
int KREPLYABSELEMTYPEPIC = 3;
|
||||||
|
int KREPLYABSELEMTYPETEXT = 1;
|
||||||
|
int KREPLYABSELEMTYPEUNKNOWN = 0;
|
||||||
|
int KREPLYATMEMSGTYPEINMSGBOX = 1002;
|
||||||
|
int KRMDOWNTYPEORIG = 1;
|
||||||
|
int KRMDOWNTYPETHUMB = 2;
|
||||||
|
int KRMDOWNTYPEUNKNOWN = 0;
|
||||||
|
int KRMFILETHUMBSIZE128 = 128;
|
||||||
|
int KRMFILETHUMBSIZE320 = 320;
|
||||||
|
int KRMFILETHUMBSIZE384 = 384;
|
||||||
|
int KRMFILETHUMBSIZE750 = 750;
|
||||||
|
int KRMPICAIOTHUMBSIZE = 0;
|
||||||
|
int KRMPICTHUMBSIZE198 = 198;
|
||||||
|
int KRMPICTHUMBSIZE720 = 720;
|
||||||
|
int KRMPICTYPEBMP = 3;
|
||||||
|
int KRMPICTYPECHECKOTHER = 900;
|
||||||
|
int KRMPICTYPEGIF = 2;
|
||||||
|
int KRMPICTYPEJPG = 0;
|
||||||
|
int KRMPICTYPENEWPICAPNG = 2001;
|
||||||
|
int KRMPICTYPENEWPICBMP = 1005;
|
||||||
|
int KRMPICTYPENEWPICGIF = 2000;
|
||||||
|
int KRMPICTYPENEWPICJPEG = 1000;
|
||||||
|
int KRMPICTYPENEWPICPNG = 1001;
|
||||||
|
int KRMPICTYPENEWPICPROGERSSIVJPEG = 1003;
|
||||||
|
int KRMPICTYPENEWPICSHARPP = 1004;
|
||||||
|
int KRMPICTYPENEWPICWEBP = 1002;
|
||||||
|
int KRMPICTYPEPNG = 1;
|
||||||
|
int KRMPICTYPEUNKOWN = 0;
|
||||||
|
int KRMTHUMBSIZEZERO = 0;
|
||||||
|
int KRMTRNASFERSTATUSDOWNLOADING = 3;
|
||||||
|
int KRMTRNASFERSTATUSFAIL = 5;
|
||||||
|
int KRMTRNASFERSTATUSINIT = 1;
|
||||||
|
int KRMTRNASFERSTATUSSUC = 4;
|
||||||
|
int KRMTRNASFERSTATUSUNKOW = 0;
|
||||||
|
int KRMTRNASFERSTATUSUPLOADING = 2;
|
||||||
|
int KRMTRNASFERSTATUSUSERCANCEL = 6;
|
||||||
|
int KSEEKINGPARTNERFLAGSEEKING = 1;
|
||||||
|
int KSEEKINGPARTNERFLAGUNKNOWN = 0;
|
||||||
|
int KSENDSTATUSFAILED = 0;
|
||||||
|
int KSENDSTATUSSENDING = 1;
|
||||||
|
int KSENDSTATUSSUCCESS = 2;
|
||||||
|
int KSENDSTATUSSUCCESSNOSEQ = 3;
|
||||||
|
int KSENDTYPEDROPPED = 6;
|
||||||
|
int KSENDTYPELOCAL = 3;
|
||||||
|
int KSENDTYPEOTHERDEVICE = 2;
|
||||||
|
int KSENDTYPERECV = 0;
|
||||||
|
int KSENDTYPESELF = 1;
|
||||||
|
int KSENDTYPESELFFORWARD = 4;
|
||||||
|
int KSENDTYPESELFMULTIFORWARD = 5;
|
||||||
|
int KSESSIONTYPEADDRESSBOOK = 5;
|
||||||
|
int KSESSIONTYPEC2C = 1;
|
||||||
|
int KSESSIONTYPEDISC = 3;
|
||||||
|
int KSESSIONTYPEFAV = 41;
|
||||||
|
int KSESSIONTYPEGROUP = 2;
|
||||||
|
int KSESSIONTYPEGROUPBLESS = 52;
|
||||||
|
int KSESSIONTYPEGUILD = 4;
|
||||||
|
int KSESSIONTYPEGUILDMETA = 16;
|
||||||
|
int KSESSIONTYPENEARBYPRO = 54;
|
||||||
|
int KSESSIONTYPEQQNOTIFY = 51;
|
||||||
|
int KSESSIONTYPERELATEACCOUNT = 50;
|
||||||
|
int KSESSIONTYPESERVICEASSISTANT = 19;
|
||||||
|
int KSESSIONTYPESUBSCRIBEFOLDER = 30;
|
||||||
|
int KSESSIONTYPETYPEBUDDYNOTIFY = 7;
|
||||||
|
int KSESSIONTYPETYPEGROUPHELPER = 9;
|
||||||
|
int KSESSIONTYPETYPEGROUPNOTIFY = 8;
|
||||||
|
int KSESSIONTYPEUNKNOWN = 0;
|
||||||
|
int KSESSIONTYPEWEIYUN = 40;
|
||||||
|
int KSPECIALCAREMSGTYPEINMSGBOX = 1006;
|
||||||
|
int KSPECIFIEDREDENVELOPEATMEMSGTYPEINMSGBOX = 1004;
|
||||||
|
int KSPECIFIEDREDENVELOPEATONEMSGTYPEINMSGBOX = 1003;
|
||||||
|
int KTENCENTDOCTYPEADDON = 110;
|
||||||
|
int KTENCENTDOCTYPEDOC = 0;
|
||||||
|
int KTENCENTDOCTYPEDRAWING = 89;
|
||||||
|
int KTENCENTDOCTYPEDRIVE = 101;
|
||||||
|
int KTENCENTDOCTYPEFILE = 100;
|
||||||
|
int KTENCENTDOCTYPEFLOWCHART = 91;
|
||||||
|
int KTENCENTDOCTYPEFOLDER = 3;
|
||||||
|
int KTENCENTDOCTYPEFORM = 2;
|
||||||
|
int KTENCENTDOCTYPEMIND = 90;
|
||||||
|
int KTENCENTDOCTYPENOTES = 5;
|
||||||
|
int KTENCENTDOCTYPEPDF = 6;
|
||||||
|
int KTENCENTDOCTYPEPROGRAM = 7;
|
||||||
|
int KTENCENTDOCTYPESHEET = 1;
|
||||||
|
int KTENCENTDOCTYPESLIDE = 4;
|
||||||
|
int KTENCENTDOCTYPESMARTCANVAS = 8;
|
||||||
|
int KTENCENTDOCTYPESMARTSHEET = 9;
|
||||||
|
int KTENCENTDOCTYPESPEECH = 102;
|
||||||
|
int KTENCENTDOCTYPEUNKNOWN = 10;
|
||||||
|
int KTOFURECORDMSG = 23;
|
||||||
|
int KTOPMSGTYPETASK = 1;
|
||||||
|
int KTOPMSGTYPEUNKNOWN = 0;
|
||||||
|
int KTRIGGERTYPEAUTO = 1;
|
||||||
|
int KTRIGGERTYPEMANUAL = 0;
|
||||||
|
int KUNKNOWN = 0;
|
||||||
|
int KUNKNOWNTYPEINMSGBOX = 0;
|
||||||
|
int KUNREADCNTUPTYPEALLDIRECTSESSION = 4;
|
||||||
|
int KUNREADCNTUPTYPEALLFEEDSINGUILD = 6;
|
||||||
|
int KUNREADCNTUPTYPEALLGUILD = 3;
|
||||||
|
int KUNREADCNTUPTYPECATEGORY = 5;
|
||||||
|
int KUNREADCNTUPTYPECHANNEL = 1;
|
||||||
|
int KUNREADCNTUPTYPECONTACT = 0;
|
||||||
|
int KUNREADCNTUPTYPEGUILD = 2;
|
||||||
|
int KUNREADCNTUPTYPEGUILDGROUP = 7;
|
||||||
|
int KUNREADSHOWTTYPEGRAYPOINT = 2;
|
||||||
|
int KUNREADSHOWTYPEREDPOINT = 1;
|
||||||
|
int KUNREADSHOWTYPESMALLGRAYPOINT = 4;
|
||||||
|
int KUNREADSHOWTYPESMALLREDPOINT = 3;
|
||||||
|
int KUNREADSHOWTYPEUNKNOWN = 0;
|
||||||
|
int KVASGIFTCOINTYPECOIN = 0;
|
||||||
|
int KVASGIFTCOINTYPEMARKETCOIN = 1;
|
||||||
|
int KYOLOGAMERESULTMSG = 18;
|
||||||
|
int PIC_800_RECOMMENDED = 7;
|
||||||
|
int PIC_AIGC_EMOJI = 14;
|
||||||
|
int PIC_ALBUM_GIF = 11;
|
||||||
|
int PIC_COMMERCIAL_ADVERTISING = 9;
|
||||||
|
int PIC_FIND = 10;
|
||||||
|
int PIC_HOT = 2;
|
||||||
|
int PIC_HOT_EMOJI = 13;
|
||||||
|
int PIC_NORMAL = 0;
|
||||||
|
int PIC_PK = 3;
|
||||||
|
int PIC_QQZONE = 5;
|
||||||
|
int PIC_SELFIE_GIF = 8;
|
||||||
|
int PIC_SEND_FROM_TAB_SEARCH_BOX = 12;
|
||||||
|
int PIC_USER = 1;
|
||||||
|
int PIC_WISDOM_FIGURE = 4;
|
||||||
|
int REPLYORIGINALMSGSTATEHASRECALL = 1;
|
||||||
|
int REPLYORIGINALMSGSTATEUNKNOWN = 0;
|
||||||
|
int SHARELOCATIONELEMSUBTYPENORMAL = 1;
|
||||||
|
int SHARELOCATIONELEMSUBTYPEUNKNOWN = 0;
|
||||||
|
int TEXTELEMENTSUBTYPELINK = 1;
|
||||||
|
int TEXTELEMENTSUBTYPETENCENTDOC = 2;
|
||||||
|
int TEXTELEMENTSUBTYPEUNKNOWN = 0;
|
||||||
|
```
|
24
docs/develop/参与开发.md
Normal file
24
docs/develop/参与开发.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# 前排提示
|
||||||
|
由于Core未处于开源,非组织人员无法参与Core开发,此处为Core开发提示
|
||||||
|
|
||||||
|
# 准备工具
|
||||||
|
frida ida-pro jadx x64dbg ce 内部调试脚本
|
||||||
|
|
||||||
|
## ida-pro
|
||||||
|
1. 用于快速分析入参和返回类型
|
||||||
|
2. 通过静态QLog推测语义
|
||||||
|
3. 提取Listener与Service (常用)
|
||||||
|
## frida
|
||||||
|
1. 用于动态获取QLog推测语义
|
||||||
|
2. 捕捉Native函数 实际入参与数据 分析中间流程
|
||||||
|
|
||||||
|
## jadx
|
||||||
|
1. 通过其它平台实现 静态获取QLog推测语义
|
||||||
|
2. 提供部分未调用代码 参考
|
||||||
|
|
||||||
|
## x64dbg
|
||||||
|
1. 验证IDA的Hook点
|
||||||
|
|
||||||
|
## 内部脚本
|
||||||
|
1. 提取Listener与Service (不调用无类型 不推荐)
|
||||||
|
2. 获取NT调用流程
|
@@ -1,70 +0,0 @@
|
|||||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
|
||||||
import _import from "eslint-plugin-import";
|
|
||||||
import { fixupPluginRules } from "@eslint/compat";
|
|
||||||
import globals from "globals";
|
|
||||||
import tsParser from "@typescript-eslint/parser";
|
|
||||||
import path from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
import js from "@eslint/js";
|
|
||||||
import { FlatCompat } from "@eslint/eslintrc";
|
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url);
|
|
||||||
const dirname = path.dirname(filename);
|
|
||||||
const compat = new FlatCompat({
|
|
||||||
baseDirectory: dirname,
|
|
||||||
recommendedConfig: js.configs.recommended,
|
|
||||||
allConfig: js.configs.all
|
|
||||||
});
|
|
||||||
|
|
||||||
export default [{
|
|
||||||
ignores: ["src/core/proto/"],
|
|
||||||
}, ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"), {
|
|
||||||
plugins: {
|
|
||||||
"@typescript-eslint": typescriptEslint,
|
|
||||||
import: fixupPluginRules(_import),
|
|
||||||
},
|
|
||||||
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.browser,
|
|
||||||
...globals.node,
|
|
||||||
},
|
|
||||||
|
|
||||||
parser: tsParser,
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
sourceType: "module",
|
|
||||||
},
|
|
||||||
|
|
||||||
settings: {
|
|
||||||
"import/parsers": {
|
|
||||||
"@typescript-eslint/parser": [".ts"],
|
|
||||||
},
|
|
||||||
|
|
||||||
"import/resolver": {
|
|
||||||
typescript: {
|
|
||||||
alwaysTryTypes: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
indent: ["error", 4],
|
|
||||||
semi: ["error", "always"],
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"no-async-promise-executor": "off",
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"object-curly-spacing": ["error", "always"],
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
files: ["**/.eslintrc.{js,cjs}"],
|
|
||||||
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.node,
|
|
||||||
},
|
|
||||||
ecmaVersion: 5,
|
|
||||||
sourceType: "commonjs",
|
|
||||||
},
|
|
||||||
}];
|
|
BIN
external/LiteLoaderWrapper.zip
vendored
BIN
external/LiteLoaderWrapper.zip
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,32 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001
|
|
||||||
set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
|
|
||||||
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
|
|
||||||
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
|
|
||||||
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
|
|
||||||
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
|
|
||||||
:loop_read
|
|
||||||
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
|
||||||
set RetString=%%b
|
|
||||||
goto :napcat_boot
|
|
||||||
)
|
|
||||||
|
|
||||||
:napcat_boot
|
|
||||||
for %%a in ("%RetString%") do (
|
|
||||||
set "pathWithoutUninstall=%%~dpa"
|
|
||||||
)
|
|
||||||
|
|
||||||
SET QQPath=%pathWithoutUninstall%QQ.exe
|
|
||||||
|
|
||||||
if not exist "%QQpath%" (
|
|
||||||
echo provided QQ path is invalid: %QQpath%
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
|
||||||
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
|
|
||||||
|
|
||||||
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1
|
|
||||||
|
|
||||||
pause
|
|
@@ -1,33 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001
|
|
||||||
set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
|
|
||||||
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
|
|
||||||
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
|
|
||||||
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
|
|
||||||
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
|
|
||||||
:loop_read
|
|
||||||
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
|
||||||
set RetString=%%b
|
|
||||||
goto :napcat_boot
|
|
||||||
)
|
|
||||||
|
|
||||||
:napcat_boot
|
|
||||||
for %%a in ("%RetString%") do (
|
|
||||||
set "pathWithoutUninstall=%%~dpa"
|
|
||||||
)
|
|
||||||
|
|
||||||
SET QQPath=%pathWithoutUninstall%QQ.exe
|
|
||||||
|
|
||||||
if not exist "%QQpath%" (
|
|
||||||
echo provided QQ path is invalid: %QQpath%
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
|
||||||
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
|
|
||||||
|
|
||||||
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1
|
|
||||||
|
|
||||||
REM "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" 123456
|
|
||||||
|
|
||||||
pause
|
|
@@ -1,40 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorLevel% == 0 (
|
|
||||||
echo Administrator mode detected.
|
|
||||||
) else (
|
|
||||||
echo Please run this script in administrator mode.
|
|
||||||
powershell -Command "Start-Process 'cmd.exe' -ArgumentList '/c cd /d \"%cd%\" && \"%~f0\" %1' -Verb runAs"
|
|
||||||
exit
|
|
||||||
)
|
|
||||||
|
|
||||||
set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
|
|
||||||
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
|
|
||||||
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
|
|
||||||
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
|
|
||||||
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
|
|
||||||
:loop_read
|
|
||||||
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
|
||||||
set RetString=%%b
|
|
||||||
goto :napcat_boot
|
|
||||||
)
|
|
||||||
|
|
||||||
:napcat_boot
|
|
||||||
for %%a in ("%RetString%") do (
|
|
||||||
set "pathWithoutUninstall=%%~dpa"
|
|
||||||
)
|
|
||||||
|
|
||||||
SET QQPath=%pathWithoutUninstall%QQ.exe
|
|
||||||
|
|
||||||
if not exist "%QQpath%" (
|
|
||||||
echo provided QQ path is invalid: %QQpath%
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
|
||||||
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
|
|
||||||
|
|
||||||
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1
|
|
||||||
|
|
||||||
REM "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" 123456
|
|
@@ -1,39 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001
|
|
||||||
net session >nul 2>&1
|
|
||||||
if %errorLevel% == 0 (
|
|
||||||
echo Administrator mode detected.
|
|
||||||
) else (
|
|
||||||
echo Please run this script in administrator mode.
|
|
||||||
powershell -Command "Start-Process 'wt.exe' -ArgumentList 'cmd /c cd /d \"%cd%\" && \"%~f0\" %1' -Verb runAs"
|
|
||||||
exit
|
|
||||||
)
|
|
||||||
|
|
||||||
set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
|
|
||||||
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
|
|
||||||
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
|
|
||||||
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
|
|
||||||
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
|
|
||||||
:loop_read
|
|
||||||
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
|
||||||
set RetString=%%b
|
|
||||||
goto :napcat_boot
|
|
||||||
)
|
|
||||||
|
|
||||||
:napcat_boot
|
|
||||||
for %%a in ("%RetString%") do (
|
|
||||||
set "pathWithoutUninstall=%%~dpa"
|
|
||||||
)
|
|
||||||
|
|
||||||
SET QQPath=%pathWithoutUninstall%QQ.exe
|
|
||||||
|
|
||||||
if not exist "%QQpath%" (
|
|
||||||
echo provided QQ path is invalid: %QQpath%
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
|
||||||
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
|
|
||||||
|
|
||||||
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1
|
|
@@ -1,5 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const CurrentPath = path.dirname(__filename);
|
|
||||||
(async () => {
|
|
||||||
await import("file://" + path.join(CurrentPath, './napcat/napcat.mjs'));
|
|
||||||
})();
|
|
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "qq-chat",
|
|
||||||
"version": "9.9.16-29456",
|
|
||||||
"verHash": "dd395162",
|
|
||||||
"linuxVersion": "3.2.13-29456",
|
|
||||||
"linuxVerHash": "e379390a",
|
|
||||||
"type": "module",
|
|
||||||
"private": true,
|
|
||||||
"description": "QQ",
|
|
||||||
"productName": "QQ",
|
|
||||||
"author": {
|
|
||||||
"name": "Tencent",
|
|
||||||
"email": "QQ-Team@tencent.com"
|
|
||||||
},
|
|
||||||
"homepage": "https://im.qq.com",
|
|
||||||
"sideEffects": true,
|
|
||||||
"bin": {
|
|
||||||
"qd": "externals/devtools/cli/index.js"
|
|
||||||
},
|
|
||||||
"main": "./loadNapCat.js",
|
|
||||||
"buildVersion": "29456",
|
|
||||||
"isPureShell": true,
|
|
||||||
"isByteCodeShell": true,
|
|
||||||
"platform": "win32",
|
|
||||||
"eleArch": "x64"
|
|
||||||
}
|
|
@@ -1,4 +0,0 @@
|
|||||||
@echo off
|
|
||||||
REM ./launcher.bat 123456
|
|
||||||
REM ./launcher-win10.bat 123456
|
|
||||||
REM 带有REM的为注释 删掉你需要的系统的那行REM这三个单词 修改QQ本脚本启动即可
|
|
BIN
logo.png
BIN
logo.png
Binary file not shown.
Before Width: | Height: | Size: 335 KiB After Width: | Height: | Size: 208 KiB |
@@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"manifest_version": 4,
|
|
||||||
"type": "extension",
|
|
||||||
"name": "NapCatQQ",
|
|
||||||
"slug": "NapCat.Framework",
|
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
|
||||||
"version": "4.1.0",
|
|
||||||
"icon": "./logo.png",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "MliKiowa",
|
|
||||||
"link": "https://github.com/MliKiowa"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Young",
|
|
||||||
"link": "https://github.com/Wesley-Young"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"repo": "NapNeko/NapCatQQ",
|
|
||||||
"branch": "main"
|
|
||||||
},
|
|
||||||
"platform": [
|
|
||||||
"win32",
|
|
||||||
"linux",
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"injects": {
|
|
||||||
"renderer": "./renderer.js",
|
|
||||||
"main": "./liteloader.cjs",
|
|
||||||
"preload": "./preload.cjs"
|
|
||||||
}
|
|
||||||
}
|
|
24
napcat.webui/.gitignore
vendored
24
napcat.webui/.gitignore
vendored
@@ -1,24 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
3
napcat.webui/.vscode/extensions.json
vendored
3
napcat.webui/.vscode/extensions.json
vendored
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": ["Vue.volar"]
|
|
||||||
}
|
|
@@ -1,5 +0,0 @@
|
|||||||
# Vue 3 + TypeScript + Vite
|
|
||||||
|
|
||||||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
|
||||||
|
|
||||||
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
|
@@ -1,52 +0,0 @@
|
|||||||
import globals from 'globals';
|
|
||||||
import ts from 'typescript-eslint';
|
|
||||||
import vue from 'eslint-plugin-vue';
|
|
||||||
import prettier from 'eslint-plugin-prettier/recommended';
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.browser,
|
|
||||||
...globals.node,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...ts.configs.recommended,
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-explicit-any': 'warn',
|
|
||||||
'@typescript-eslint/no-var-requires': 'warn',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...vue.configs['flat/base'],
|
|
||||||
{
|
|
||||||
files: ['*.vue', '**/*.vue'],
|
|
||||||
languageOptions: {
|
|
||||||
parserOptions: {
|
|
||||||
parser: ts.parser,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
indent: ['error', 4],
|
|
||||||
semi: ['error', 'always'],
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'warn',
|
|
||||||
'@typescript-eslint/no-unused-vars': 'warn',
|
|
||||||
'@typescript-eslint/no-var-requires': 'warn',
|
|
||||||
'object-curly-spacing': ['error', 'always'],
|
|
||||||
'vue/v-for-delimiter-style': ['error', 'in'],
|
|
||||||
'vue/require-name-property': 'warn',
|
|
||||||
'vue/prefer-true-attribute-shorthand': 'warn',
|
|
||||||
'prefer-arrow-callback': 'warn',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
prettier,
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
'prettier/prettier': 'warn',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
@@ -1,13 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>NapCat WebUI</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="./src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "napcat.webui",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"webui:lint": "eslint . --fix",
|
|
||||||
"webui:dev": "vite",
|
|
||||||
"webui:build": "vue-tsc -b && vite build",
|
|
||||||
"webui:preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
|
||||||
"qrcode": "^1.5.4",
|
|
||||||
"tdesign-vue-next": "^1.10.3",
|
|
||||||
"vue": "^3.5.12",
|
|
||||||
"vue-router": "^4.4.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
|
||||||
"@eslint/js": "^9.14.0",
|
|
||||||
"@types/qrcode": "^1.5.5",
|
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
|
||||||
"eslint-config-prettier": "^9.1.0",
|
|
||||||
"eslint-plugin-vue": "^9.31.0",
|
|
||||||
"globals": "^15.12.0",
|
|
||||||
"typescript": "~5.6.2",
|
|
||||||
"vite": "^5.4.10",
|
|
||||||
"vue-tsc": "^2.1.8"
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 335 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,7 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div id="app">
|
|
||||||
<router-view />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 335 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
|
Before Width: | Height: | Size: 496 B |
@@ -1,185 +0,0 @@
|
|||||||
import { OneBotConfig } from '../../../src/onebot/config/config';
|
|
||||||
|
|
||||||
export class QQLoginManager {
|
|
||||||
private retCredential: string;
|
|
||||||
private readonly apiPrefix: string;
|
|
||||||
|
|
||||||
//调试时http://127.0.0.1:6099/api 打包时 ../api
|
|
||||||
constructor(retCredential: string, apiPrefix: string = '../api') {
|
|
||||||
this.retCredential = retCredential;
|
|
||||||
this.apiPrefix = apiPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
public async GetOB11Config(): Promise<OneBotConfig> {
|
|
||||||
try {
|
|
||||||
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/GetConfig`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (ConfigResponse.status == 200) {
|
|
||||||
const ConfigResponseJson = await ConfigResponse.json();
|
|
||||||
if (ConfigResponseJson.code == 0) {
|
|
||||||
return ConfigResponseJson?.data as OneBotConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting OB11 config:', error);
|
|
||||||
}
|
|
||||||
return {} as OneBotConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async SetOB11Config(config: OneBotConfig): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/SetConfig`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ config: JSON.stringify(config) }),
|
|
||||||
});
|
|
||||||
if (ConfigResponse.status == 200) {
|
|
||||||
const ConfigResponseJson = await ConfigResponse.json();
|
|
||||||
if (ConfigResponseJson.code == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error setting OB11 config:', error);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async checkQQLoginStatus(): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/CheckLoginStatus`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (QQLoginResponse.status == 200) {
|
|
||||||
const QQLoginResponseJson = await QQLoginResponse.json();
|
|
||||||
if (QQLoginResponseJson.code == 0) {
|
|
||||||
return QQLoginResponseJson.data.isLogin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error checking QQ login status:', error);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async checkWebUiLogined(): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
const LoginResponse = await fetch(`${this.apiPrefix}/auth/check`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (LoginResponse.status == 200) {
|
|
||||||
const LoginResponseJson = await LoginResponse.json();
|
|
||||||
if (LoginResponseJson.code == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error checking web UI login status:', error);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async loginWithToken(token: string): Promise<string | null> {
|
|
||||||
try {
|
|
||||||
const loginResponse = await fetch(`${this.apiPrefix}/auth/login`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ token: token }),
|
|
||||||
});
|
|
||||||
const loginResponseJson = await loginResponse.json();
|
|
||||||
const retCode = loginResponseJson.code;
|
|
||||||
if (retCode === 0) {
|
|
||||||
this.retCredential = loginResponseJson.data.Credential;
|
|
||||||
return this.retCredential;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error logging in with token:', error);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getQQLoginQrcode(): Promise<string> {
|
|
||||||
try {
|
|
||||||
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQQLoginQrcode`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (QQLoginResponse.status == 200) {
|
|
||||||
const QQLoginResponseJson = await QQLoginResponse.json();
|
|
||||||
if (QQLoginResponseJson.code == 0) {
|
|
||||||
return QQLoginResponseJson.data.qrcode || '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting QQ login QR code:', error);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getQQQuickLoginList(): Promise<string[]> {
|
|
||||||
try {
|
|
||||||
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQuickLoginList`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (QQLoginResponse.status == 200) {
|
|
||||||
const QQLoginResponseJson = await QQLoginResponse.json();
|
|
||||||
if (QQLoginResponseJson.code == 0) {
|
|
||||||
return QQLoginResponseJson.data || [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting QQ quick login list:', error);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public async setQuickLogin(uin: string): Promise<{ result: boolean; errMsg: string }> {
|
|
||||||
try {
|
|
||||||
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/SetQuickLogin`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ uin: uin }),
|
|
||||||
});
|
|
||||||
if (QQLoginResponse.status == 200) {
|
|
||||||
const QQLoginResponseJson = await QQLoginResponse.json();
|
|
||||||
if (QQLoginResponseJson.code == 0) {
|
|
||||||
return { result: true, errMsg: '' };
|
|
||||||
} else {
|
|
||||||
return { result: false, errMsg: QQLoginResponseJson.message };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error setting quick login:', error);
|
|
||||||
}
|
|
||||||
return { result: false, errMsg: '接口异常' };
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,55 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="dashboard-container">
|
|
||||||
<SidebarMenu :menu-items="menuItems" class="sidebar-menu" />
|
|
||||||
<div class="content">
|
|
||||||
<router-view />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import SidebarMenu from './webui/Nav.vue';
|
|
||||||
|
|
||||||
interface MenuItem {
|
|
||||||
value: string;
|
|
||||||
icon: string;
|
|
||||||
label: string;
|
|
||||||
route: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const menuItems = ref<MenuItem[]>([
|
|
||||||
{ value: 'item1', icon: 'dashboard', label: '基础信息', route: '/dashboard/basic-info' },
|
|
||||||
{ value: 'item3', icon: 'wifi-1', label: '网络配置', route: '/dashboard/network-config' },
|
|
||||||
{ value: 'item4', icon: 'setting', label: '其余配置', route: '/dashboard/other-config' },
|
|
||||||
{ value: 'item5', icon: 'system-log', label: '日志查看', route: '/dashboard/log-view' },
|
|
||||||
{ value: 'item6', icon: 'info-circle', label: '关于我们', route: '/dashboard/about-us' },
|
|
||||||
]);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.dashboard-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-menu {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex: 1;
|
|
||||||
/* padding: 20px; */
|
|
||||||
overflow: auto;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.content {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,167 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="login-container">
|
|
||||||
<h2 class="sotheby-font">QQ Login</h2>
|
|
||||||
<div class="login-methods">
|
|
||||||
<t-button
|
|
||||||
id="quick-login"
|
|
||||||
class="login-method"
|
|
||||||
:class="{ active: loginMethod === 'quick' }"
|
|
||||||
@click="loginMethod = 'quick'"
|
|
||||||
>Quick Login</t-button
|
|
||||||
>
|
|
||||||
<t-button
|
|
||||||
id="qrcode-login"
|
|
||||||
class="login-method"
|
|
||||||
:class="{ active: loginMethod === 'qrcode' }"
|
|
||||||
@click="loginMethod = 'qrcode'"
|
|
||||||
>QR Code</t-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div v-show="loginMethod === 'quick'" id="quick-login-dropdown" class="login-form">
|
|
||||||
<t-select
|
|
||||||
id="quick-login-select"
|
|
||||||
v-model="selectedAccount"
|
|
||||||
placeholder="Select Account"
|
|
||||||
@change="selectAccount"
|
|
||||||
>
|
|
||||||
<t-option v-for="account in quickLoginList" :key="account" :value="account">{{ account }}</t-option>
|
|
||||||
</t-select>
|
|
||||||
</div>
|
|
||||||
<div v-show="loginMethod === 'qrcode'" id="qrcode" class="qrcode">
|
|
||||||
<canvas ref="qrcodeCanvas"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, onMounted } from 'vue';
|
|
||||||
import * as QRCode from 'qrcode';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
|
||||||
import { QQLoginManager } from '@/backend/shell';
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const loginMethod = ref<'quick' | 'qrcode'>('quick');
|
|
||||||
const quickLoginList = ref<string[]>([]);
|
|
||||||
const selectedAccount = ref<string>('');
|
|
||||||
const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
|
|
||||||
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || '');
|
|
||||||
let heartBeatTimer: number | null = null;
|
|
||||||
|
|
||||||
const selectAccount = async (accountName: string): Promise<void> => {
|
|
||||||
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
|
|
||||||
if (result) {
|
|
||||||
await MessagePlugin.success('登录成功即将跳转');
|
|
||||||
await router.push({ path: '/dashboard/basic-info' });
|
|
||||||
} else {
|
|
||||||
await MessagePlugin.error('登录失败,' + errMsg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateQrCode = (data: string, canvas: HTMLCanvasElement | null): void => {
|
|
||||||
if (!canvas) {
|
|
||||||
console.error('Canvas element not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QRCode.toCanvas(canvas, data, function (error: Error | null | undefined) {
|
|
||||||
if (error) {
|
|
||||||
console.error('Error generating QR Code:', error);
|
|
||||||
} else {
|
|
||||||
console.log('QR Code generated!');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const HeartBeat = async (): Promise<void> => {
|
|
||||||
const isLogined = await qqLoginManager.checkQQLoginStatus();
|
|
||||||
if (isLogined) {
|
|
||||||
if (heartBeatTimer) {
|
|
||||||
clearInterval(heartBeatTimer);
|
|
||||||
}
|
|
||||||
await router.push({ path: '/dashboard/basic-info' });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const InitPages = async (): Promise<void> => {
|
|
||||||
quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
|
|
||||||
const qrcodeData = await qqLoginManager.getQQLoginQrcode();
|
|
||||||
generateQrCode(qrcodeData, qrcodeCanvas.value);
|
|
||||||
heartBeatTimer = window.setInterval(HeartBeat, 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
InitPages();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.login-container {
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: white;
|
|
||||||
max-width: 400px;
|
|
||||||
min-width: 300px;
|
|
||||||
position: relative;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.login-container {
|
|
||||||
width: 90%;
|
|
||||||
min-width: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-methods {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-method {
|
|
||||||
padding: 10px 15px;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-method.active {
|
|
||||||
background-color: #e6f0ff;
|
|
||||||
color: #007bff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form,
|
|
||||||
.qrcode {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qrcode {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 15px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sotheby-font {
|
|
||||||
font-family: Sotheby, Helvetica, monospace;
|
|
||||||
font-size: 3.125rem;
|
|
||||||
line-height: 1.2;
|
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: #888;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,151 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="login-container">
|
|
||||||
<h2 class="sotheby-font">WebUi Login</h2>
|
|
||||||
<t-form ref="form" :data="formData" colon :label-width="0" @submit="onSubmit">
|
|
||||||
<t-form-item name="password">
|
|
||||||
<t-input v-model="formData.token" type="password" clearable placeholder="请输入Token">
|
|
||||||
<template #prefix-icon>
|
|
||||||
<lock-on-icon />
|
|
||||||
</template>
|
|
||||||
</t-input>
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item>
|
|
||||||
<t-button theme="primary" type="submit" block>登录</t-button>
|
|
||||||
</t-form-item>
|
|
||||||
</t-form>
|
|
||||||
</div>
|
|
||||||
<div class="footer">Power By NapCat.WebUi</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import '../css/style.css';
|
|
||||||
import '../css/font.css';
|
|
||||||
import { reactive, onMounted } from 'vue';
|
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
|
||||||
import { LockOnIcon } from 'tdesign-icons-vue-next';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { QQLoginManager } from '@/backend/shell';
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
interface FormData {
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData: FormData = reactive({
|
|
||||||
token: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleLoginSuccess = async (credential: string) => {
|
|
||||||
localStorage.setItem('auth', credential);
|
|
||||||
await checkLoginStatus();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLoginFailure = (message: string) => {
|
|
||||||
MessagePlugin.error(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkLoginStatus = async () => {
|
|
||||||
const storedCredential = localStorage.getItem('auth');
|
|
||||||
if (!storedCredential) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
|
||||||
const isWenUiLoggedIn = await loginManager.checkWebUiLogined();
|
|
||||||
console.log('isWenUiLoggedIn', isWenUiLoggedIn);
|
|
||||||
if (!isWenUiLoggedIn) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isQQLoggedIn = await loginManager.checkQQLoginStatus();
|
|
||||||
if (isQQLoggedIn) {
|
|
||||||
await router.push({ path: '/dashboard/basic-info' });
|
|
||||||
} else {
|
|
||||||
await router.push({ path: '/qqlogin' });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const loginWithToken = async (token: string) => {
|
|
||||||
const loginManager = new QQLoginManager('');
|
|
||||||
const credential = await loginManager.loginWithToken(token);
|
|
||||||
if (credential) {
|
|
||||||
await handleLoginSuccess(credential);
|
|
||||||
} else {
|
|
||||||
handleLoginFailure('登录失败,请检查Token');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const url = new URL(window.location.href);
|
|
||||||
const token = url.searchParams.get('token');
|
|
||||||
if (token) {
|
|
||||||
loginWithToken(token);
|
|
||||||
}
|
|
||||||
checkLoginStatus();
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = async ({ validateResult }: { validateResult: boolean }) => {
|
|
||||||
if (validateResult) {
|
|
||||||
await loginWithToken(formData.token);
|
|
||||||
} else {
|
|
||||||
handleLoginFailure('请填写Token');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.login-container {
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: white;
|
|
||||||
max-width: 400px;
|
|
||||||
min-width: 300px;
|
|
||||||
position: relative;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.login-container {
|
|
||||||
width: 90%;
|
|
||||||
min-width: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tdesign-demo-block-column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
row-gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tdesign-demo-block-column-large {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
row-gap: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tdesign-demo-block-row {
|
|
||||||
display: flex;
|
|
||||||
column-gap: 16px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sotheby-font {
|
|
||||||
font-family: Sotheby, Helvetica, monospace;
|
|
||||||
font-size: 3.125rem;
|
|
||||||
line-height: 1.2;
|
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: #888;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,71 +0,0 @@
|
|||||||
<template>
|
|
||||||
<t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu">
|
|
||||||
<template #logo> </template>
|
|
||||||
<router-link v-for="item in menuItems" :key="item.value" :to="item.route">
|
|
||||||
<t-menu-item :value="item.value" :disabled="item.disabled" class="menu-item">
|
|
||||||
<template #icon>
|
|
||||||
<t-icon :name="item.icon" />
|
|
||||||
</template>
|
|
||||||
{{ item.label }}
|
|
||||||
</t-menu-item>
|
|
||||||
</router-link>
|
|
||||||
<template #operations>
|
|
||||||
<t-button class="t-demo-collapse-btn" variant="text" shape="square" @click="changeCollapsed">
|
|
||||||
<template #icon><t-icon :name="iconName" /></template>
|
|
||||||
</t-button>
|
|
||||||
</template>
|
|
||||||
</t-menu>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, defineProps } from 'vue';
|
|
||||||
|
|
||||||
type MenuItem = {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
route: string;
|
|
||||||
icon?: string;
|
|
||||||
disabled?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
menuItems: MenuItem[];
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const collapsed = ref<boolean>(localStorage.getItem('sidebar-collapsed') === 'true');
|
|
||||||
const iconName = ref<string>(collapsed.value ? 'menu-unfold' : 'menu-fold');
|
|
||||||
|
|
||||||
const changeCollapsed = (): void => {
|
|
||||||
collapsed.value = !collapsed.value;
|
|
||||||
iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold';
|
|
||||||
localStorage.setItem('sidebar-collapsed', collapsed.value.toString());
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.sidebar-menu {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 200px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.sidebar-menu {
|
|
||||||
width: 100px; /* 移动端侧边栏宽度 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-text {
|
|
||||||
display: block;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-item {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,6 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Sotheby';
|
|
||||||
src: url('../assets/Sotheby.ttf') format('truetype');
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
@@ -1,84 +0,0 @@
|
|||||||
:root {
|
|
||||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
color-scheme: light dark;
|
|
||||||
color: rgba(255, 255, 255, 0.87);
|
|
||||||
background-color: #242424;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #646cff;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #535bf2;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3.2em;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
padding: 0.6em 1.2em;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: inherit;
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: border-color 0.25s;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
border-color: #646cff;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:focus,
|
|
||||||
button:focus-visible {
|
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
:root {
|
|
||||||
color: #213547;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #747bff;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,62 +0,0 @@
|
|||||||
import { createApp } from 'vue';
|
|
||||||
import App from './App.vue';
|
|
||||||
import {
|
|
||||||
Button as TButton,
|
|
||||||
Input as TInput,
|
|
||||||
Form as TForm,
|
|
||||||
FormItem as TFormItem,
|
|
||||||
Select as TSelect,
|
|
||||||
Option as TOption,
|
|
||||||
Menu as TMenu,
|
|
||||||
MenuItem as TMenuItem,
|
|
||||||
Icon as TIcon,
|
|
||||||
Submenu as TSubmenu,
|
|
||||||
Col as TCol,
|
|
||||||
Row as TRow,
|
|
||||||
Card as TCard,
|
|
||||||
Divider as TDivider,
|
|
||||||
Link as TLink,
|
|
||||||
List as TList,
|
|
||||||
Alert as TAlert,
|
|
||||||
Tag as TTag,
|
|
||||||
ListItem as TListItem,
|
|
||||||
Tabs as TTabs,
|
|
||||||
TabPanel as TTabPanel,
|
|
||||||
Space as TSpace,
|
|
||||||
Checkbox as TCheckbox,
|
|
||||||
Popup as TPopup,
|
|
||||||
Dialog as TDialog,
|
|
||||||
Switch as TSwitch,
|
|
||||||
} from 'tdesign-vue-next';
|
|
||||||
import { router } from './router';
|
|
||||||
import 'tdesign-vue-next/es/style/index.css';
|
|
||||||
|
|
||||||
const app = createApp(App);
|
|
||||||
app.use(router);
|
|
||||||
app.use(TButton);
|
|
||||||
app.use(TInput);
|
|
||||||
app.use(TForm);
|
|
||||||
app.use(TFormItem);
|
|
||||||
app.use(TSelect);
|
|
||||||
app.use(TOption);
|
|
||||||
app.use(TMenu);
|
|
||||||
app.use(TMenuItem);
|
|
||||||
app.use(TIcon);
|
|
||||||
app.use(TSubmenu);
|
|
||||||
app.use(TCol);
|
|
||||||
app.use(TRow);
|
|
||||||
app.use(TCard);
|
|
||||||
app.use(TDivider);
|
|
||||||
app.use(TLink);
|
|
||||||
app.use(TList);
|
|
||||||
app.use(TAlert);
|
|
||||||
app.use(TTag);
|
|
||||||
app.use(TListItem);
|
|
||||||
app.use(TTabs);
|
|
||||||
app.use(TTabPanel);
|
|
||||||
app.use(TSpace);
|
|
||||||
app.use(TCheckbox);
|
|
||||||
app.use(TPopup);
|
|
||||||
app.use(TDialog);
|
|
||||||
app.use(TSwitch);
|
|
||||||
app.mount('#app');
|
|
@@ -1,63 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="about-us">
|
|
||||||
<div>
|
|
||||||
<t-divider content="面板关于信息" align="left" />
|
|
||||||
<t-alert theme="success" message="NapCat.WebUi is running" />
|
|
||||||
<t-list class="list">
|
|
||||||
<t-list-item class="list-item">
|
|
||||||
<span class="item-label">开发人员:</span>
|
|
||||||
<span class="item-content">
|
|
||||||
<t-link href="mailto:nanaeonn@outlook.com">Mlikiowa</t-link>
|
|
||||||
</span>
|
|
||||||
</t-list-item>
|
|
||||||
<t-list-item class="list-item">
|
|
||||||
<span class="item-label">版本信息:</span>
|
|
||||||
<span class="item-content">
|
|
||||||
<t-tag class="tag-item" theme="success"> WebUi: 1.0.0 </t-tag>
|
|
||||||
<t-tag class="tag-item" theme="success"> NapCat: 4.?.? </t-tag>
|
|
||||||
<t-tag class="tag-item" theme="success"> Tdesign: 1.10.3 </t-tag>
|
|
||||||
</span>
|
|
||||||
</t-list-item>
|
|
||||||
</t-list>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.about-us {
|
|
||||||
padding: 20px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-label {
|
|
||||||
flex: 1;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-content {
|
|
||||||
flex: 2;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-item {
|
|
||||||
margin-right: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,6 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="basic-info">
|
|
||||||
<h1>面板基础信息</h1>
|
|
||||||
<p>这里显示面板的基础信息。</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@@ -1,6 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="log-view">
|
|
||||||
<h1>面板日志信息</h1>
|
|
||||||
<p>这里显示面板的日志信息。</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@@ -1,244 +0,0 @@
|
|||||||
<template>
|
|
||||||
<t-space class="full-space">
|
|
||||||
<template v-if="clientPanelData.length > 0">
|
|
||||||
<t-tabs v-model="activeTab" :addable="true" theme="card" @add="showAddTabDialog" @remove="removeTab" class="full-tabs">
|
|
||||||
<t-tab-panel
|
|
||||||
v-for="(config, idx) in clientPanelData"
|
|
||||||
:key="idx"
|
|
||||||
:label="config.name"
|
|
||||||
:removable="true"
|
|
||||||
:value="idx"
|
|
||||||
class="full-tab-panel"
|
|
||||||
>
|
|
||||||
<component :is="resolveDynamicComponent(getComponent(config.key))" :config="config.data" />
|
|
||||||
<div class="button-container">
|
|
||||||
<t-button @click="saveConfig" style="width: 100px; height: 40px;">保存</t-button>
|
|
||||||
</div>
|
|
||||||
</t-tab-panel>
|
|
||||||
</t-tabs>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<EmptyStateComponent :showAddTabDialog="showAddTabDialog" />
|
|
||||||
</template>
|
|
||||||
<t-dialog
|
|
||||||
v-model:visible="isDialogVisible"
|
|
||||||
header="添加网络配置"
|
|
||||||
@close="isDialogVisible = false"
|
|
||||||
@confirm="addTab"
|
|
||||||
>
|
|
||||||
<t-form ref="form" :model="newTab">
|
|
||||||
<t-form-item :rules="[{ required: true, message: '请输入名称' }]" label="名称" name="name">
|
|
||||||
<t-input v-model="newTab.name" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item :rules="[{ required: true, message: '请选择类型' }]" label="类型" name="type">
|
|
||||||
<t-select v-model="newTab.type">
|
|
||||||
<t-option value="httpServers">HTTP 服务器</t-option>
|
|
||||||
<t-option value="httpClients">HTTP 客户端</t-option>
|
|
||||||
<t-option value="websocketServers">WebSocket 服务器</t-option>
|
|
||||||
<t-option value="websocketClients">WebSocket 客户端</t-option>
|
|
||||||
</t-select>
|
|
||||||
</t-form-item>
|
|
||||||
</t-form>
|
|
||||||
</t-dialog>
|
|
||||||
</t-space>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, resolveDynamicComponent, nextTick, Ref, onMounted, reactive, Reactive } from 'vue';
|
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
|
||||||
import {
|
|
||||||
httpServerDefaultConfigs,
|
|
||||||
httpClientDefaultConfigs,
|
|
||||||
websocketServerDefaultConfigs,
|
|
||||||
websocketClientDefaultConfigs,
|
|
||||||
HttpClientConfig,
|
|
||||||
HttpServerConfig,
|
|
||||||
WebsocketClientConfig,
|
|
||||||
WebsocketServerConfig,
|
|
||||||
NetworkConfig,
|
|
||||||
OneBotConfig,
|
|
||||||
mergeOneBotConfigs,
|
|
||||||
} from '../../../src/onebot/config/config';
|
|
||||||
import { QQLoginManager } from '@/backend/shell';
|
|
||||||
import HttpServerComponent from '@/pages/network/HttpServerComponent.vue';
|
|
||||||
import HttpClientComponent from '@/pages/network/HttpClientComponent.vue';
|
|
||||||
import WebsocketServerComponent from '@/pages/network/WebsocketServerComponent.vue';
|
|
||||||
import WebsocketClientComponent from '@/pages/network/WebsocketClientComponent.vue';
|
|
||||||
import EmptyStateComponent from '@/pages/network/EmptyStateComponent.vue';
|
|
||||||
|
|
||||||
type ConfigKey = 'httpServers' | 'httpClients' | 'websocketServers' | 'websocketClients';
|
|
||||||
|
|
||||||
type ConfigUnion = HttpClientConfig | HttpServerConfig | WebsocketServerConfig | WebsocketClientConfig;
|
|
||||||
|
|
||||||
const defaultConfigs: Record<ConfigKey, ConfigUnion> = {
|
|
||||||
httpServers: httpServerDefaultConfigs,
|
|
||||||
httpClients: httpClientDefaultConfigs,
|
|
||||||
websocketServers: websocketServerDefaultConfigs,
|
|
||||||
websocketClients: websocketClientDefaultConfigs,
|
|
||||||
};
|
|
||||||
|
|
||||||
const componentMap: Record<
|
|
||||||
ConfigKey,
|
|
||||||
| typeof HttpServerComponent
|
|
||||||
| typeof HttpClientComponent
|
|
||||||
| typeof WebsocketServerComponent
|
|
||||||
| typeof WebsocketClientComponent
|
|
||||||
> = {
|
|
||||||
httpServers: HttpServerComponent,
|
|
||||||
httpClients: HttpClientComponent,
|
|
||||||
websocketServers: WebsocketServerComponent,
|
|
||||||
websocketClients: WebsocketClientComponent,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ClientPanel {
|
|
||||||
name: string;
|
|
||||||
key: ConfigKey;
|
|
||||||
data: Ref<ConfigUnion>;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComponentKey = keyof typeof componentMap;
|
|
||||||
|
|
||||||
// TODO: store these state in global store (aka pinia)
|
|
||||||
const activeTab = ref<number>(0);
|
|
||||||
const isDialogVisible = ref(false);
|
|
||||||
const newTab = ref<{ name: string; type: ComponentKey }>({ name: '', type: 'httpServers' });
|
|
||||||
const clientPanelData: Reactive<Array<ClientPanel>> = reactive([]);
|
|
||||||
|
|
||||||
const getComponent = (type: ComponentKey) => {
|
|
||||||
return componentMap[type];
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOB11Config = async (): Promise<OneBotConfig | undefined> => {
|
|
||||||
const storedCredential = localStorage.getItem('auth');
|
|
||||||
if (!storedCredential) {
|
|
||||||
console.error('No stored credential found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
|
||||||
return await loginManager.GetOB11Config();
|
|
||||||
};
|
|
||||||
|
|
||||||
const setOB11Config = async (config: OneBotConfig): Promise<boolean> => {
|
|
||||||
const storedCredential = localStorage.getItem('auth');
|
|
||||||
if (!storedCredential) {
|
|
||||||
console.error('No stored credential found');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
|
||||||
return await loginManager.SetOB11Config(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addToPanel = <T extends ConfigUnion>(configs: T[], key: ConfigKey) => {
|
|
||||||
configs.forEach((config) => clientPanelData.push({ name: config.name, data: config, key: key }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const addConfigDataToPanel = (data: NetworkConfig) => {
|
|
||||||
Object.entries(data).forEach(([key, configs]) => {
|
|
||||||
if (key in defaultConfigs) {
|
|
||||||
addToPanel(configs as ConfigUnion[], key as ConfigKey);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const parsePanelData = (): NetworkConfig => {
|
|
||||||
return {
|
|
||||||
websocketClients: clientPanelData
|
|
||||||
.filter((panel) => panel.key === 'websocketClients')
|
|
||||||
.map((panel) => panel.data as WebsocketClientConfig),
|
|
||||||
websocketServers: clientPanelData
|
|
||||||
.filter((panel) => panel.key === 'websocketServers')
|
|
||||||
.map((panel) => panel.data as WebsocketServerConfig),
|
|
||||||
httpClients: clientPanelData
|
|
||||||
.filter((panel) => panel.key === 'httpClients')
|
|
||||||
.map((panel) => panel.data as HttpClientConfig),
|
|
||||||
httpServers: clientPanelData
|
|
||||||
.filter((panel) => panel.key === 'httpServers')
|
|
||||||
.map((panel) => panel.data as HttpServerConfig),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadConfig = async () => {
|
|
||||||
try {
|
|
||||||
const userConfig = await getOB11Config();
|
|
||||||
if (!userConfig) return;
|
|
||||||
const mergedConfig = mergeOneBotConfigs(userConfig);
|
|
||||||
addConfigDataToPanel(mergedConfig.network);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading config:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// It's better to "saveConfig" instead of using deep watch
|
|
||||||
const saveConfig = async () => {
|
|
||||||
const config = parsePanelData();
|
|
||||||
const userConfig = await getOB11Config();
|
|
||||||
if (!userConfig) return;
|
|
||||||
userConfig.network = config;
|
|
||||||
const success = await setOB11Config(userConfig);
|
|
||||||
if (success) {
|
|
||||||
MessagePlugin.success('配置保存成功');
|
|
||||||
} else {
|
|
||||||
MessagePlugin.error('配置保存失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const showAddTabDialog = () => {
|
|
||||||
newTab.value = { name: '', type: 'httpServers' };
|
|
||||||
isDialogVisible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addTab = async () => {
|
|
||||||
const { name, type } = newTab.value;
|
|
||||||
if (clientPanelData.some(panel => panel.name === name)) {
|
|
||||||
MessagePlugin.error('选项卡名称已存在');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const defaultConfig = structuredClone(defaultConfigs[type]);
|
|
||||||
defaultConfig.name = name;
|
|
||||||
clientPanelData.push({ name, data: defaultConfig, key: type });
|
|
||||||
isDialogVisible.value = false;
|
|
||||||
await nextTick();
|
|
||||||
activeTab.value = clientPanelData.length - 1;
|
|
||||||
MessagePlugin.success('选项卡添加成功');
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeTab = async (payload: { value: string; index: number; e: PointerEvent }) => {
|
|
||||||
clientPanelData.splice(payload.index, 1);
|
|
||||||
activeTab.value = Math.max(0, activeTab.value - 1);
|
|
||||||
await saveConfig();
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
loadConfig();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.full-space {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-tabs {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-tab-panel {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,134 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<t-divider content="其余配置" align="left" />
|
|
||||||
</div>
|
|
||||||
<div class="other-config-container">
|
|
||||||
<div class="other-config">
|
|
||||||
<t-form ref="form" :model="otherConfig" class="form">
|
|
||||||
<t-form-item label="音乐签名地址" name="musicSignUrl" class="form-item">
|
|
||||||
<t-input v-model="otherConfig.musicSignUrl" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="启用本地文件到URL" name="enableLocalFile2Url" class="form-item">
|
|
||||||
<t-switch v-model="otherConfig.enableLocalFile2Url" />
|
|
||||||
</t-form-item>
|
|
||||||
</t-form>
|
|
||||||
<div class="button-container">
|
|
||||||
<t-button @click="saveConfig">保存</t-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, onMounted } from 'vue';
|
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
|
||||||
import { OneBotConfig } from '../../../src/onebot/config/config';
|
|
||||||
import { QQLoginManager } from '@/backend/shell';
|
|
||||||
|
|
||||||
const otherConfig = ref<Partial<OneBotConfig>>({
|
|
||||||
musicSignUrl: '',
|
|
||||||
enableLocalFile2Url: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getOB11Config = async (): Promise<OneBotConfig | undefined> => {
|
|
||||||
const storedCredential = localStorage.getItem('auth');
|
|
||||||
if (!storedCredential) {
|
|
||||||
console.error('No stored credential found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
|
||||||
return await loginManager.GetOB11Config();
|
|
||||||
};
|
|
||||||
|
|
||||||
const setOB11Config = async (config: OneBotConfig): Promise<boolean> => {
|
|
||||||
const storedCredential = localStorage.getItem('auth');
|
|
||||||
if (!storedCredential) {
|
|
||||||
console.error('No stored credential found');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
|
||||||
return await loginManager.SetOB11Config(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadConfig = async () => {
|
|
||||||
try {
|
|
||||||
const userConfig = await getOB11Config();
|
|
||||||
if (userConfig) {
|
|
||||||
otherConfig.value.musicSignUrl = userConfig.musicSignUrl;
|
|
||||||
otherConfig.value.enableLocalFile2Url = userConfig.enableLocalFile2Url;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading config:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveConfig = async () => {
|
|
||||||
try {
|
|
||||||
const userConfig = await getOB11Config();
|
|
||||||
if (userConfig) {
|
|
||||||
userConfig.musicSignUrl = otherConfig.value.musicSignUrl || '';
|
|
||||||
userConfig.enableLocalFile2Url = otherConfig.value.enableLocalFile2Url ?? false;
|
|
||||||
const success = await setOB11Config(userConfig);
|
|
||||||
if (success) {
|
|
||||||
MessagePlugin.success('配置保存成功');
|
|
||||||
} else {
|
|
||||||
MessagePlugin.error('配置保存失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error saving config:', error);
|
|
||||||
MessagePlugin.error('配置保存失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
loadConfig();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.other-config-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.other-config {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600px;
|
|
||||||
background: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.form-item {
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-item t-input,
|
|
||||||
.form-item t-switch {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,22 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="empty-state">
|
|
||||||
<p>当前没有网络配置</p>
|
|
||||||
<t-button @click="showAddTabDialog">添加网络配置</t-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { defineProps } from 'vue';
|
|
||||||
defineProps<{ showAddTabDialog: () => void }>();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.empty-state {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,65 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<div class="form-container">
|
|
||||||
<h3>HTTP Client 配置</h3>
|
|
||||||
<t-form>
|
|
||||||
<t-form-item label="启用">
|
|
||||||
<t-checkbox v-model="config.enable" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="URL">
|
|
||||||
<t-input v-model="config.url" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="消息格式">
|
|
||||||
<t-select v-model="config.messagePostFormat" :options="messageFormatOptions" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="报告自身消息">
|
|
||||||
<t-checkbox v-model="config.reportSelfMessage" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="Token">
|
|
||||||
<t-input v-model="config.token" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="调试模式">
|
|
||||||
<t-checkbox v-model="config.debug" />
|
|
||||||
</t-form-item>
|
|
||||||
</t-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { defineProps, ref, watch } from 'vue';
|
|
||||||
import { HttpClientConfig } from '../../../../src/onebot/config/config';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
config: HttpClientConfig;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const messageFormatOptions = ref([
|
|
||||||
{ label: 'Array', value: 'array' },
|
|
||||||
{ label: 'String', value: 'string' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
watch(() => props.config.messagePostFormat, (newValue) => {
|
|
||||||
if (newValue !== 'array' && newValue !== 'string') {
|
|
||||||
props.config.messagePostFormat = 'array';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600px;
|
|
||||||
background: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,71 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<div class="form-container">
|
|
||||||
<h3>HTTP Server 配置</h3>
|
|
||||||
<t-form>
|
|
||||||
<t-form-item label="启用">
|
|
||||||
<t-checkbox v-model="config.enable" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="端口">
|
|
||||||
<t-input v-model.number="config.port" type="number" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="主机">
|
|
||||||
<t-input v-model="config.host" type="text" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="启用 CORS">
|
|
||||||
<t-checkbox v-model="config.enableCors" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="启用 WS">
|
|
||||||
<t-checkbox v-model="config.enableWebsocket" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="消息格式">
|
|
||||||
<t-select v-model="config.messagePostFormat" :options="messageFormatOptions" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="Token">
|
|
||||||
<t-input v-model="config.token" type="text" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="调试模式">
|
|
||||||
<t-checkbox v-model="config.debug" />
|
|
||||||
</t-form-item>
|
|
||||||
</t-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { defineProps, ref, watch } from 'vue';
|
|
||||||
import { HttpServerConfig } from '../../../../src/onebot/config/config';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
config: HttpServerConfig;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const messageFormatOptions = ref([
|
|
||||||
{ label: 'Array', value: 'array' },
|
|
||||||
{ label: 'String', value: 'string' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
watch(() => props.config.messagePostFormat, (newValue) => {
|
|
||||||
if (newValue !== 'array' && newValue !== 'string') {
|
|
||||||
props.config.messagePostFormat = 'array';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600px;
|
|
||||||
background: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,68 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<div class="form-container">
|
|
||||||
<h3>WebSocket Client 配置</h3>
|
|
||||||
<t-form>
|
|
||||||
<t-form-item label="启用">
|
|
||||||
<t-checkbox v-model="config.enable" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="URL">
|
|
||||||
<t-input v-model="config.url" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="消息格式">
|
|
||||||
<t-select v-model="config.messagePostFormat" :options="messageFormatOptions" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="报告自身消息">
|
|
||||||
<t-checkbox v-model="config.reportSelfMessage" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="Token">
|
|
||||||
<t-input v-model="config.token" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="调试模式">
|
|
||||||
<t-checkbox v-model="config.debug" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="心跳间隔">
|
|
||||||
<t-input v-model.number="config.heartInterval" type="number" />
|
|
||||||
</t-form-item>
|
|
||||||
</t-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { defineProps, ref, watch } from 'vue';
|
|
||||||
import { WebsocketClientConfig } from '../../../../src/onebot/config/config';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
config: WebsocketClientConfig;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const messageFormatOptions = ref([
|
|
||||||
{ label: 'Array', value: 'array' },
|
|
||||||
{ label: 'String', value: 'string' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
watch(() => props.config.messagePostFormat, (newValue) => {
|
|
||||||
if (newValue !== 'array' && newValue !== 'string') {
|
|
||||||
props.config.messagePostFormat = 'array';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600px;
|
|
||||||
background: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,74 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<div class="form-container">
|
|
||||||
<h3>WebSocket Server 配置</h3>
|
|
||||||
<t-form>
|
|
||||||
<t-form-item label="启用">
|
|
||||||
<t-checkbox v-model="config.enable" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="主机">
|
|
||||||
<t-input v-model="config.host" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="端口">
|
|
||||||
<t-input v-model.number="config.port" type="number" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="消息格式">
|
|
||||||
<t-select v-model="config.messagePostFormat" :options="messageFormatOptions" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="上报自身消息">
|
|
||||||
<t-checkbox v-model="config.reportSelfMessage" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="Token">
|
|
||||||
<t-input v-model="config.token" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="强制推送事件">
|
|
||||||
<t-checkbox v-model="config.enableForcePushEvent" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="调试模式">
|
|
||||||
<t-checkbox v-model="config.debug" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="心跳间隔">
|
|
||||||
<t-input v-model.number="config.heartInterval" type="number" />
|
|
||||||
</t-form-item>
|
|
||||||
</t-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { defineProps, ref, watch } from 'vue';
|
|
||||||
import { WebsocketServerConfig } from '../../../../src/onebot/config/config';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
config: WebsocketServerConfig;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const messageFormatOptions = ref([
|
|
||||||
{ label: 'Array', value: 'array' },
|
|
||||||
{ label: 'String', value: 'string' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
watch(() => props.config.messagePostFormat, (newValue) => {
|
|
||||||
if (newValue !== 'array' && newValue !== 'string') {
|
|
||||||
props.config.messagePostFormat = 'array';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600px;
|
|
||||||
background: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,267 +0,0 @@
|
|||||||
<template>
|
|
||||||
<t-space class="full-space">
|
|
||||||
<t-tabs v-model="activeTab" :addable="true" theme="card" @add="showAddTabDialog" @remove="removeTab" class="full-tabs">
|
|
||||||
<t-tab-panel
|
|
||||||
v-for="(config, idx) in clientPanelData"
|
|
||||||
:key="idx"
|
|
||||||
:label="config.name"
|
|
||||||
:removable="true"
|
|
||||||
:value="idx"
|
|
||||||
class="full-tab-panel"
|
|
||||||
>
|
|
||||||
<component :is="resolveDynamicComponent(getComponent(config.key))" :config="config.data" />
|
|
||||||
<div class="button-container">
|
|
||||||
<t-button @click="saveConfig" style="width: 100px; height: 40px;">保存</t-button>
|
|
||||||
</div>
|
|
||||||
</t-tab-panel>
|
|
||||||
</t-tabs>
|
|
||||||
<t-dialog
|
|
||||||
v-model:visible="isDialogVisible"
|
|
||||||
header="添加新选项卡"
|
|
||||||
@close="isDialogVisible = false"
|
|
||||||
@confirm="addTab"
|
|
||||||
>
|
|
||||||
<t-form ref="form" :model="newTab">
|
|
||||||
<t-form-item :rules="[{ required: true, message: '请输入名称' }]" label="名称" name="name">
|
|
||||||
<t-input v-model="newTab.name" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item :rules="[{ required: true, message: '请选择类型' }]" label="类型" name="type">
|
|
||||||
<t-select v-model="newTab.type">
|
|
||||||
<t-option value="httpServers">HTTP 服务器</t-option>
|
|
||||||
<t-option value="httpClients">HTTP 客户端</t-option>
|
|
||||||
<t-option value="websocketServers">WebSocket 服务器</t-option>
|
|
||||||
<t-option value="websocketClients">WebSocket 客户端</t-option>
|
|
||||||
</t-select>
|
|
||||||
</t-form-item>
|
|
||||||
</t-form>
|
|
||||||
</t-dialog>
|
|
||||||
</t-space>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, resolveDynamicComponent, nextTick, Ref, onMounted, reactive, Reactive } from 'vue';
|
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
|
||||||
import {
|
|
||||||
httpServerDefaultConfigs,
|
|
||||||
httpClientDefaultConfigs,
|
|
||||||
websocketServerDefaultConfigs,
|
|
||||||
websocketClientDefaultConfigs,
|
|
||||||
HttpClientConfig,
|
|
||||||
HttpServerConfig,
|
|
||||||
WebsocketClientConfig,
|
|
||||||
WebsocketServerConfig,
|
|
||||||
NetworkConfig,
|
|
||||||
OneBotConfig,
|
|
||||||
mergeOneBotConfigs,
|
|
||||||
} from '../../../../src/onebot/config/config';
|
|
||||||
import { QQLoginManager } from '@/backend/shell';
|
|
||||||
import HttpServerComponent from '@/pages/network/HttpServerComponent.vue';
|
|
||||||
import HttpClientComponent from '@/pages/network/HttpClientComponent.vue';
|
|
||||||
import WebsocketServerComponent from '@/pages/network/WebsocketServerComponent.vue';
|
|
||||||
import WebsocketClientComponent from '@/pages/network/WebsocketClientComponent.vue';
|
|
||||||
|
|
||||||
type ConfigKey = 'httpServers' | 'httpClients' | 'websocketServers' | 'websocketClients';
|
|
||||||
|
|
||||||
type ConfigUnion = HttpClientConfig | HttpServerConfig | WebsocketServerConfig | WebsocketClientConfig;
|
|
||||||
|
|
||||||
const defaultConfigs: Record<ConfigKey, ConfigUnion> = {
|
|
||||||
httpServers: httpServerDefaultConfigs,
|
|
||||||
httpClients: httpClientDefaultConfigs,
|
|
||||||
websocketServers: websocketServerDefaultConfigs,
|
|
||||||
websocketClients: websocketClientDefaultConfigs,
|
|
||||||
};
|
|
||||||
|
|
||||||
const componentMap: Record<
|
|
||||||
ConfigKey,
|
|
||||||
| typeof HttpServerComponent
|
|
||||||
| typeof HttpClientComponent
|
|
||||||
| typeof WebsocketServerComponent
|
|
||||||
| typeof WebsocketClientComponent
|
|
||||||
> = {
|
|
||||||
httpServers: HttpServerComponent,
|
|
||||||
httpClients: HttpClientComponent,
|
|
||||||
websocketServers: WebsocketServerComponent,
|
|
||||||
websocketClients: WebsocketClientComponent,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ClientPanel {
|
|
||||||
name: string;
|
|
||||||
key: ConfigKey;
|
|
||||||
data: Ref<ConfigUnion>;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComponentKey = keyof typeof componentMap;
|
|
||||||
|
|
||||||
// TODO: store these state in global store (aka pinia)
|
|
||||||
const activeTab = ref<number>(0);
|
|
||||||
const isDialogVisible = ref(false);
|
|
||||||
const newTab = ref<{ name: string; type: ComponentKey }>({ name: '', type: 'httpServers' });
|
|
||||||
const clientPanelData: Reactive<Array<ClientPanel>> = reactive([]);
|
|
||||||
|
|
||||||
const getComponent = (type: ComponentKey) => {
|
|
||||||
return componentMap[type];
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOB11Config = async (): Promise<OneBotConfig | undefined> => {
|
|
||||||
const storedCredential = localStorage.getItem('auth');
|
|
||||||
if (!storedCredential) {
|
|
||||||
console.error('No stored credential found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
|
||||||
return await loginManager.GetOB11Config();
|
|
||||||
};
|
|
||||||
|
|
||||||
const setOB11Config = async (config: OneBotConfig): Promise<boolean> => {
|
|
||||||
const storedCredential = localStorage.getItem('auth');
|
|
||||||
if (!storedCredential) {
|
|
||||||
console.error('No stored credential found');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
|
||||||
return await loginManager.SetOB11Config(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addToPanel = <T extends ConfigUnion>(configs: T[], key: ConfigKey) => {
|
|
||||||
configs.forEach((config) => clientPanelData.push({ name: config.name, data: config, key: key }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const addConfigDataToPanel = (data: NetworkConfig) => {
|
|
||||||
Object.entries(data).forEach(([key, configs]) => {
|
|
||||||
if (key in defaultConfigs) {
|
|
||||||
addToPanel(configs as ConfigUnion[], key as ConfigKey);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const parsePanelData = (): NetworkConfig => {
|
|
||||||
return {
|
|
||||||
websocketClients: clientPanelData
|
|
||||||
.filter((panel) => panel.key === 'websocketClients')
|
|
||||||
.map((panel) => panel.data as WebsocketClientConfig),
|
|
||||||
websocketServers: clientPanelData
|
|
||||||
.filter((panel) => panel.key === 'websocketServers')
|
|
||||||
.map((panel) => panel.data as WebsocketServerConfig),
|
|
||||||
httpClients: clientPanelData
|
|
||||||
.filter((panel) => panel.key === 'httpClients')
|
|
||||||
.map((panel) => panel.data as HttpClientConfig),
|
|
||||||
httpServers: clientPanelData
|
|
||||||
.filter((panel) => panel.key === 'httpServers')
|
|
||||||
.map((panel) => panel.data as HttpServerConfig),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadConfig = async () => {
|
|
||||||
try {
|
|
||||||
const userConfig = await getOB11Config();
|
|
||||||
if (!userConfig) return;
|
|
||||||
const mergedConfig = mergeOneBotConfigs(userConfig);
|
|
||||||
addConfigDataToPanel(mergedConfig.network);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading config:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// It's better to "saveConfig" instead of using deep watch
|
|
||||||
const saveConfig = async () => {
|
|
||||||
const config = parsePanelData();
|
|
||||||
const userConfig = await getOB11Config();
|
|
||||||
if (!userConfig) return;
|
|
||||||
userConfig.network = config;
|
|
||||||
const success = await setOB11Config(userConfig);
|
|
||||||
if (success) {
|
|
||||||
MessagePlugin.success('配置保存成功');
|
|
||||||
} else {
|
|
||||||
MessagePlugin.error('配置保存失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const showAddTabDialog = () => {
|
|
||||||
newTab.value = { name: '', type: 'httpServers' };
|
|
||||||
isDialogVisible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addTab = async () => {
|
|
||||||
const { name, type } = newTab.value;
|
|
||||||
if (clientPanelData.some(panel => panel.name === name)) {
|
|
||||||
MessagePlugin.error('选项卡名称已存在');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const defaultConfig = structuredClone(defaultConfigs[type]);
|
|
||||||
defaultConfig.name = name;
|
|
||||||
clientPanelData.push({ name, data: defaultConfig, key: type });
|
|
||||||
isDialogVisible.value = false;
|
|
||||||
await nextTick();
|
|
||||||
activeTab.value = clientPanelData.length - 1;
|
|
||||||
MessagePlugin.success('选项卡添加成功');
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeTab = async (payload: { value: string; index: number; e: PointerEvent }) => {
|
|
||||||
clientPanelData.splice(payload.index, 1);
|
|
||||||
activeTab.value = Math.max(0, activeTab.value - 1);
|
|
||||||
await saveConfig();
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
loadConfig();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.full-space {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-tabs {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-tab-panel {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<style scoped>
|
|
||||||
.full-space {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-tabs {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-tab-panel {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,32 +0,0 @@
|
|||||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
|
||||||
import Dashboard from '../components/Dashboard.vue';
|
|
||||||
import BasicInfo from '../pages/BasicInfo.vue';
|
|
||||||
import AboutUs from '../pages/AboutUs.vue';
|
|
||||||
import LogView from '../pages/Log.vue';
|
|
||||||
import NetWork from '../pages/NetWork.vue';
|
|
||||||
import QQLogin from '../components/QQLogin.vue';
|
|
||||||
import WebUiLogin from '../components/WebUiLogin.vue';
|
|
||||||
import OtherConfig from '../pages/OtherConfig.vue';
|
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
|
||||||
{ path: '/', redirect: '/webui' },
|
|
||||||
{ path: '/webui', component: WebUiLogin, name: 'WebUiLogin' },
|
|
||||||
{ path: '/qqlogin', component: QQLogin, name: 'QQLogin' },
|
|
||||||
{
|
|
||||||
path: '/dashboard',
|
|
||||||
component: Dashboard,
|
|
||||||
children: [
|
|
||||||
{ path: '', redirect: 'basic-info' },
|
|
||||||
{ path: 'basic-info', component: BasicInfo, name: 'BasicInfo' },
|
|
||||||
{ path: 'network-config', component: NetWork, name: 'NetWork' },
|
|
||||||
{ path: 'log-view', component: LogView, name: 'LogView' },
|
|
||||||
{ path: 'other-config', component: OtherConfig, name: 'OtherConfig' },
|
|
||||||
{ path: 'about-us', component: AboutUs, name: 'AboutUs' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const router = createRouter({
|
|
||||||
history: createWebHashHistory(),
|
|
||||||
routes,
|
|
||||||
});
|
|
1
napcat.webui/src/vite-env.d.ts
vendored
1
napcat.webui/src/vite-env.d.ts
vendored
@@ -1 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ESNext",
|
|
||||||
"jsx": "preserve",
|
|
||||||
"jsxImportSource": "vue",
|
|
||||||
"lib": [
|
|
||||||
"DOM",
|
|
||||||
"DOM.Iterable"
|
|
||||||
],
|
|
||||||
"baseUrl": ".",
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"paths": {
|
|
||||||
"@/*": [
|
|
||||||
"src/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"types": [
|
|
||||||
"vite/client"
|
|
||||||
],
|
|
||||||
"strict": true,
|
|
||||||
"strictNullChecks": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"useDefineForClassFields": true
|
|
||||||
},
|
|
||||||
"include": ["src"],
|
|
||||||
"exclude": ["node_modules"],
|
|
||||||
"references": [{"path": "./tsconfig.node.json"}]
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strictNullChecks": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
import { defineConfig } from 'vite';
|
|
||||||
import vue from '@vitejs/plugin-vue';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
// https://vite.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [vue()],
|
|
||||||
base: './',
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': path.resolve(__dirname, 'src'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
proxy: {
|
|
||||||
'/api': 'http://localhost:6099',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
manualChunks(id) {
|
|
||||||
if (id.includes('node_modules')) {
|
|
||||||
return id.toString().split('node_modules/')[1].split('/')[0].toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
80
package.json
80
package.json
@@ -2,59 +2,71 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.1.0",
|
"version": "1.5.9",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:framework": "npm run build:webui && vite build --mode framework",
|
"watch:dev": "vite --mode development",
|
||||||
"build:shell": "npm run build:webui && vite build --mode shell",
|
"watch:prod": "vite --mode production",
|
||||||
"build:webui": "cd napcat.webui && vite build",
|
"build:dev": "vite build --mode development",
|
||||||
"dev:framework": "vite build --mode framework",
|
"build:prod": "vite build --mode production",
|
||||||
"dev:shell": "vite build --mode shell",
|
"build": "npm run build:dev",
|
||||||
"dev:webui": "cd napcat.webui && npm run webui:dev",
|
"build:core": "cd ./src/core && npm run build && cd ../.. && node ./script/copy-core.cjs",
|
||||||
"lint": "eslint --fix src/**/*.{js,ts,vue}",
|
"build:webui": "cd ./src/webui && vite build",
|
||||||
|
"watch": "npm run watch:dev",
|
||||||
|
"debug-win": "powershell dist/napcat.ps1",
|
||||||
|
"lint": "eslint --fix src/**/*.{js,ts}",
|
||||||
|
"release": "npm run build:prod",
|
||||||
"depend": "cd dist && npm install --omit=dev"
|
"depend": "cd dist && npm install --omit=dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.24.7",
|
||||||
"@babel/preset-typescript": "^7.24.7",
|
"@babel/preset-typescript": "^7.24.7",
|
||||||
"@eslint/compat": "^1.2.2",
|
"vite-plugin-babel": "^1.2.0",
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@eslint/js": "^9.14.0",
|
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||||
"@log4js-node/log4js-api": "^1.0.2",
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
"@napneko/nap-proto-core": "^0.0.4",
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@rollup/plugin-typescript": "^11.1.6",
|
"@rollup/plugin-typescript": "^11.1.6",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/figlet": "^1.5.8",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"@types/node": "^22.0.1",
|
"@types/node": "^20.11.30",
|
||||||
"@types/qrcode-terminal": "^0.12.2",
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
"@types/ws": "^8.5.12",
|
"@types/uuid": "^10.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
"@types/ws": "^8.5.10",
|
||||||
"@typescript-eslint/parser": "^8.3.0",
|
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||||
"ajv": "^8.13.0",
|
"@typescript-eslint/parser": "^7.4.0",
|
||||||
"async-mutex": "^0.5.0",
|
"eslint": "^8.57.0",
|
||||||
"commander": "^12.1.0",
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"eslint": "^9.14.0",
|
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"fast-xml-parser": "^4.3.6",
|
"i": "^0.3.7",
|
||||||
"file-type": "^19.0.0",
|
"javascript-obfuscator": "^4.1.0",
|
||||||
"globals": "^15.12.0",
|
"rollup": "^4.13.2",
|
||||||
"image-size": "^1.1.1",
|
"rollup-plugin-dts": "^6.1.0",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"rollup-plugin-obfuscator": "^1.1.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"typescript-eslint": "^8.13.0",
|
|
||||||
"vite": "^5.2.6",
|
"vite": "^5.2.6",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-tsconfig-paths": "^5.1.0",
|
"vite-plugin-dts": "^3.8.2",
|
||||||
"winston": "^3.17.0"
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.0.0",
|
"ajv": "^8.13.0",
|
||||||
|
"chalk": "^5.3.0",
|
||||||
|
"commander": "^12.0.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^5.0.0-beta.2",
|
||||||
|
"fast-xml-parser": "^4.3.6",
|
||||||
|
"file-type": "^19.0.0",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
|
"image-size": "^1.1.1",
|
||||||
|
"json-schema-to-ts": "^3.1.0",
|
||||||
|
"log4js": "^6.9.1",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"silk-wasm": "^3.6.1",
|
"silk-wasm": "^3.3.4",
|
||||||
"ws": "^8.18.0",
|
"sqlite3": "^5.1.7",
|
||||||
"piscina": "^4.7.0"
|
"uuid": "^10.0.0",
|
||||||
|
"ws": "^8.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
@echo off
|
|
||||||
taskkill /f /im QQ.exe
|
|
@@ -4,25 +4,14 @@ const process = require("process");
|
|||||||
console.log("[NapCat] [CheckVersion] 开始检测当前仓库版本...");
|
console.log("[NapCat] [CheckVersion] 开始检测当前仓库版本...");
|
||||||
try {
|
try {
|
||||||
const packageJson = require("../package.json");
|
const packageJson = require("../package.json");
|
||||||
const manifsetJson = require("../manifest.json");
|
|
||||||
|
|
||||||
const currentVersion = packageJson.version;
|
const currentVersion = packageJson.version;
|
||||||
const targetVersion = process.env.VERSION;
|
const targetVersion = process.env.VERSION;
|
||||||
|
|
||||||
const manifestCurrentVersion = manifsetJson.version;
|
|
||||||
const manifestTargetVersion = process.env.VERSION;
|
|
||||||
|
|
||||||
console.log("[NapCat] [CheckVersion] currentVersion:", currentVersion, "targetVersion:", targetVersion);
|
console.log("[NapCat] [CheckVersion] currentVersion:", currentVersion, "targetVersion:", targetVersion);
|
||||||
console.log("[NapCat] [CheckVersion] manifestCurrentVersion:", manifestCurrentVersion, "manifestTargetVersion:", manifestTargetVersion);
|
|
||||||
|
|
||||||
// 验证 targetVersion 格式
|
// 验证 targetVersion 格式
|
||||||
if (!targetVersion || typeof targetVersion !== 'string') {
|
if (!targetVersion || typeof targetVersion !== 'string') {
|
||||||
console.log("[NapCat] [CheckVersion] 目标版本格式不正确或未设置!");
|
console.error("[NapCat] [CheckVersion] 目标版本格式不正确或未设置!");
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 验证 manifestTargetVersion 格式
|
|
||||||
if (!manifestTargetVersion || typeof manifestTargetVersion !== 'string') {
|
|
||||||
console.log("[NapCat] [CheckVersion] manifest目标版本格式不正确或未设置!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +21,7 @@ try {
|
|||||||
console.log("[NapCat] [CheckVersion] checkVersion.sh 文件已更新。");
|
console.log("[NapCat] [CheckVersion] checkVersion.sh 文件已更新。");
|
||||||
};
|
};
|
||||||
|
|
||||||
if (currentVersion === targetVersion && manifestCurrentVersion === manifestTargetVersion) {
|
if (currentVersion === targetVersion) {
|
||||||
// 不需要更新版本,写入一个简单的脚本
|
// 不需要更新版本,写入一个简单的脚本
|
||||||
const simpleScript = "#!/bin/bash\necho \"CheckVersion Is Done\"";
|
const simpleScript = "#!/bin/bash\necho \"CheckVersion Is Done\"";
|
||||||
writeScriptToFile(simpleScript);
|
writeScriptToFile(simpleScript);
|
||||||
@@ -40,16 +29,14 @@ try {
|
|||||||
// 更新版本,构建安全的sed命令
|
// 更新版本,构建安全的sed命令
|
||||||
const safeScriptContent = `
|
const safeScriptContent = `
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
git config --global user.email "nanaeonn@outlook.com"
|
git config --global user.email "bot@test.wumiao.wang"
|
||||||
git config --global user.name "Mlikiowa"
|
git config --global user.name "Version"
|
||||||
sed -i "s/\\"version\\": \\"${currentVersion}\\"/\\"version\\": \\"${targetVersion}\\"/g" package.json
|
sed -i "s/\\\"version\\\": \\\"${currentVersion}\\\"/\\\"version\\\": \\\"${targetVersion}\\\"/g" package.json
|
||||||
sed -i "s/\\"version\\": \\"${manifestCurrentVersion}\\"/\\"version\\": \\"${targetVersion}\\"/g" manifest.json
|
|
||||||
sed -i "s/napCatVersion = '.*'/napCatVersion = '${targetVersion}'/g" ./src/common/version.ts
|
|
||||||
git add .
|
git add .
|
||||||
git commit -m "release: v${targetVersion}"
|
git commit -m "chore:version change"
|
||||||
git push -u origin main`;
|
git push -u origin main`;
|
||||||
writeScriptToFile(safeScriptContent);
|
writeScriptToFile(safeScriptContent);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[NapCat] [CheckVersion] 检测过程中发生错误:", error);
|
console.error("[NapCat] [CheckVersion] 检测过程中发生错误:", error);
|
||||||
}
|
}
|
32
script/copy-core.cjs
Normal file
32
script/copy-core.cjs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
let fs = require('fs');
|
||||||
|
let path = require('path');
|
||||||
|
|
||||||
|
const coreDistDir = path.join(path.resolve(__dirname, '../'), 'src/core/dist/core/src');
|
||||||
|
const coreLibDir = path.join(path.resolve(__dirname, '../'), 'src/core.lib/src');
|
||||||
|
|
||||||
|
function copyDir(currentPath, outputDir) {
|
||||||
|
fs.readdir(currentPath, { withFileTypes: true }, (err, entries) => {
|
||||||
|
if (err?.errno === -4058) return;
|
||||||
|
|
||||||
|
entries.forEach(entry => {
|
||||||
|
const localBasePath = path.join(currentPath, entry.name);
|
||||||
|
const outputLocalBasePath = path.join(outputDir, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
// 如果是目录,递归调用
|
||||||
|
if (!fs.existsSync(outputLocalBasePath)) {
|
||||||
|
fs.mkdirSync(outputLocalBasePath, { recursive: true });
|
||||||
|
}
|
||||||
|
copyDir(localBasePath, outputLocalBasePath);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// 如果是文件,直接复制
|
||||||
|
fs.copyFile(localBasePath, outputLocalBasePath, (err) => {
|
||||||
|
if (err) throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
copyDir(coreDistDir, coreLibDir);
|
21
script/gen-version.ts
Normal file
21
script/gen-version.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { version } from '../src/onebot11/version'
|
||||||
|
|
||||||
|
const manifestPath = path.join(__dirname, '../package.json')
|
||||||
|
|
||||||
|
function readManifest (): any {
|
||||||
|
if (fs.existsSync(manifestPath)) {
|
||||||
|
return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeManifest (manifest: any) {
|
||||||
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifest = readManifest()
|
||||||
|
if (version !== manifest.version) {
|
||||||
|
manifest.version = version
|
||||||
|
writeManifest(manifest)
|
||||||
|
}
|
3
script/napcat-custom.bat
Normal file
3
script/napcat-custom.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
chcp 65001
|
||||||
|
set ELECTRON_RUN_AS_NODE=1
|
||||||
|
"H:\Program Files\QQNT最新版\QQ.exe" %~dp0/napcat.cjs %*
|
15
script/napcat-gc.ps1
Normal file
15
script/napcat-gc.ps1
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "Error getting UninstallString: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$params = $args -join " "
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
$Bootfile = Join-Path (Get-Location) "\dist\inject.cjs"
|
||||||
|
$env:ELECTRON_RUN_AS_NODE = 1
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& '$QQpath' --expose-gc $Bootfile $params}"
|
17
script/napcat-log.ps1
Normal file
17
script/napcat-log.ps1
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return "D:\QQ.exe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$params = $args -join " "
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
$Bootfile = Join-Path $PSScriptRoot "napcat.cjs"
|
||||||
|
$env:ELECTRON_RUN_AS_NODE = 1
|
||||||
|
$argumentList = '-noexit', '-noprofile', "-command `"$QQpath`" `"$Bootfile`" $params"
|
||||||
|
Start-Process powershell -ArgumentList $argumentList -RedirectStandardOutput "log.txt" -RedirectStandardError "error.txt"
|
||||||
|
powershell Get-Content -Wait -Encoding UTF8 log.txt
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user