Compare commits

..

7 Commits

Author SHA1 Message Date
手瓜一十雪
fa1bbf6098 release: 1.8.4 2024-08-07 16:03:17 +08:00
手瓜一十雪
f5188c1ec6 feat: 主动/被动临时会话完全支持 2024-08-07 15:16:11 +08:00
手瓜一十雪
6b71f1c345 feat: 主动临时群消息 2024-08-07 14:28:48 +08:00
手瓜一十雪
538acbf7ea chore: fix 2024-08-07 13:19:17 +08:00
手瓜一十雪
d7f97a6fa0 style&chore: 整理代码 2024-08-07 09:53:48 +08:00
手瓜一十雪
4b6cde786e chore: 移除无用代码 2024-08-07 09:39:23 +08:00
手瓜一十雪
b3c1eff137 fix: script error 2024-08-07 09:27:32 +08:00
726 changed files with 27940 additions and 31950 deletions

View File

@@ -15,10 +15,7 @@ charset = utf-8
# 2 space indentation
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
indent_style = space
indent_size = 4
[*.bat]
charset = latin1
indent_size = 2
# 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.

1
.env.development Normal file
View File

@@ -0,0 +1 @@
VITE_BUILD_TYPE = Development

View File

@@ -1,2 +0,0 @@
VITE_BUILD_TYPE = Production
VITE_BUILD_PLATFORM = Framework

View File

@@ -1,2 +1 @@
VITE_BUILD_TYPE = Production
VITE_BUILD_PLATFORM = Shell

68
.eslintrc.cjs Normal file
View 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'],
}
};

View File

@@ -10,7 +10,6 @@ body:
在提交新的 Bug 反馈前,请确保您:
* 已经搜索了现有的 issues并且没有找到可以解决您问题的方法
* 不与现有的某一 issue 重复
* 不涉及[已经停止维护的特性](https://github.com/NapNeko/NapCatQQ?tab=readme-ov-file#挥别昨日),例如 CQ 码
- type: input
id: system-version
attributes:

View File

@@ -1,47 +1,74 @@
name: "Build Action"
on:
push:
pull_request:
workflow_dispatch:
push:
branches:
- main
permissions: write-all
jobs:
Build-LiteLoader:
build-linux:
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target_platform: [linux]
target_arch: [x64, arm64]
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCatQQ'
submodules: true
ref: main
token: ${{ secrets.NAPCAT_BUILD }}
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NapCat.Framework
- name: Build NuCat Linux
run: |
npm i && cd napcat.webui && npm i && cd .. || exit 1
npm run build:framework && npm run depend || exit 1
rm package-lock.json
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
npm run build:prod
cd dist
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
cd ..
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Framework
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
path: dist
Build-Shell:
build-win32:
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target_platform: [win32]
target_arch: [x64,ia32]
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
with:
repository: 'NapNeko/NapCatQQ'
submodules: true
ref: main
token: ${{ secrets.NAPCAT_BUILD }}
- name: Use Node.js 20.X
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Build NapCat.Shell
- name: Build NuCat Linux
run: |
npm i && cd napcat.webui && npm i && cd .. || exit 1
npm run build:shell && npm run depend || exit 1
rm package-lock.json
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
npm run build:prod
cd dist
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
cd ..
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Shell
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
path: dist

View File

@@ -30,9 +30,14 @@ jobs:
ls
node ./script/checkVersion.cjs
sh ./checkVersion.sh
Build-LiteLoader:
build-linux:
needs: [check-version]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target_platform: [linux]
target_arch: [x64, arm64]
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
@@ -46,24 +51,28 @@ jobs:
with:
node-version: 20.x
- name: Build NuCat Framework
- name: Build NuCat Linux
run: |
npm i
cd napcat.webui
npm i
cd ..
npm run build:framework
export NAPCAT_BUILDSYS=${{ matrix.target_platform }}
export NAPCAT_BUILDARCH=${{ matrix.target_arch }}
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
npm run build:prod
cd dist
npm i --omit=dev
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
cd ..
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Framework
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
path: dist
Build-Shell:
build-win32:
runs-on: ubuntu-latest
needs: [check-version]
strategy:
fail-fast: false
matrix:
target_platform: [win32]
target_arch: [x64,ia32]
steps:
- name: Clone Main Repository
uses: actions/checkout@v4
@@ -78,61 +87,36 @@ jobs:
with:
node-version: 20.x
- name: Build NuCat Shell
- name: Build NuCat Linux
run: |
npm i
cd napcat.webui
npm i
cd ..
npm run build:shell
export NAPCAT_BUILDSYS=${{ matrix.target_platform }}
export NAPCAT_BUILDARCH=${{ matrix.target_arch }}
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
npm run build:prod
cd dist
npm i --omit=dev
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
cd ..
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: NapCat.Shell
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
path: dist
release-napcat:
needs: [Build-LiteLoader,Build-Shell]
needs: [build-win32,build-linux]
runs-on: ubuntu-latest
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
uses: actions/download-artifact@v4
- name: Compress subdirectories
run: |
cd ./NapCat.Shell/
zip -q -r NapCat.Shell.zip *
cd ..
cd ./NapCat.Framework/
zip -q -r NapCat.Framework.zip *
cd ..
rm ./NapCat.Shell.zip -rf
rm ./NapCat.Framework.zip -rf
mv ./NapCat.Shell/NapCat.Shell.zip ./
mv ./NapCat.Framework/NapCat.Framework.zip ./
for dir in */; do
base=$(basename "$dir")
zip -r "${base}.zip" "$dir"
done
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
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
@@ -146,7 +130,10 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
body_path: CHANGELOG.md
files: |
NapCat.Framework.zip
NapCat.Shell.zip
NapCat.Framework.Windows.Once.zip
NapCat.win32.ia32.zip
NapCat.win32.x64.zip
NapCat.linux.x64.zip
NapCat.linux.arm64.zip
# NapCat.darwin.x64.zip
# NapCat.darwin.arm64.zip
draft: true

69
.github/workflows/test.yml vendored Normal file
View File

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

View File

@@ -1,10 +0,0 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"bracketSpacing": true,
"arrowParens": "always",
"printWidth": 120,
"endOfLine": "auto"
}

380
LICENSE
View File

@@ -1,19 +1,373 @@
Limited Redistribution License for NapCat
Mozilla Public License Version 2.0
==================================
Copyright © 2024 Mlikiowa
1. Definitions
--------------
1. Usage and Reproduction:
- Unauthorized use, reproduction, modification, or distribution of this code is prohibited without explicit permission from the main author of the NapCat repository.
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
2. Redistribution:
- Redistribution of this code is permitted, provided that the full text of this license is included, and the source and copyright information is clearly stated.
- Minor modifications and extensions are allowed for redistribution purposes, but the modified code must not be publicly released.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
3. Non-Commercial Use:
- This code is not to be used for any commercial purposes.
1.3. "Contribution"
means Covered Software of a particular Contributor.
4. Additional Permissions:
- Any rights not explicitly addressed in this license must be requested from and granted by the main author of the NapCat repository.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
5. Disclaimer:
- This code is provided "as is," without any express or implied warranties, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. In no event shall the author be liable for any damages or other liability arising from, out of, or in connection with the use or distribution of this code.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@@ -1,53 +1,33 @@
<div align="center">
![Logo](https://socialify.git.ci/NapNeko/NapCatQQ/image?font=Jost&logo=https%3A%2F%2Fnapneko.github.io%2Fassets%2Flogo.png&name=1&owner=1&pattern=Diagonal%20Stripes&stargazers=1&theme=Auto)
<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>
---
## 欢迎回家
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现
## 项目介绍
## 特性介绍
- [x] **安装简单**:就算是笨蛋也能使用
- [x] **性能友好**:就算是低内存也能使用
- [x] **接口丰富**:就算是没有也能使用
- [x] **稳定好用**:就算是被捉也能使用
NapCatQQ 是基于 PC NTQQ 本体实现一套无头 Bot 框架。
## 使用框架
名字寓意 瞌睡猫QQ像睡着了一样在后台低占用运行的无需GUI界面的NTQQ。
## 如何使用
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
**首次使用**请务必查看如下文档看使用教程
### 文档地址
[Cloudflare.Worker](https://doc.napneko.icu/)
[Cloudflare.HKServer](https://napcat.napneko.icu/)
[Github.IO](https://napneko.github.io/)
[Cloudflare.Pages](https://napneko.pages.dev/)
[Server.Other](https://napcat.cyou/)
**首次使用** 请务必前往 [官方文档](https://napneko.github.io/) 查看使用文档与教程
## 回家旅途
[QQ Group](https://qm.qq.com/q/NWP25OeV0c)
## 项目声明
## 感谢他们
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权
* 请不要在无关地方宣传NapCatQQ本项目只是用于学习 node 相关知识,切勿用于违法用途
感谢 Tencent Tdesign / Vue3 强力驱动 NapCat.WebUi
* NapCat 不会收集用户隐私信息,但是未来可能会为了更好的利于 NapCat 的优化会收集一些设备信息,如 cpu 架构,系统版本等
不过最最重要的 还是需要感谢屏幕前的你哦~
## 相关链接
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
---
## 鸣谢名单
## 特殊感谢
[LLOneBot](https://github.com/LLOneBot/LLOneBot) 相关的开发曾参与本项目
[Lagrange](https://github.com/LagrangeDev/Lagrange.Core)
## 开源附加
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经仓库主作者授权二次分发或基于 NapCat 代码开发。**
<!--
QQ群545402644
-->

View File

@@ -0,0 +1,12 @@
# v1.8.4
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
## 启动的方式
Way03/Way05
## 新增与调整
1. 支持主动临时消息
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View File

@@ -0,0 +1,11 @@
# v1.4.9
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
## 修复与优化
* 修复接口调用问题 接口标准化 APIset_group_add_request
## 新增与调整
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View 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)

View File

@@ -0,0 +1,11 @@
# v1.6.0
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
## 修复与优化
## 新增与调整
* 新增图片subtype属性 区分表情图片与商城图片
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,11 @@
# v1.6.1
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
## 修复与优化
## 新增与调整
* 修复poke异常事件
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,13 @@
# v1.6.2
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
## 修复与优化
* 修复获取Cookies异常崩溃问题
* 尝试修复成员退群缓存问题
* 修复自身退群后群缓存清理问题
## 新增与调整
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,13 @@
# v1.6.3
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
## 修复与优化
* 修复带有groupid的私聊消息异常发送到群聊消息
* 尝试修复rws热重载失效问题
* 尝试修复进群事件无法正常获取uin
## 新增与调整
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,18 @@
# v1.6.4
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
## 使用前警告
1. 在最近版本由于QQ本体大幅变动为了保证NapCat可用性NapCat近期启动与安装方式将将大幅变动请关注文档和社群获取。
2. 在Core上完全执行开源请不要用于违法用途如此可能造成NapCat完全停止更新。
3. 针对原启动方式的围堵NapCat研发了多种方式除此其余理论与扩展的分析和思路将部分展示于Docs以便各位参与开发与维护NapCat。
## 其余·备注
启动方式: WayBoot.03 Electron Main进程为Node 直接注入代码 同理项目: LiteLoader
## 修复与优化
1. 支持Win平台 9.9.12
2. 修复部分发送图片下载异常情况
## 新增与调整
没有哦
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,18 @@
# v1.6.5
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
## 使用前警告
1. 在最近版本由于QQ本体大幅变动为了保证NapCat可用性NapCat近期启动与安装方式将将大幅变动请关注文档和社群获取。
2. 在Core上完全执行开源请不要用于违法用途如此可能造成NapCat完全停止更新。
3. 针对原启动方式的围堵NapCat研发了多种方式除此其余理论与扩展的分析和思路将部分展示于Docs以便各位参与开发与维护NapCat。
## 其余·备注
启动方式: WayBoot.03 Electron Main进程为Node 直接注入代码 同理项目: LiteLoader
## 修复与优化
1. 优化了WrapperNative载入代码
2. 优化缓存
## 新增与调整
没有哦
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,17 @@
# v1.6.6
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
## 使用前警告
1. 在最近版本由于QQ本体大幅变动为了保证NapCat可用性NapCat近期启动与安装方式将将大幅变动请关注文档和社群获取。
2. 在Core上完全执行开源请不要用于违法用途如此可能造成NapCat完全停止更新。
3. 针对原启动方式的围堵NapCat研发了多种方式除此其余理论与扩展的分析和思路将部分展示于Docs以便各位参与开发与维护NapCat。
## 其余·备注
启动方式: WayBoot.03 Electron Main进程为Node 直接注入代码 同理项目: LiteLoader
## 修复与优化
1. 修复了一些问题
## 新增与调整
没有哦
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)

View File

@@ -0,0 +1,49 @@
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";

View 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;
```

View File

@@ -0,0 +1,16 @@
# 开发方向
方向一 NativeCall/Hook:
1. 崩溃检测机制的实现
2. Api_Caller 的Hook 可以拿到Event/Handler 进一步提升NC 即时的拦截与处理一些事件比如ReCall拦截
3. Node包装层 进一步分析拿到脱离自带Listener/Adapter可以拿到一些更加底层的数据变动 或许包括更多二进制数据
方向二 全新的无头启动 Way01
1. 基于Node启动原理借助导出符号获取函数地址 再次还原NodeMain
方向三 发包与收包
1. 参考 方向一/3 大概可以收包
2. 发包 (暂时没有计划)
方向四 版本控制
1. 根据不同版本进行逻辑控制
2. 某些参数的自动提取

View File

@@ -0,0 +1,8 @@
# Api方向
## getMsgUniqueId √ 已应用
getMsgUniqueId 传入时间 产出一个唯一ID 发送消息作为一个参数
# Native方向
## magic_load
## api_caller
## NodeMain

View File

@@ -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",
},
}];

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,5 +0,0 @@
const path = require('path');
const CurrentPath = path.dirname(__filename);
(async () => {
await import("file://" + path.join(CurrentPath, './napcat/napcat.mjs'));
})();

View File

@@ -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"
}

View File

@@ -1,4 +0,0 @@
@echo off
REM ./launcher.bat 123456
REM ./launcher-win10.bat 123456
REM 带有REM的为注释 删掉你需要的系统的那行REM这三个单词 修改QQ本脚本启动即可

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

After

Width:  |  Height:  |  Size: 208 KiB

View File

@@ -1,33 +0,0 @@
{
"manifest_version": 4,
"type": "extension",
"name": "NapCatQQ",
"slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现",
"version": "4.2.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"
}
}

View File

@@ -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?

View File

@@ -1,3 +0,0 @@
{
"recommendations": ["Vue.volar"]
}

View File

@@ -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).

View File

@@ -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',
},
},
];

View File

@@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./logo_webui.png" />
<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>

View File

@@ -1,34 +0,0 @@
{
"name": "napcat.webui",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"webui:lint": "eslint --fix src/**/*.{js,ts,vue}",
"webui:dev": "vite",
"webui:build": "vite build",
"webui:preview": "vite preview"
},
"dependencies": {
"eslint-plugin-prettier": "^5.2.1",
"qrcode": "^1.5.4",
"tdesign-icons-vue-next": "^0.3.3",
"tdesign-vue-next": "^1.10.3",
"vue": "^3.5.13",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.14.0",
"@types/qrcode": "^1.5.5",
"@vitejs/plugin-legacy": "^5.4.3",
"@vitejs/plugin-vue": "^5.1.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-vue": "^9.31.0",
"globals": "^15.12.0",
"terser": "^5.36.0",
"typescript": "~5.6.2",
"vite": "^5.4.10",
"vue-tsc": "^2.1.8"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

View File

@@ -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

View File

@@ -1,112 +0,0 @@
<template>
<div id="app" theme-mode="dark">
<router-view />
</div>
<div v-if="show">
<t-sticky-tool shape="round" placement="right-bottom" :offset="[-50, 10]" @click="changeTheme">
<t-sticky-item label="浅色" popup="切换浅色模式">
<template #icon><sunny-icon /></template>
</t-sticky-item>
<t-sticky-item label="深色" popup="切换深色模式">
<template #icon><mode-dark-icon /></template>
</t-sticky-item>
<t-sticky-item label="自动" popup="跟随系统">
<template #icon><control-platform-icon /></template>
</t-sticky-item>
</t-sticky-tool>
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, onUnmounted, ref } from 'vue';
import { ControlPlatformIcon, ModeDarkIcon, SunnyIcon } from 'tdesign-icons-vue-next';
const smallScreen = window.matchMedia('(max-width: 768px)');
interface Item {
label: string;
popup: string;
}
interface Context {
item: Item;
}
enum ThemeMode {
Dark = 'dark',
Light = 'light',
Auto = 'auto',
}
const themeLabelMap: Record<string, ThemeMode> = {
"浅色": ThemeMode.Light,
"深色": ThemeMode.Dark,
"自动": ThemeMode.Auto,
};
const show = ref<boolean>(true);
const createSetThemeAttributeFunction = () => {
let mediaQueryForAutoTheme: MediaQueryList | null = null;
return (mode: ThemeMode | null) => {
const element = document.documentElement;
if (mode === ThemeMode.Dark) {
element.setAttribute('theme-mode', ThemeMode.Dark);
} else if (mode === ThemeMode.Light) {
element.removeAttribute('theme-mode');
} else if (mode === ThemeMode.Auto) {
mediaQueryForAutoTheme = window.matchMedia('(prefers-color-scheme: dark)');
const handleMediaChange = (e: MediaQueryListEvent) => {
if (e.matches) {
element.setAttribute('theme-mode', ThemeMode.Dark);
} else {
element.removeAttribute('theme-mode');
}
};
mediaQueryForAutoTheme.addEventListener('change', handleMediaChange);
const event = new Event('change');
Object.defineProperty(event, 'matches', {
value: mediaQueryForAutoTheme.matches,
writable: false,
});
mediaQueryForAutoTheme.dispatchEvent(event);
onBeforeUnmount(() => {
if (mediaQueryForAutoTheme) {
mediaQueryForAutoTheme.removeEventListener('change', handleMediaChange);
}
});
}
};
};
const setThemeAttribute = createSetThemeAttributeFunction();
const getStoredTheme = (): ThemeMode | null => {
return localStorage.getItem('theme') as ThemeMode | null;
};
const initTheme = () => {
const storedTheme = getStoredTheme();
if (storedTheme === null) {
setThemeAttribute(ThemeMode.Auto);
} else {
setThemeAttribute(storedTheme);
}
};
const changeTheme = (context: Context) => {
const themeLabel = themeLabelMap[context.item.label] as ThemeMode;
console.log(themeLabel);
setThemeAttribute(themeLabel);
localStorage.setItem('theme', themeLabel);
};
const haddingFbars = () => {
show.value = !smallScreen.matches;
if (smallScreen.matches) {
localStorage.setItem('theme', 'auto');
}
};
onMounted(() => {
initTheme();
haddingFbars();
window.addEventListener('resize', haddingFbars);
});
onUnmounted(() => {
window.removeEventListener('resize', haddingFbars);
});
</script>
<style scoped></style>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

View File

@@ -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

View File

@@ -1,205 +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 checkQQLoginStatusWithQrcode(): Promise<{ qrcodeurl: string; isLogin: string } | undefined> {
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;
}
}
} catch (error) {
console.error('Error checking QQ login status:', error);
}
return undefined;
}
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: '接口异常' };
}
}

View File

@@ -1,58 +0,0 @@
<template>
<t-layout class="dashboard-container">
<div ref="menuRef">
<SidebarMenu :menu-items="menuItems" class="sidebar-menu" />
</div>
<t-layout>
<router-view />
</t-layout>
</t-layout>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import SidebarMenu from './webui/Nav.vue';
import emitter from '@/ts/event-bus';
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' },
]);
const menuRef = ref<HTMLDivElement | null>(null);
emitter.on('sendMenu', (event) => {
emitter.emit('sendWidth', menuRef.value?.offsetWidth);
localStorage.setItem('menuWidth', menuRef.value?.offsetWidth?.toString() || '0');
});
onMounted(() => {
localStorage.setItem('menuWidth', menuRef.value?.offsetWidth?.toString() || '0');
});
</script>
<style scoped>
.dashboard-container {
display: flex;
flex-direction: row;
height: 100vh;
width: 100%;
}
.sidebar-menu {
position: relative;
z-index: 2;
}
@media (max-width: 768px) {
.content {
padding: 10px;
}
}
</style>

View File

@@ -1,185 +0,0 @@
<template>
<t-card class="layout">
<div class="login-container">
<h2 class="sotheby-font">QQ Login</h2>
<div class="login-methods">
<t-tooltip content="快速登录">
<t-button
id="quick-login"
class="login-method"
:class="{ active: loginMethod === 'quick' }"
@click="loginMethod = 'quick'"
>Quick Login</t-button
>
</t-tooltip>
<t-tooltip content="二维码登录">
<t-button
id="qrcode-login"
class="login-method"
:class="{ active: loginMethod === 'qrcode' }"
@click="loginMethod = 'qrcode'"
>QR Code</t-button
>
</t-tooltip>
</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>
<t-footer class="footer">Power By NapCat.WebUi</t-footer>
</t-card>
</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;
let qrcodeUrl: string = '';
const selectAccount = async (accountName: string): Promise<void> => {
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
if (result) {
if (heartBeatTimer) {
clearInterval(heartBeatTimer);
}
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.checkQQLoginStatusWithQrcode();
if (isLogined?.isLogin) {
if (heartBeatTimer) {
clearInterval(heartBeatTimer);
}
// //判断是否已经调转
// if (router.currentRoute.value.path !== '/dashboard/basic-info') {
// return;
// }
await MessagePlugin.success('登录成功即将跳转');
await router.push({ path: '/dashboard/basic-info' });
} else if (isLogined?.qrcodeurl && qrcodeUrl !== isLogined.qrcodeurl) {
qrcodeUrl = isLogined.qrcodeurl;
generateQrCode(qrcodeUrl, qrcodeCanvas.value);
}
};
const InitPages = async (): Promise<void> => {
quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
qrcodeUrl = await qqLoginManager.getQQLoginQrcode();
generateQrCode(qrcodeUrl, qrcodeCanvas.value);
heartBeatTimer = window.setInterval(HeartBeat, 3000);
};
onMounted(() => {
InitPages();
});
</script>
<style scoped>
.layout {
height: 100vh;
}
.login-container {
padding: 20px;
border-radius: 5px;
max-width: 400px;
min-width: 300px;
position: relative;
margin: 50px 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;
}
</style>

View File

@@ -1,153 +0,0 @@
<template>
<t-card class="layout">
<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>
<t-footer class="footer">Power By NapCat.WebUi</t-footer>
</t-card>
</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>
.layout {
height: 100vh;
}
.login-container {
padding: 20px;
border-radius: 5px;
max-width: 400px;
min-width: 300px;
position: relative;
margin: 50px 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;
}
</style>

View File

@@ -1,127 +0,0 @@
<template>
<t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu">
<template #logo>
<div class="logo">
<img class="logo-img" :width="collapsed ? 35 : 'auto'" src="@/assets/logo_webui.png" alt="logo" />
<div class="logo-textBox">
<div class="logo-text">{{ collapsed ? '' : 'NapCat' }}</div>
</div>
</div>
</template>
<router-link v-for="item in menuItems" :key="item.value" :to="item.route">
<t-tooltip :disabled="!collapsed" :content="item.label" placement="right">
<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>
</t-tooltip>
</router-link>
<template #operations>
<t-button
:disabled="disBtn"
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, onMounted, watch } from 'vue';
import emitter from '@/ts/event-bus';
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 disBtn = ref<boolean>(false);
const changeCollapsed = (): void => {
collapsed.value = !collapsed.value;
iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold';
localStorage.setItem('sidebar-collapsed', collapsed.value.toString());
};
watch(collapsed, (newValue, oldValue) => {
setTimeout(() => {
emitter.emit('sendMenu', collapsed.value);
}, 300);
});
onMounted(() => {
const mediaQuery = window.matchMedia('(max-width: 800px)');
const handleMediaChange = (e: MediaQueryListEvent) => {
disBtn.value = e.matches;
if (e.matches) {
collapsed.value = e.matches;
}
};
mediaQuery.addEventListener('change', handleMediaChange);
const event = new Event('change');
Object.defineProperty(event, 'matches', {
value: mediaQuery.matches,
writable: false,
});
mediaQuery.dispatchEvent(event);
return () => {
mediaQuery.removeEventListener('change', handleMediaChange);
};
});
</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 {
display: flex;
width: auto;
height: 100%;
}
.logo-img {
object-fit: contain;
margin-top: 8px;
margin-bottom: 8px;
}
.logo-textBox {
display: flex;
align-items: center;
margin-left: 10px;
}
.logo-text {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 22px;
font-family: Sotheby, Helvetica, monospace;
}
.menu-item {
margin-bottom: 10px;
}
</style>

View File

@@ -1,6 +0,0 @@
@font-face {
font-family: 'Sotheby';
src: url('../assets/Sotheby.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}

View File

@@ -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;
}
}

View File

@@ -1,87 +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,
Descriptions as TDescriptionsProps,
DescriptionsItem as TDescriptionsItem,
Collapse as TCollapse,
CollapsePanel as TCollapsePanel,
ListItem as TListItem,
Tabs as TTabs,
TabPanel as TTabPanel,
Space as TSpace,
Checkbox as TCheckbox,
Popup as TPopup,
Dialog as TDialog,
Switch as TSwitch,
Tooltip as Tooltip,
StickyTool as TStickyTool,
StickyItem as TStickyItem,
Layout as TLayout,
Content as TContent,
Footer as TFooter,
Aside as TAside,
Popconfirm as Tpopconfirm,
Empty as TEmpty,
} 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(TDescriptionsProps);
app.use(TDescriptionsItem);
app.use(TCollapse);
app.use(TCollapsePanel);
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.use(Tooltip);
app.use(TStickyTool);
app.use(TStickyItem);
app.use(TLayout);
app.use(TContent);
app.use(TFooter);
app.use(TAside);
app.use(Tpopconfirm);
app.use(TEmpty);
app.mount('#app');

View File

@@ -1,66 +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: {{ pkg.version }} </t-tag>
<t-tag class="tag-item" theme="success"> NapCat: {{ napCatVersion }} </t-tag>
<t-tag class="tag-item" theme="success">
TDesign: {{ pkg.dependencies['tdesign-vue-next'] }}
</t-tag>
</span>
</t-list-item>
</t-list>
</div>
</div>
</template>
<script setup lang="ts">
import pkg from '../../package.json';
import { napCatVersion } from '../../../src/common/version';
</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>

View File

@@ -1,6 +0,0 @@
<template>
<div class="basic-info">
<h1>面板基础信息</h1>
<p>这里显示面板的基础信息</p>
</div>
</template>

View File

@@ -1,6 +0,0 @@
<template>
<div class="log-view">
<h1>面板日志信息</h1>
<p>这里显示面板的日志信息</p>
</div>
</template>

View File

@@ -1,540 +0,0 @@
<template>
<div ref="headerBox" class="title">
<t-divider content="网络配置" align="left" />
<t-divider align="right">
<t-button @click="addConfig()">
<template #icon><add-icon /></template>
添加配置</t-button>
</t-divider>
</div>
<div v-if="loadPage" ref="setting" class="setting">
<t-tabs ref="tabsRef" :style="{ width: tabsWidth + 'px' }" default-value="all" @change="selectType">
<t-tab-panel value="all" label="全部"></t-tab-panel>
<t-tab-panel value="httpServers" label="HTTP 服务器"></t-tab-panel>
<t-tab-panel value="httpClients" label="HTTP 客户端"></t-tab-panel>
<t-tab-panel value="websocketServers" label="WebSocket 服务器"></t-tab-panel>
<t-tab-panel value="websocketClients" label="WebSocket 客户端"></t-tab-panel>
</t-tabs>
</div>
<div v-if="loadPage" class="card-box" :style="{ width: tabsWidth + 'px' }">
<div class="setting-box" :style="{ maxHeight: cardHeight + 'px' }" v-if="cardConfig.length > 0">
<div v-for="(item, index) in cardConfig" :key="index">
<t-card :title="item.name" :description="item.type" :style="{ width: cardWidth + 'px' }"
:header-bordered="true" class="setting-card">
<template #actions>
<t-space>
<edit2-icon size="20px" @click="editConfig(item)"></edit2-icon>
<t-popconfirm theme="danger" content="确认删除" @confirm="delConfig(item)">
<delete-icon size="20px"></delete-icon>
</t-popconfirm>
</t-space>
</template>
<div class="setting-content">
<t-card class="card-address" :style="{
borderLeft: '7px solid ' + (item.enable ?
'var(--td-success-color)' :
'var(--td-error-color)')
}">
<div class="local-box" v-if="item.host&&item.port">
<server-filled-icon class="local-icon" size="20px"></server-filled-icon>
<strong class="local">{{ item.host }}:{{ item.port }}</strong>
<copy-icon class="copy-icon" size="20px" @click="copyText(item.host + ':' + item.port)"></copy-icon>
</div>
<div class="local-box" v-if="item.url">
<server-filled-icon class="local-icon" size="20px"></server-filled-icon>
<strong class="local" >{{ item.url }}</strong>
<copy-icon class="copy-icon" size="20px" @click="copyText(item.url)"></copy-icon>
</div>
</t-card>
<t-collapse :default-value="[0]" expand-mutex style="margin-top:10px;" class="info-coll">
<t-collapse-panel header="基础信息">
<t-descriptions size="small" :layout="infoOneCol ? 'vertical' : 'horizontal'"
class="setting-base-info">
<t-descriptions-item v-if="item.token" label="连接密钥">
<div v-if="mediumScreen.matches||largeScreen.matches" class="token-view">
<span>{{ showToken ? item.token : '******' }}</span>
<browse-icon class="browse-icon" v-if="showToken" size="18px"
@click="showToken = false"></browse-icon>
<browse-off-icon class="browse-icon" v-else size="18px"
@click="showToken = true"></browse-off-icon>
</div>
<div v-else>
<t-popup :showArrow="true" trigger="click">
<t-tag theme="primary">点击查看</t-tag>
<template #content>
<div @click="copyText(item.token)">{{item.token}}</div>
</template>
</t-popup>
</div>
</t-descriptions-item>
<t-descriptions-item label="消息格式">{{ item.messagePostFormat }}</t-descriptions-item>
</t-descriptions>
</t-collapse-panel>
<t-collapse-panel header="状态信息">
<t-descriptions size="small" :layout="infoOneCol ? 'vertical' : 'horizontal'"
class="setting-base-info">
<t-descriptions-item v-if="item.hasOwnProperty('debug')" label="调试日志">
<t-tag class="tag-item" :theme="item.debug ? 'success' : 'danger'">
{{ item.debug ? '开启' : '关闭' }}</t-tag>
</t-descriptions-item>
<t-descriptions-item v-if="item.hasOwnProperty('enableWebsocket')"
label="Websocket 功能">
<t-tag class="tag-item" :theme="item.enableWebsocket ? 'success' : 'danger'">
{{ item.enableWebsocket ? '启用' : '禁用' }}</t-tag>
</t-descriptions-item>
<t-descriptions-item v-if="item.hasOwnProperty('enableCors')" label="跨域放行">
<t-tag class="tag-item" :theme="item.enableCors ? 'success' : 'danger'">
{{ item.enableCors ? '开启' : '关闭' }}</t-tag>
</t-descriptions-item>
<t-descriptions-item v-if="item.hasOwnProperty('enableForcePushEvent')"
label="上报自身消息">
<t-tag class="tag-item" :theme="item.reportSelfMessage ? 'success' : 'danger'">
{{ item.reportSelfMessage ? '开启' : '关闭' }}</t-tag>
</t-descriptions-item>
<t-descriptions-item v-if="item.hasOwnProperty('enableForcePushEvent')"
label="强制推送事件">
<t-tag class="tag-item"
:theme="item.enableForcePushEvent ? 'success' : 'danger'">
{{ item.enableForcePushEvent ? '开启' : '关闭' }}</t-tag>
</t-descriptions-item>
</t-descriptions>
</t-collapse-panel>
</t-collapse>
</div>
</t-card>
</div>
<div style="height: 20vh"></div>
</div>
<t-card v-else>
<t-empty class="card-none" title="暂无网络配置"> </t-empty>
</t-card>
</div>
<t-dialog v-model:visible="visibleBody" :header="dialogTitle" :destroy-on-close="true"
:show-in-attached-element="true" placement="center" :on-confirm="saveConfig" class=".t-dialog__ctx .t-dialog--defaul">
<div slot="body" class="dialog-body" >
<t-form ref="form" :data="newTab" labelAlign="left" :model="newTab">
<t-form-item style="text-align: left" :rules="[{ required: true, message: '请输入名称', trigger: 'blur' }]"
label="名称" name="name">
<t-input v-model="newTab.name" />
</t-form-item>
<t-form-item style="text-align: left" :rules="[{ required: true, message: '请选择类型', trigger: 'change' }]"
label="类型" name="type">
<t-select v-model="newTab.type" @change="onloadDefault">
<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>
<div>
<component :is="resolveDynamicComponent(getComponent(newTab.type as ComponentKey))"
:config="newTab.data" />
</div>
</t-form>
</div>
</t-dialog>
</template>
<script setup lang="ts">
import { AddIcon, DeleteIcon, Edit2Icon, ServerFilledIcon, CopyIcon, BrowseOffIcon, BrowseIcon } from 'tdesign-icons-vue-next';
import { onMounted, onUnmounted, ref, resolveDynamicComponent } from 'vue';
import emitter from '@/ts/event-bus';
import {
mergeNetworkDefaultConfig,
mergeOneBotConfigs,
NetworkConfig,
OneBotConfig,
} from '../../../src/onebot/config/config';
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 { MessagePlugin } from 'tdesign-vue-next';
import { QQLoginManager } from '@/backend/shell';
const showToken = ref<boolean>(false);
const infoOneCol = ref<boolean>(true);
const tabsWidth = ref<number>(0);
const menuWidth = ref<number>(0);
const cardWidth = ref<number>(0);
const cardHeight = ref<number>(0);
const mediumScreen = window.matchMedia('(min-width: 768px) and (max-width: 1024px)');
const largeScreen = window.matchMedia('(min-width: 1025px)');
const headerBox = ref<HTMLDivElement | null>(null);
const setting = ref<HTMLDivElement | null>(null);
const loadPage = ref<boolean>(false);
const visibleBody = ref<boolean>(false);
const newTab = ref<{ name: string; data: any; type: string }>({ name: '', data: {}, type: '' });
const dialogTitle = ref<string>('');
type ComponentKey = keyof typeof mergeNetworkDefaultConfig;
const componentMap: Record<
ComponentKey,
| typeof HttpServerComponent
| typeof HttpClientComponent
| typeof WebsocketServerComponent
| typeof WebsocketClientComponent
> = {
httpServers: HttpServerComponent,
httpClients: HttpClientComponent,
websocketServers: WebsocketServerComponent,
websocketClients: WebsocketClientComponent,
};
//操作类型
const operateType = ref<string>('');
//配置项索引
const configIndex = ref<number>(0);
//保存时所用数据
const networkConfig: NetworkConfig & { [key: string]: any; } = {
websocketClients: [],
websocketServers: [],
httpClients: [],
httpServers: [],
};
//挂载的数据
const WebConfg = ref(
new Map<string, Array<null>>([
['all', []],
['httpServers', []],
['httpClients', []],
['websocketServers', []],
['websocketClients', []],
])
);
const typeCh: Record<ComponentKey, string> = {
httpServers: 'HTTP 服务器',
httpClients: 'HTTP 客户端',
websocketServers: 'WebSocket 服务器',
websocketClients: 'WebSocket 客户端',
};
const cardConfig = ref<any>([]);
const getComponent = (type: ComponentKey) => {
return componentMap[type];
};
const getKeyByValue = (obj: typeof typeCh, value: string): string | undefined => {
return Object.entries(obj).find(([_, v]) => v === value)?.[0];
};
const addConfig = () => {
dialogTitle.value = '添加配置';
newTab.value = { name: '', data: {}, type: '' };
operateType.value = 'add';
visibleBody.value = true;
};
const editConfig = (item: any) => {
dialogTitle.value = '修改配置';
const type = getKeyByValue(typeCh, item.type);
if (type) {
newTab.value = { name: item.name, data: item, type: type };
}
operateType.value = 'edit';
configIndex.value = networkConfig[newTab.value.type].findIndex((obj: any) => obj.name === item.name);
visibleBody.value = true;
};
const delConfig = (item: any) => {
const type = getKeyByValue(typeCh, item.type);
if (type) {
newTab.value = { name: item.name, data: item, type: type };
}
configIndex.value = configIndex.value = networkConfig[newTab.value.type].findIndex(
(obj: any) => obj.name === item.name
);
operateType.value = 'delete';
saveConfig();
};
const selectType = (key: ComponentKey) => {
cardConfig.value = WebConfg.value.get(key);
};
const onloadDefault = (key: ComponentKey) => {
console.log(key);
newTab.value.data = structuredClone(mergeNetworkDefaultConfig[key]);
};
//检测重名
const checkName = (name: string) => {
const allConfigs = WebConfg.value.get('all')?.findIndex((obj: any) => obj.name === name);
if (newTab.value.name === '' || newTab.value.type === '') {
MessagePlugin.error('请填写完整信息');
return false;
} else if (allConfigs === -1 || newTab.value.data.name === name) {
return true;
} else {
MessagePlugin.error('名称已存在');
return false;
}
};
//保存
const saveConfig = async () => {
if (operateType.value == 'add') {
if (!checkName(newTab.value.name)) return;
newTab.value.data.name = newTab.value.name;
networkConfig[newTab.value.type].push(newTab.value.data);
} else if (operateType.value == 'edit') {
if (!checkName(newTab.value.name)) return;
newTab.value.data.name = newTab.value.name;
networkConfig[newTab.value.type][configIndex.value] = newTab.value.data;
} else if (operateType.value == 'delete') {
networkConfig[newTab.value.type].splice(configIndex.value, 1);
}
const userConfig = await getOB11Config();
if (!userConfig) return;
userConfig.network = networkConfig;
const success = await setOB11Config(userConfig);
if (success) {
operateType.value = '';
configIndex.value = 0;
MessagePlugin.success('配置保存成功');
await loadConfig();
visibleBody.value = false;
} else {
MessagePlugin.error('配置保存失败');
}
};
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 getAllData = (data: NetworkConfig) => {
cardConfig.value = [];
WebConfg.value.set('all', []);
for (const key in data) {
const configs = data[key as keyof NetworkConfig];
if (key in mergeNetworkDefaultConfig) {
networkConfig[key] = [...configs];
const newConfigsArray = configs.map((config: any) => ({
...config,
type: typeCh[key as ComponentKey],
}));
WebConfg.value.set(key, newConfigsArray);
const allConfigs = WebConfg.value.get('all');
if (allConfigs) {
const newAllConfigs = [...allConfigs, ...newConfigsArray];
WebConfg.value.set('all', newAllConfigs);
}
cardConfig.value = WebConfg.value.get('all');
}
}
};
const loadConfig = async () => {
try {
const userConfig = await getOB11Config();
if (!userConfig) return;
const mergedConfig = mergeOneBotConfigs(userConfig);
getAllData(mergedConfig.network);
} catch (error) {
console.error('Error loading config:', error);
}
};
const copyText = async (text: string) => {
const input = document.createElement('input');
input.value = text;
document.body.appendChild(input);
input.select();
await navigator.clipboard.writeText(text);
document.body.removeChild(input);
MessagePlugin.success('复制成功');
};
const handleResize = () => {
// 得根据卡片宽度改,懒得改了;先不管了
// if(window.innerWidth < 540) {
// infoOneCol.value= true
// } else {
// infoOneCol.value= false
// }
tabsWidth.value = window.innerWidth - 41 - menuWidth.value;
if (mediumScreen.matches) {
cardWidth.value = (tabsWidth.value - 20) / 2;
} else if (largeScreen.matches) {
cardWidth.value = (tabsWidth.value - 40) / 3;
} else {
cardWidth.value = tabsWidth.value;
}
loadPage.value = true;
setTimeout(() => {
cardHeight.value = window.innerHeight - (headerBox.value?.offsetHeight ?? 0) - (setting.value?.offsetHeight ?? 0) - 21;
}, 300);
};
emitter.on('sendWidth', (width) => {
if (typeof width === 'number' && !isNaN(width)) {
menuWidth.value = width;
handleResize();
}
});
onMounted(() => {
loadConfig();
const cachedWidth = localStorage.getItem('menuWidth');
if (cachedWidth) {
menuWidth.value = parseInt(cachedWidth);
setTimeout(() => {
handleResize();
}, 300);
}
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped>
.title {
padding: 20px 20px 0 20px;
display: flex;
justify-content: space-between;
}
.setting {
margin: 0 20px;
}
.setting-box {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
overflow-y: auto;
}
.setting-card {
width: 100%;
text-align: left;
}
.setting-content {
width: 100%;
}
.card-address svg {
fill: var(--td-brand-color);
cursor: pointer;
}
.local-box {
display: flex;
margin-top: 2px;
}
.local-icon{
flex: 1;
}
.local {
flex: 6;
margin: 0 10px 0 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.copy-icon {
flex: 1;
cursor: pointer;
flex-direction: row;
}
.token-view {
display: flex;
align-items: center;
}
.token-view span {
flex: 5;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.browse-icon{
flex: 2;
}
:global(.t-dialog__ctx .t-dialog--defaul) {
margin: 0 20px;
}
@media (max-width: 1024px) {
.setting-box {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 786px) {
.setting-box {
grid-template-columns: 1fr;
}
}
.card-box {
margin: 10px 20px 0 20px;
}
.card-none {
line-height: 400px !important;
}
.dialog-body {
max-height: 60vh;
overflow-y: auto;
}
::-webkit-scrollbar {
width: 0;
background: transparent;
}
</style>
<style>
.setting-card .t-card__title {
text-align: left !important;
}
.setting-card .t-card__description {
margin-bottom: 0;
font-size: 12px;
}
.card-address .t-card__body {
display: flex;
flex-direction: row;
align-items: center;
}
.setting-base-info .t-descriptions__header {
font-size: 15px;
margin-bottom: 0;
}
.setting-base-info .t-descriptions__label {
padding: 0 var(--td-comp-paddingLR-l) !important;
}
.setting-base-info tr>td:last-child {
text-align: right;
}
.info-coll .t-collapse-panel__wrapper .t-collapse-panel__content {
padding: var(--td-comp-paddingTB-m) var(--td-comp-paddingLR-l);
}
</style>

View File

@@ -1,148 +0,0 @@
<template>
<div class="title">
<t-divider content="其余配置" align="left" />
</div>
<t-card class="card">
<div class="other-config-container">
<div class="other-config">
<t-form ref="form" :model="otherConfig" :label-align="labelAlign" label-width="auto" colon>
<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-item label="启用上报解析合并消息" name="parseMultMsg" class="form-item">
<t-switch v-model="otherConfig.parseMultMsg" />
</t-form-item>
</t-form>
<div class="button-container">
<t-button @click="saveConfig">保存</t-button>
</div>
</div>
</div>
</t-card>
</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,
parseMultMsg: true
});
const labelAlign = ref<string>();
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;
otherConfig.value.parseMultMsg = userConfig.parseMultMsg;
}
} 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;
userConfig.parseMultMsg = otherConfig.value.parseMultMsg ?? true;
const success = await setOB11Config(userConfig);
if (success) {
MessagePlugin.success('配置保存成功');
} else {
MessagePlugin.error('配置保存失败');
}
}
} catch (error) {
console.error('Error saving config:', error);
MessagePlugin.error('配置保存失败');
}
};
onMounted(() => {
loadConfig();
const mediaQuery = window.matchMedia('(max-width: 768px)');
const handleMediaChange = (e: MediaQueryListEvent) => {
if (e.matches) {
labelAlign.value = 'top';
} else {
labelAlign.value = 'left';
}
};
mediaQuery.addEventListener('change', handleMediaChange);
const event = new Event('change');
Object.defineProperty(event, 'matches', {
value: mediaQuery.matches,
writable: false,
});
mediaQuery.dispatchEvent(event);
return () => {
mediaQuery.removeEventListener('change', handleMediaChange);
};
});
</script>
<style scoped>
.title {
padding: 20px 20px 0 20px;
}
.card {
margin: 0 20px;
padding-top: 20px;
padding-bottom: 20px;
}
.other-config-container {
display: flex;
justify-content: center;
align-items: flex-start;
box-sizing: border-box;
}
.other-config {
width: 100%;
max-width: 500px;
border-radius: 8px;
}
.form-item {
margin-bottom: 20px;
text-align: left;
}
.button-container {
display: flex;
justify-content: center;
margin-top: 20px;
}
</style>

View File

@@ -1,49 +0,0 @@
<template>
<div>
<t-form labelAlign="left">
<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>
</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></style>

View File

@@ -1,55 +0,0 @@
<template>
<div>
<t-form labelAlign="left">
<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>
</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></style>

View File

@@ -1,52 +0,0 @@
<template>
<div>
<t-form labelAlign="left">
<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>
</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></style>

View File

@@ -1,58 +0,0 @@
<template>
<div>
<t-form labelAlign="left">
<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>
</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></style>

View File

@@ -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,
});

View File

@@ -1,3 +0,0 @@
import mitt from 'mitt';
const emitter = mitt();
export default emitter;

View File

@@ -1 +0,0 @@
/// <reference types="vite/client" />

View File

@@ -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"}]
}

View File

@@ -1,11 +0,0 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strictNullChecks": true
},
"include": ["vite.config.ts"]
}

View File

@@ -1,41 +0,0 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import legacy from '@vitejs/plugin-legacy';
import path from 'path';
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
legacy({
targets: ['defaults', 'not IE 11'],
modernPolyfills: ['web.structured-clone'],
}),
],
base: './',
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
server: {
proxy: {
'/api': 'http://localhost:6099',
},
},
build: {
chunkSizeWarningLimit: 4000,
rollupOptions: {
output: {
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks(id: string) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
},
},
},
},
});

View File

@@ -2,59 +2,69 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.2.0",
"version": "1.8.4",
"scripts": {
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
"build:shell": "npm run build:webui && vite build --mode shell || exit 1",
"build:webui": "cd napcat.webui && vite build",
"dev:framework": "vite build --mode framework",
"dev:shell": "vite build --mode shell",
"dev:webui": "cd napcat.webui && npm run webui:dev",
"lint": "eslint --fix src/**/*.{js,ts,vue}",
"watch:dev": "vite --mode development",
"watch:prod": "vite --mode production",
"build:dev": "vite build --mode development",
"build:prod": "vite build --mode production",
"build": "npm run build:dev",
"build:core": "cd ./src/core && npm run build && cd ../.. && node ./script/copy-core.cjs",
"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"
},
"devDependencies": {
"@babel/core": "^7.24.7",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@eslint/compat": "^1.2.2",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.14.0",
"@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-typescript": "^11.1.6",
"@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/node": "^22.0.1",
"@types/jest": "^29.5.12",
"@types/node": "^22.0.0",
"@types/qrcode-terminal": "^0.12.2",
"@types/ws": "^8.5.12",
"@typescript-eslint/eslint-plugin": "^8.3.0",
"@typescript-eslint/parser": "^8.3.0",
"ajv": "^8.13.0",
"async-mutex": "^0.5.0",
"commander": "^12.1.0",
"cors": "^2.8.5",
"eslint": "^9.14.0",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"eslint": "^8.57.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"fast-xml-parser": "^4.3.6",
"file-type": "^19.0.0",
"globals": "^15.12.0",
"image-size": "^1.1.1",
"json-schema-to-ts": "^3.1.1",
"i": "^0.3.7",
"javascript-obfuscator": "^4.1.0",
"rollup": "^4.13.2",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-obfuscator": "^1.1.0",
"typescript": "^5.3.3",
"typescript-eslint": "^8.13.0",
"vite": "^6.0.1",
"vite": "^5.2.6",
"vite-plugin-babel": "^1.2.0",
"vite-plugin-cp": "^4.0.8",
"vite-tsconfig-paths": "^5.1.0",
"winston": "^3.17.0"
"vite-plugin-dts": "^3.8.2",
"vite-tsconfig-paths": "^4.3.2"
},
"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",
"piscina": "^4.7.0",
"image-size": "^1.1.1",
"json-schema-to-ts": "^3.1.0",
"log4js": "^6.9.1",
"qrcode-terminal": "^0.12.0",
"silk-wasm": "^3.6.1",
"ws": "^8.18.0"
"ws": "^8.16.0"
}
}

Some files were not shown because too many files have changed in this diff Show More