mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1070278eaf | ||
![]() |
16b1a6b153 | ||
![]() |
096a7534e0 | ||
![]() |
b0897187d2 | ||
![]() |
885d94882d | ||
![]() |
11a3341e13 | ||
![]() |
231890f78a | ||
![]() |
a272feda6a | ||
![]() |
bc936a0ca7 | ||
![]() |
28030c2d13 | ||
![]() |
7fe9176286 | ||
![]() |
1105f9b8d6 | ||
![]() |
e4653defa8 | ||
![]() |
1c62a1e839 | ||
![]() |
3a3bbfe201 | ||
![]() |
1c38833998 | ||
![]() |
38894177ee | ||
![]() |
dce8416942 | ||
![]() |
14219e9b42 | ||
![]() |
7b459e7502 | ||
![]() |
31824c0504 | ||
![]() |
e203abae85 | ||
![]() |
faf83b680b | ||
![]() |
67dcbcb842 | ||
![]() |
6533a25404 | ||
![]() |
4dc760b0e9 | ||
![]() |
25933b9043 | ||
![]() |
a53aaa456e | ||
![]() |
e8a7ea07a5 | ||
![]() |
8817dc6b10 | ||
![]() |
491ec04b46 | ||
![]() |
8a5d4a683b | ||
![]() |
dfc7c7357a | ||
![]() |
690a2f7d34 | ||
![]() |
58f22b24e4 | ||
![]() |
3cce9f528b | ||
![]() |
20fd5ac8cb | ||
![]() |
9e05e086eb | ||
![]() |
056e0adddf | ||
![]() |
b36388200d | ||
![]() |
9793e5741a | ||
![]() |
143380c012 | ||
![]() |
4b92254945 | ||
![]() |
f9c1d8b4a6 | ||
![]() |
c0c469339b | ||
![]() |
0ca6343ed7 | ||
![]() |
3db74c3427 |
386
LICENSE
386
LICENSE
@@ -1,21 +1,373 @@
|
||||
MIT License
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
Copyright (c) 2024 NapCatQQ
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
@@ -1,11 +0,0 @@
|
||||
# v1.7.6
|
||||
|
||||
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
|
||||
|
||||
## 启动的方式
|
||||
Way03/Way05
|
||||
|
||||
## 新增与调整
|
||||
1. 支持 9.9.15/3.2.12 版本QQ
|
||||
|
||||
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
21
docs/changelogs/CHANGELOG.v1.7.9.md
Normal file
21
docs/changelogs/CHANGELOG.v1.7.9.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# v1.8.0
|
||||
|
||||
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
|
||||
|
||||
## 启动的方式
|
||||
Way03/Way05
|
||||
|
||||
## 新增与调整
|
||||
1. 消息ID映射到UINT32空间
|
||||
2. 回复ID验证与修复
|
||||
3. 新API /fetch_emoji_like
|
||||
|
||||
```json5
|
||||
{
|
||||
"message_id": 1557274996,//消息ID
|
||||
"emojiType": "1",//可以从event事件获取 一般为1或者2
|
||||
"emojiId": "76"//可以从event事件获取 一个表情对应一个ID
|
||||
}
|
||||
```
|
||||
|
||||
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
@@ -2,7 +2,7 @@
|
||||
"name": "napcat",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "1.7.6",
|
||||
"version": "1.8.0",
|
||||
"scripts": {
|
||||
"watch:dev": "vite --mode development",
|
||||
"watch:prod": "vite --mode production",
|
||||
|
@@ -86,7 +86,7 @@ export abstract class HttpServerBase {
|
||||
this.start(port, host);
|
||||
}
|
||||
|
||||
abstract handleFailed(res: Response, payload: any, err: any): void
|
||||
abstract handleFailed(res: Response, payload: any, err: Error): void
|
||||
|
||||
registerRouter(method: 'post' | 'get' | string, url: string, handler: RegisterHandler) {
|
||||
if (!url.startsWith('/')) {
|
||||
@@ -111,7 +111,7 @@ export abstract class HttpServerBase {
|
||||
try {
|
||||
res.send(await handler(res, payload));
|
||||
} catch (e: any) {
|
||||
this.handleFailed(res, payload, e.stack.toString());
|
||||
this.handleFailed(res, payload, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -84,7 +84,7 @@ export class NTEventWrapper {
|
||||
}
|
||||
//统一回调清理事件
|
||||
async DispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
||||
//console.log(ListenerMainName, ListenerSubName, ...args,this.EventTask.get(ListenerMainName)?.get(ListenerSubName));
|
||||
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args);
|
||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.forEach((task, uuid) => {
|
||||
//console.log(task.func, uuid, task.createtime, task.timeout);
|
||||
if (task.createtime + task.timeout < Date.now()) {
|
||||
|
@@ -2,7 +2,7 @@ import { Peer } from '@/core';
|
||||
import crypto, { randomInt, randomUUID } from 'crypto';
|
||||
import { logError } from './log';
|
||||
|
||||
class LimitedHashTable<K, V> {
|
||||
export class LimitedHashTable<K, V> {
|
||||
private keyToValue: Map<K, V> = new Map();
|
||||
private valueToKey: Map<V, K> = new Map();
|
||||
private maxSize: number;
|
||||
@@ -99,8 +99,10 @@ class MessageUniqueWrapper {
|
||||
}
|
||||
createMsg(peer: Peer, msgId: string): number | undefined {
|
||||
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||
const hash = crypto.createHash('md5').update(key);
|
||||
const shortId = hash.digest().readInt32BE(0);
|
||||
const hash = crypto.createHash('md5').update(key).digest();
|
||||
//设置第一个bit为0 保证shortId为正数
|
||||
hash[0] &= 0x7f;
|
||||
const shortId = hash.readInt32BE(0);
|
||||
//减少性能损耗
|
||||
// const isExist = this.msgIdMap.getKey(shortId);
|
||||
// if (isExist && isExist === msgId) {
|
||||
|
@@ -1,61 +1,49 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import { systemPlatform } from '@/common/utils/system';
|
||||
import { logError } from '@/common/utils/log';
|
||||
export const exePath = process.execPath;
|
||||
import { getDefaultQQVersionConfigInfo, getQQVersionConfigPath } from './helper';
|
||||
import AppidTable from '@/core/external/appid.json';
|
||||
import { log } from './log';
|
||||
|
||||
export const pkgInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'package.json');
|
||||
let configVersionInfoPath;
|
||||
//基础目录获取
|
||||
export let QQMainPath = process.execPath;
|
||||
export let QQPackageInfoPath: string = path.join(path.dirname(QQMainPath), 'resources', 'app', 'package.json');
|
||||
export let QQVersionConfigPath: string | undefined = getQQVersionConfigPath(QQMainPath);
|
||||
|
||||
if (os.platform() !== 'linux') {
|
||||
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');
|
||||
} else {
|
||||
const userPath = os.homedir();
|
||||
const appDataPath = path.resolve(userPath, './.config/QQ');
|
||||
configVersionInfoPath = path.resolve(appDataPath, './versions/config.json');
|
||||
//基础信息获取 无快更则启用默认模板填充
|
||||
export let isQuickUpdate: boolean = !!QQVersionConfigPath;
|
||||
export let QQVersionConfig: QQVersionConfigType = isQuickUpdate ? JSON.parse(fs.readFileSync(QQVersionConfigPath!).toString()) : getDefaultQQVersionConfigInfo();
|
||||
export let QQPackageInfo: QQPackageInfoType = JSON.parse(fs.readFileSync(QQPackageInfoPath).toString());
|
||||
export let { appid: QQVersionAppid, qua: QQVersionQua } = getAppidV2();
|
||||
|
||||
//基础函数
|
||||
export function getQQBuildStr() {
|
||||
return isQuickUpdate ? QQVersionConfig.buildId : QQPackageInfo.buildVersion;
|
||||
}
|
||||
|
||||
if (typeof configVersionInfoPath !== 'string') {
|
||||
throw new Error('Something went wrong when load QQ info path');
|
||||
export function getFullQQVesion() {
|
||||
return isQuickUpdate ? QQVersionConfig.curVersion : QQPackageInfo.version;
|
||||
}
|
||||
|
||||
export { configVersionInfoPath };
|
||||
|
||||
type QQPkgInfo = {
|
||||
version: string;
|
||||
buildVersion: string;
|
||||
platform: string;
|
||||
eleArch: string;
|
||||
export function requireMinNTQQBuild(buildStr: string) {
|
||||
return parseInt(getQQBuildStr()) >= parseInt(buildStr);
|
||||
}
|
||||
type QQVersionConfigInfo = {
|
||||
baseVersion: string;
|
||||
curVersion: string;
|
||||
prevVersion: string;
|
||||
onErrorVersions: Array<any>;
|
||||
buildId: string;
|
||||
//此方法不要直接使用
|
||||
export function getQUAInternal() {
|
||||
return systemPlatform === 'linux' ? `V1_LNX_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B` : `V1_WIN_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B`;
|
||||
}
|
||||
|
||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
||||
'baseVersion': '9.9.15-26702',
|
||||
'curVersion': '9.9.15-26702',
|
||||
'prevVersion': '',
|
||||
'onErrorVersions': [],
|
||||
'buildId': '26702'
|
||||
};
|
||||
|
||||
if (fs.existsSync(configVersionInfoPath)) {
|
||||
export function getAppidV2(): { appid: string, qua: string } {
|
||||
let appidTbale = AppidTable as unknown as QQAppidTableType;
|
||||
try {
|
||||
const _ = JSON.parse(fs.readFileSync(configVersionInfoPath).toString());
|
||||
_qqVersionConfigInfo = Object.assign(_qqVersionConfigInfo, _);
|
||||
} catch (e) {
|
||||
logError('Load QQ version config info failed, Use default version', e);
|
||||
let data = appidTbale[getFullQQVesion()];
|
||||
if (data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
log('[QQ版本兼容性检测] 版本兼容性不佳,可能会导致一些功能无法正常使用', e);
|
||||
}
|
||||
// 以下是兜底措施
|
||||
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: getQUAInternal() };
|
||||
}
|
||||
|
||||
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
||||
//V1_WIN_NQ_9.9.12_25765_GW_B
|
||||
export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toString());
|
||||
// platform_type: 3,
|
||||
// app_type: 4,
|
||||
// app_version: '9.9.12-25765',
|
||||
@@ -63,16 +51,6 @@ export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toSt
|
||||
// appid: '537234702',
|
||||
// platVer: '10.0.26100',
|
||||
// clientVer: '9.9.9-25765',
|
||||
|
||||
// Linux
|
||||
// app_version: '3.2.9-25765',
|
||||
// qua: 'V1_LNX_NQ_3.2.10_25765_GW_B',
|
||||
export function requireMinNTQBuild(buildStr: string) {
|
||||
return parseInt(qqVersionConfigInfo.buildId) >= parseInt(buildStr);
|
||||
}
|
||||
let _appid: string = '537237765'; // 默认为 Windows 平台的 appid
|
||||
if (systemPlatform === 'linux') {
|
||||
_appid = '537237950';
|
||||
}
|
||||
// todo: mac 平台的 appid
|
||||
export const appid = _appid;
|
@@ -1,10 +1,11 @@
|
||||
import crypto from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import fs from 'fs/promises';
|
||||
import fs from 'fs';
|
||||
import { log, logDebug } from './log';
|
||||
import { dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as fsPromise from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -304,14 +305,14 @@ export function migrateConfig(oldConfig: any) {
|
||||
}
|
||||
// 升级旧的配置到新的
|
||||
export async function UpdateConfig() {
|
||||
const configFiles = await fs.readdir(path.join(__dirname, 'config'));
|
||||
const configFiles = await fsPromise.readdir(path.join(__dirname, 'config'));
|
||||
for (const file of configFiles) {
|
||||
if (file.match(/^onebot11_\d+.json$/)) {
|
||||
const CurrentConfig = JSON.parse(await fs.readFile(path.join(__dirname, 'config', file), 'utf8'));
|
||||
const CurrentConfig = JSON.parse(await fsPromise.readFile(path.join(__dirname, 'config', file), 'utf8'));
|
||||
if (isValidOldConfig(CurrentConfig)) {
|
||||
log('正在迁移旧配置到新配置 File:', file);
|
||||
const NewConfig = migrateConfig(CurrentConfig);
|
||||
await fs.writeFile(path.join(__dirname, 'config', file), JSON.stringify(NewConfig, null, 2));
|
||||
await fsPromise.writeFile(path.join(__dirname, 'config', file), JSON.stringify(NewConfig, null, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,7 +332,56 @@ export function isEqual(obj1: any, obj2: any) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
|
||||
if (os.platform() === 'linux') {
|
||||
return {
|
||||
baseVersion: '3.2.12-26702',
|
||||
curVersion: '3.2.12-26702',
|
||||
prevVersion: '',
|
||||
onErrorVersions: [],
|
||||
buildId: '26702'
|
||||
};
|
||||
}
|
||||
return {
|
||||
baseVersion: '9.9.15-26702',
|
||||
curVersion: '9.9.15-26702',
|
||||
prevVersion: '',
|
||||
onErrorVersions: [],
|
||||
buildId: '26702'
|
||||
};
|
||||
}
|
||||
export async function promisePipeline(promises: Promise<any>[], callback: (result: any) => boolean): Promise<void> {
|
||||
let callbackCalled = false;
|
||||
for (const promise of promises) {
|
||||
if (callbackCalled) break;
|
||||
try {
|
||||
const result = await promise;
|
||||
if (!callbackCalled) {
|
||||
callbackCalled = callback(result);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in promise pipeline:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getQQVersionConfigPath(exePath: string = ""): string | undefined {
|
||||
let configVersionInfoPath;
|
||||
if (os.platform() !== 'linux') {
|
||||
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');
|
||||
} else {
|
||||
const userPath = os.homedir();
|
||||
const appDataPath = path.resolve(userPath, './.config/QQ');
|
||||
configVersionInfoPath = path.resolve(appDataPath, './versions/config.json');
|
||||
}
|
||||
if (typeof configVersionInfoPath !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
if (!fs.existsSync(configVersionInfoPath)) {
|
||||
return undefined;
|
||||
}
|
||||
return configVersionInfoPath;
|
||||
}
|
||||
export async function deleteOldFiles(directoryPath: string, daysThreshold: number) {
|
||||
try {
|
||||
const files = await fsPromise.readdir(directoryPath);
|
||||
|
@@ -1,31 +1,17 @@
|
||||
/**
|
||||
* 运行时类型转换与检查类
|
||||
*/
|
||||
export class TypeCheck {
|
||||
static isEmpty(value: any): boolean {
|
||||
return value === null || value === undefined || value === '' ||
|
||||
(Array.isArray(value) && value.length === 0) || (typeof value === 'object' && Object.keys(value).length === 0);
|
||||
}
|
||||
//QQVersionType
|
||||
type QQPackageInfoType = {
|
||||
version: string;
|
||||
buildVersion: string;
|
||||
platform: string;
|
||||
eleArch: string;
|
||||
}
|
||||
|
||||
export class TypeConvert {
|
||||
static toNumber(value: any): number {
|
||||
const num = Number(value);
|
||||
if (isNaN(num)) {
|
||||
throw new Error(`无法将输入转换为数字: ${value}`);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static toString(value: any): string {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
static toBoolean(value: any): boolean {
|
||||
return Boolean(value);
|
||||
}
|
||||
|
||||
static toArray(value: any): any[] {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
}
|
||||
type QQVersionConfigType = {
|
||||
baseVersion: string;
|
||||
curVersion: string;
|
||||
prevVersion: string;
|
||||
onErrorVersions: Array<any>;
|
||||
buildId: string;
|
||||
}
|
||||
type QQAppidTableType = {
|
||||
[key: string]: { appid: string, qua: string };
|
||||
}
|
@@ -1,11 +1,66 @@
|
||||
import { FriendRequest, User } from '@/core/entities';
|
||||
import { napCatCore, OnBuddyChangeParams } from '@/core';
|
||||
import { FriendRequest, FriendV2, SimpleInfo, User } from '@/core/entities';
|
||||
import { BuddyListReqType, napCatCore, NodeIKernelBuddyListener, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
|
||||
import { LimitedHashTable } from '@/common/utils/MessageUnique';
|
||||
import { CacheClassFuncAsyncExtend } from '@/common/utils/helper';
|
||||
export class NTQQFriendApi {
|
||||
static async getBuddyV2(refresh = false): Promise<FriendV2[]> {
|
||||
let uids: string[] = [];
|
||||
const buddyService = napCatCore.session.getBuddyService();
|
||||
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||
);
|
||||
return Array.from(data.values());
|
||||
}
|
||||
@CacheClassFuncAsyncExtend(3600 * 1000, 'getBuddyIdMap', () => true)
|
||||
static async getBuddyIdMapCache(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||
return await NTQQFriendApi.getBuddyIdMap(refresh);
|
||||
}
|
||||
static async getBuddyIdMap(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||
let uids: string[] = [];
|
||||
let retMap: LimitedHashTable<string, string> = new LimitedHashTable<string, string>(5000);
|
||||
const buddyService = napCatCore.session.getBuddyService();
|
||||
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||
);
|
||||
data.forEach((value, key) => {
|
||||
retMap.set(value.uin!, value.uid!);
|
||||
});
|
||||
//console.log('getBuddyIdMap', retMap.getValue);
|
||||
return retMap;
|
||||
}
|
||||
static async getBuddyV2ExWithCate(refresh = false) {
|
||||
let uids: string[] = [];
|
||||
let categoryMap: Map<string, any> = new Map();
|
||||
const buddyService = napCatCore.session.getBuddyService();
|
||||
const buddyListV2 = refresh ? (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data : (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data;
|
||||
uids.push(
|
||||
...buddyListV2.flatMap(item => {
|
||||
item.buddyUids.forEach(uid => {
|
||||
categoryMap.set(uid, { categoryId: item.categoryId, categroyName: item.categroyName });
|
||||
});
|
||||
return item.buddyUids
|
||||
}));
|
||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||
);
|
||||
return Array.from(data).map(([key, value]) => {
|
||||
const category = categoryMap.get(key);
|
||||
return category ? { ...value, categoryId: category.categoryId, categroyName: category.categroyName } : value;
|
||||
});
|
||||
}
|
||||
static async isBuddy(uid: string) {
|
||||
return napCatCore.session.getBuddyService().isBuddy(uid);
|
||||
}
|
||||
/**
|
||||
* @deprecated
|
||||
* @param forced
|
||||
* @returns
|
||||
*/
|
||||
static async getFriends(forced = false): Promise<User[]> {
|
||||
let [_retData, _BuddyArg] = await NTEventDispatch.CallNormalEvent
|
||||
<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType, GroupNotifyTypes, ChatType, Peer } from '../entities';
|
||||
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
|
||||
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType, GroupNotifyTypes, ChatType, Peer, GroupListUpdateType } from '../entities';
|
||||
import { GeneralCallResult, NTQQUserApi, NodeIKernelGroupListener, NodeIKernelGroupService, napCatCore } from '@/core';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { log } from '@/common/utils/log';
|
||||
import { groupMembers } from '../data';
|
||||
@@ -9,20 +9,21 @@ export class NTQQGroupApi {
|
||||
return napCatCore.session.getGroupService().setHeader(gc, filePath);
|
||||
}
|
||||
static async getGroups(forced = false) {
|
||||
type ListenerType = NodeIKernelGroupListener['onGroupListUpdate'];
|
||||
let [_retData, _updateType, groupList] = await NTEventDispatch.CallNormalEvent
|
||||
<(force: boolean) => Promise<any>, (updateType: number, groupList: Group[]) => void>
|
||||
<(force: boolean) => Promise<any>, ListenerType>
|
||||
(
|
||||
'NodeIKernelGroupService/getGroupList',
|
||||
'NodeIKernelGroupListener/onGroupListUpdate',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
(updateType) => true,
|
||||
forced
|
||||
);
|
||||
return groupList;
|
||||
}
|
||||
|
||||
@CacheClassFuncAsyncExtend(600, "LastestSendTime", () => true)
|
||||
@CacheClassFuncAsyncExtend(3600 * 1000, "LastestSendTime", () => true)
|
||||
static async getGroupMemberLastestSendTimeCache(GroupCode: string) {
|
||||
return NTQQGroupApi.getGroupMemberLastestSendTime(GroupCode);
|
||||
}
|
||||
@@ -80,6 +81,9 @@ export class NTQQGroupApi {
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
static async getGroupMemberAll(GroupCode: string, forced = false) {
|
||||
return napCatCore.session.getGroupService().getAllMemberList(GroupCode, forced);
|
||||
}
|
||||
static async getLastestMsg(GroupCode: string, uins: string[]) {
|
||||
let uids: Array<string> = [];
|
||||
for (let uin of uins) {
|
||||
@@ -154,6 +158,25 @@ export class NTQQGroupApi {
|
||||
);
|
||||
return notifies;
|
||||
}
|
||||
static async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
|
||||
type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
|
||||
type EventType = NodeIKernelGroupService['getMemberInfo'];
|
||||
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
|
||||
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
|
||||
const [ret, _groupCode, _changeType, _members] = await NTEventDispatch.CallNormalEvent
|
||||
<EventType, ListenerType>
|
||||
(
|
||||
'NodeIKernelGroupService/getMemberInfo',
|
||||
'NodeIKernelGroupListener/onMemberInfoChange',
|
||||
1,
|
||||
5000,
|
||||
(groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
|
||||
return groupCode == GroupCode && members.has(uid);
|
||||
},
|
||||
GroupCode, [uid], forced
|
||||
);
|
||||
return _members.get(uid);
|
||||
}
|
||||
static async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||
const groupService = napCatCore.session.getGroupService();
|
||||
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
||||
@@ -161,6 +184,7 @@ export class NTQQGroupApi {
|
||||
if (result.errCode !== 0) {
|
||||
throw ('获取群成员列表出错,' + result.errMsg);
|
||||
}
|
||||
|
||||
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values()));
|
||||
return result.result.infos;
|
||||
/*
|
||||
|
@@ -7,7 +7,7 @@ import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
import { MessageUnique } from '../../../common/utils/MessageUnique';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { requireMinNTQBuild } from '@/common/utils/QQBasicInfo';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
async function LoadMessageIdList(Peer: Peer, msgId: string) {
|
||||
let msgList = await NTQQMsgApi.getMsgHistory(Peer, msgId, 50);
|
||||
@@ -17,7 +17,7 @@ async function LoadMessageIdList(Peer: Peer, msgId: string) {
|
||||
}
|
||||
async function loadMessageUnique() {
|
||||
if (groups.size > 100) {
|
||||
logWarn('群数量大于100,可能会导致性能问题');
|
||||
logWarn('[性能检测] 群数量大于100,可能会导致性能问题');
|
||||
}
|
||||
let predict = (groups.size + friends.size / 2) / 5;
|
||||
predict = predict < 20 ? 20 : predict;
|
||||
@@ -67,6 +67,10 @@ setTimeout(() => {
|
||||
// }, 25000)
|
||||
|
||||
export class NTQQMsgApi {
|
||||
static async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
|
||||
//console.log(peer, msgSeq, emojiId, emojiType, count);
|
||||
return napCatCore.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, "", false, 20)
|
||||
}
|
||||
// static napCatCore: NapCatCore | null = null;
|
||||
// enum BaseEmojiType {
|
||||
// NORMAL_EMOJI,
|
||||
@@ -165,7 +169,9 @@ export class NTQQMsgApi {
|
||||
// const msgId = BigInt("0x" + buffer.toString('hex')).toString();
|
||||
// return msgId;
|
||||
// }
|
||||
|
||||
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
|
||||
let data = await NTEventDispatch.CallNormalEvent<
|
||||
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||
@@ -231,7 +237,7 @@ export class NTQQMsgApi {
|
||||
return retMsg;
|
||||
}
|
||||
static async getMsgUnique(chatType: number, time: string) {
|
||||
if (requireMinNTQBuild('26702')) {
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
return napCatCore.session.getMsgService().generateMsgUniqueId(chatType, time);
|
||||
}
|
||||
return napCatCore.session.getMsgService().getMsgUniqueId(time);
|
||||
|
@@ -19,6 +19,43 @@ export interface CustomMusicSignPostData {
|
||||
image?: string,
|
||||
singer?: string
|
||||
}
|
||||
// let t = await napCatCore.session.getGroupService().shareDigest({
|
||||
// appId: "100497308",
|
||||
// appType: 1,
|
||||
// msgStyle: 0,
|
||||
// recvUin: "726067488",
|
||||
// sendType: 1,
|
||||
// clientInfo: {
|
||||
// platform: 1
|
||||
// },
|
||||
// richMsg: {
|
||||
// usingArk: true,
|
||||
// title: "Bot测试title",
|
||||
// summary: "Bot测试summary",
|
||||
// url: "https://www.bilibili.com",
|
||||
// pictureUrl: "https://y.qq.com/music/photo_new/T002R300x300M0000035DC6W4ZpSqf_1.jpg?max_age=2592000",
|
||||
// brief: "Bot测试brief",
|
||||
// }
|
||||
// });
|
||||
// {
|
||||
// errCode: 0,
|
||||
// errMsg: '',
|
||||
// rsp: {
|
||||
// sendType: 1,
|
||||
// recvUin: '726067488',
|
||||
// recvOpenId: '',
|
||||
// errCode: 901501,
|
||||
// errMsg: 'imagent service_error:150_OIDB_NO_PRIV',
|
||||
// extInfo: {
|
||||
// wording: '消息下发失败(错误码:901501)',
|
||||
// jumpResult: 0,
|
||||
// jumpUrl: '',
|
||||
// level: 0,
|
||||
// subLevel: 0,
|
||||
// developMsg: 'imagent error'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// export class MusicSign {
|
||||
// private readonly url: string;
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { ModifyProfileParams, SelfInfo, User, UserDetailInfoByUin } from '@/core/entities';
|
||||
import { ModifyProfileParams, SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '@/core/entities';
|
||||
import { friends, groupMembers, selfInfo } from '@/core/data';
|
||||
import { CacheClassFuncAsync, CacheClassFuncAsyncExtend } from '@/common/utils/helper';
|
||||
import { napCatCore } from '@/core';
|
||||
import { napCatCore, NTQQFriendApi } from '@/core';
|
||||
import { NodeIKernelProfileListener, ProfileListener } from '@/core/listeners';
|
||||
import { RequestUtil } from '@/common/utils/request';
|
||||
import { logWarn } from '@/common/utils/log';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
|
||||
import { requireMinNTQBuild } from '@/common/utils/QQBasicInfo';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
export class NTQQUserApi {
|
||||
static async getProfileLike(uid: string) {
|
||||
@@ -51,20 +51,44 @@ export class NTQQUserApi {
|
||||
return napCatCore.session.getGroupService().setHeader(gc, filePath);
|
||||
}
|
||||
|
||||
static async getSelfInfo() {
|
||||
static async fetchUserDetailInfos(uids: string[]) {
|
||||
//26702 以上使用新接口 .Dev Mlikiowa
|
||||
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||
let retData: User[] = [];
|
||||
let [_retData, _retListener] = await NTEventDispatch.CallNormalEvent
|
||||
<EventService, EventListener>
|
||||
(
|
||||
'NodeIKernelProfileService/fetchUserDetailInfo',
|
||||
'NodeIKernelProfileListener/onUserDetailInfoChanged',
|
||||
uids.length,
|
||||
5000,
|
||||
(profile) => {
|
||||
if (uids.includes(profile.uid)) {
|
||||
let RetUser: User = {
|
||||
...profile.simpleInfo.coreInfo,
|
||||
...profile.simpleInfo.status,
|
||||
...profile.simpleInfo.vasInfo,
|
||||
...profile.commonExt,
|
||||
...profile.simpleInfo.baseInfo,
|
||||
qqLevel: profile.commonExt.qqLevel,
|
||||
pendantId: ""
|
||||
};
|
||||
retData.push(RetUser);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
"BuddyProfileStore",
|
||||
uids,
|
||||
UserDetailSource.KSERVER,
|
||||
[
|
||||
ProfileBizType.KALL
|
||||
]
|
||||
);
|
||||
|
||||
return retData;
|
||||
}
|
||||
|
||||
static async getUserInfo(uid: string) {
|
||||
|
||||
}
|
||||
// enum ProfileBizType {
|
||||
// KALL,
|
||||
// KBASEEXTEND,
|
||||
// KVAS,
|
||||
// KQZONE,
|
||||
// KOTHER
|
||||
// }
|
||||
static async fetchUserDetailInfo(uid: string) {
|
||||
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||
@@ -102,7 +126,7 @@ export class NTQQUserApi {
|
||||
return RetUser;
|
||||
}
|
||||
static async getUserDetailInfo(uid: string) {
|
||||
if (requireMinNTQBuild('26702')) {
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
return this.fetchUserDetailInfo(uid);
|
||||
}
|
||||
return this.getUserDetailInfoOld(uid);
|
||||
@@ -178,22 +202,72 @@ export class NTQQUserApi {
|
||||
}
|
||||
return skey;
|
||||
}
|
||||
@CacheClassFuncAsyncExtend(3600, 'Uin2Uid', (Uin: string, Uid: string | undefined) => {
|
||||
@CacheClassFuncAsyncExtend(3600 * 1000, 'Uin2Uid', (Uin: string, Uid: string | undefined) => {
|
||||
if (Uid && Uid.indexOf('u_') != -1) {
|
||||
return true
|
||||
}
|
||||
logWarn("uin转换到uid时异常", Uin);
|
||||
logWarn("uin转换到uid时异常", Uin, Uid);
|
||||
return false;
|
||||
})
|
||||
static async getUidByUin(Uin: string) {
|
||||
//Uid 接口转
|
||||
let ret = await NTEventDispatch.CallNoListenerEvent
|
||||
<(Uin: string[]) => Promise<{ uidInfo: Map<string, string> }>>(
|
||||
'NodeIKernelUixConvertService/getUid',
|
||||
5000,
|
||||
[Uin]
|
||||
);
|
||||
let uid = ret.uidInfo.get(Uin);
|
||||
//此代码仅临时使用,后期会被废弃
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
return await NTQQUserApi.getUidByUinV2(Uin);
|
||||
}
|
||||
return await NTQQUserApi.getUidByUinV1(Uin);
|
||||
}
|
||||
@CacheClassFuncAsyncExtend(3600 * 1000, 'Uid2Uin', (Uid: string | undefined, Uin: number | undefined) => {
|
||||
if (Uin && Uin != 0 && !isNaN(Uin)) {
|
||||
return true
|
||||
}
|
||||
logWarn("uid转换到uin时异常", Uid, Uin);
|
||||
return false;
|
||||
})
|
||||
static async getUinByUid(Uid: string) {
|
||||
//此代码仅临时使用,后期会被废弃
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
return await NTQQUserApi.getUinByUidV2(Uid);
|
||||
}
|
||||
return await NTQQUserApi.getUinByUidV1(Uid);
|
||||
}
|
||||
|
||||
//后期改成流水线处理
|
||||
static async getUidByUinV2(Uin: string) {
|
||||
let uid = (await napCatCore.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
|
||||
if (uid) return uid;
|
||||
uid = (await napCatCore.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
|
||||
if (uid) return uid;
|
||||
uid = (await napCatCore.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||
if (uid) return uid;
|
||||
console.log((await NTQQFriendApi.getBuddyIdMapCache(true)));
|
||||
uid = (await NTQQFriendApi.getBuddyIdMapCache(true)).getValue(Uin);//从Buddy缓存获取Uid
|
||||
if (uid) return uid;
|
||||
uid = (await NTQQFriendApi.getBuddyIdMap(true)).getValue(Uin);
|
||||
if (uid) return uid;
|
||||
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUinV2(Uin)).detail.uid;//从QQ Native 特殊转换
|
||||
if (unveifyUid.indexOf("*") == -1) uid = unveifyUid;
|
||||
//if (uid) return uid;
|
||||
return uid;
|
||||
}
|
||||
//后期改成流水线处理
|
||||
static async getUinByUidV2(Uid: string) {
|
||||
let uin = (await napCatCore.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await napCatCore.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await napCatCore.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await NTQQFriendApi.getBuddyIdMapCache(true)).getKey(Uid);//从Buddy缓存获取Uin
|
||||
if (uin) return uin;
|
||||
uin = (await NTQQFriendApi.getBuddyIdMap(true)).getKey(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await NTQQUserApi.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||
return uin;
|
||||
}
|
||||
|
||||
static async getUidByUinV1(Uin: string) {
|
||||
// 通用转换开始尝试
|
||||
let uid = (await napCatCore.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||
// Uid 好友转
|
||||
if (!uid) {
|
||||
Array.from(friends.values()).forEach((t) => {
|
||||
@@ -220,17 +294,7 @@ export class NTQQUserApi {
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
@CacheClassFuncAsyncExtend(3600, 'Uid2Uin', (Uid: string | undefined, Uin: number | undefined) => {
|
||||
if (Uin && Uin != 0 && !isNaN(Uin)) {
|
||||
return true
|
||||
}
|
||||
logWarn("uid转换到uin时异常", Uid);
|
||||
return false;
|
||||
})
|
||||
static async getUinByUid(Uid: string | undefined) {
|
||||
if (!Uid) {
|
||||
return '';
|
||||
}
|
||||
static async getUinByUidV1(Uid: string) {
|
||||
let ret = await NTEventDispatch.CallNoListenerEvent
|
||||
<(Uin: string[]) => Promise<{ uinInfo: Map<string, string> }>>(
|
||||
'NodeIKernelUixConvertService/getUin',
|
||||
@@ -270,6 +334,14 @@ export class NTQQUserApi {
|
||||
static async getRecentContactList() {
|
||||
return await napCatCore.session.getRecentContactService().getRecentContactList();
|
||||
}
|
||||
static async getUserDetailInfoByUinV2(Uin: string) {
|
||||
return await NTEventDispatch.CallNoListenerEvent
|
||||
<(Uin: string) => Promise<UserDetailInfoByUinV2>>(
|
||||
'NodeIKernelProfileService/getUserDetailInfoByUin',
|
||||
5000,
|
||||
Uin
|
||||
);
|
||||
}
|
||||
static async getUserDetailInfoByUin(Uin: string) {
|
||||
return NTEventDispatch.CallNoListenerEvent
|
||||
<(Uin: string) => Promise<UserDetailInfoByUin>>(
|
||||
|
@@ -114,6 +114,22 @@ export interface GroupEssenceMsgRet {
|
||||
}
|
||||
}
|
||||
export class WebApi {
|
||||
static async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
|
||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||
const Bkn = WebApi.genBkn(CookiesObject.skey);
|
||||
let ret: any = undefined;
|
||||
const data = 'group_code=' + groupCode + '&msg_seq=' + msgSeq + '&msg_random=' + msgRandom + '&target_group_code=' + targetGroupCode;
|
||||
const url = 'https://qun.qq.com/cgi-bin/group_digest/share_digest?bkn=' + Bkn + "&" + data;
|
||||
//console.log(url);
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': CookieValue });
|
||||
return ret;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@CacheClassFuncAsync(3600 * 1000, 'webapi_get_group_members')
|
||||
static async getGroupEssenceMsg(GroupCode: string, page_start: string) {
|
||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||
|
@@ -15,12 +15,12 @@ import { DependsAdapter, DispatcherAdapter, GlobalAdapter, NodeIGlobalAdapter }
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import fs from 'node:fs';
|
||||
import { appid, qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
|
||||
import { getFullQQVesion, QQVersionAppid, QQVersionQua, requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
import { hostname, systemVersion } from '@/common/utils/system';
|
||||
import { genSessionConfig } from '@/core/sessionConfig';
|
||||
import { sleep } from '@/common/utils/helper';
|
||||
import crypto from 'node:crypto';
|
||||
import { rawFriends, friends, groupMembers, groups, selfInfo, stat } from '@/core/data';
|
||||
import { groupMembers, groups, selfInfo, stat } from '@/core/data';
|
||||
import { GroupMember, RawMessage } from '@/core/entities';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import {
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
setLogSelfInfo
|
||||
} from '@/common/utils/log';
|
||||
import { napCatConfig } from '@/core/utils/config';
|
||||
import { NTQQFriendApi } from './apis';
|
||||
|
||||
export interface OnLoginSuccess {
|
||||
(uin: string, uid: string): void | Promise<void>;
|
||||
@@ -84,7 +85,6 @@ export class NapCatCore {
|
||||
const dataPath = path.resolve(this.dataPath, './NapCat/data');
|
||||
fs.mkdirSync(dataPath, { recursive: true });
|
||||
logDebug('本账号数据/缓存目录:', dataPath);
|
||||
// dbUtil.init(path.resolve(dataPath, `./${arg.uin}-v2.db`)).then(() => {
|
||||
this.initDataListener();
|
||||
this.onLoginSuccessFuncList.map(cb => {
|
||||
new Promise((resolve, reject) => {
|
||||
@@ -94,11 +94,6 @@ export class NapCatCore {
|
||||
}
|
||||
}).then();
|
||||
});
|
||||
// }).catch((e) => {
|
||||
// logError('数据库初始化失败', e);
|
||||
// });
|
||||
// this.initDataListener();
|
||||
|
||||
}).catch((e) => {
|
||||
logError('initSession failed', e);
|
||||
throw new Error(`启动失败: ${JSON.stringify(e)}`);
|
||||
@@ -139,10 +134,10 @@ export class NapCatCore {
|
||||
base_path_prefix: '',
|
||||
platform_type: 3,
|
||||
app_type: 4,
|
||||
app_version: qqVersionConfigInfo.curVersion,
|
||||
app_version: getFullQQVesion(),
|
||||
os_version: 'Windows 10 Pro',
|
||||
use_xlog: true,
|
||||
qua: `V1_WIN_NQ_${qqVersionConfigInfo.curVersion.replace('-', '_')}_GW_B`,
|
||||
qua: QQVersionQua,
|
||||
global_path_config: {
|
||||
desktopGlobalPath: this.dataPathGlobal,
|
||||
},
|
||||
@@ -150,10 +145,10 @@ export class NapCatCore {
|
||||
}, new QQWrapper.NodeIGlobalAdapter(new GlobalAdapter()));
|
||||
this.loginService.initConfig({
|
||||
machineId: '',
|
||||
appid,
|
||||
appid: QQVersionAppid,
|
||||
platVer: systemVersion,
|
||||
commonPath: this.dataPathGlobal,
|
||||
clientVer: qqVersionConfigInfo.curVersion,
|
||||
clientVer: getFullQQVesion(),
|
||||
hostName: hostname
|
||||
});
|
||||
}
|
||||
@@ -258,31 +253,23 @@ export class NapCatCore {
|
||||
stat.packet_received += 1;
|
||||
};
|
||||
this.addListener(msgListener);
|
||||
// 好友相关
|
||||
// 好友相关
|
||||
const buddyListener = new BuddyListener();
|
||||
buddyListener.onBuddyListChange = arg => {
|
||||
rawFriends.length = 0;
|
||||
rawFriends.push(...arg);
|
||||
// console.log('onBuddyListChange', arg);
|
||||
for (const categoryItem of arg) {
|
||||
for (const friend of categoryItem.buddyList) {
|
||||
// console.log("onBuddyListChange", friend)
|
||||
const existFriend = friends.get(friend.uid);
|
||||
if (existFriend) {
|
||||
Object.assign(existFriend, friend);
|
||||
}
|
||||
else {
|
||||
friends.set(friend.uid, friend);
|
||||
}
|
||||
}
|
||||
// console.log("onBuddyListChange", friend)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.addListener(buddyListener);
|
||||
// 刷新一次好友列表
|
||||
this.session.getBuddyService().getBuddyList(true).then(arg => {
|
||||
// console.log('getBuddyList', arg);
|
||||
});
|
||||
// 刷新一次好友列表 26702版本以下需要手动刷新一次获取 高版本NTQQ自带缓存
|
||||
if (!requireMinNTQQBuild('26702')) {
|
||||
this.session.getBuddyService().getBuddyList(true).then(arg => {
|
||||
// console.log('getBuddyList', arg);
|
||||
});
|
||||
} else {
|
||||
// NTQQFriendApi.getBuddyV2(true).then((res) => {
|
||||
// res.forEach((item) => {
|
||||
// CachedIdMap.set(item.uid!, item.uin!);
|
||||
// });
|
||||
// }).catch();
|
||||
}
|
||||
interface SelfStatusInfo {
|
||||
uid: string
|
||||
status: number
|
||||
@@ -335,6 +322,7 @@ export class NapCatCore {
|
||||
if (groupMembers.has(groupCode)) {
|
||||
const existMembers = groupMembers.get(groupCode)!;
|
||||
arg.infos.forEach((member, uid) => {
|
||||
//console.log('onMemberListChange', member);
|
||||
const existMember = existMembers.get(uid);
|
||||
if (existMember) {
|
||||
Object.assign(existMember, member);
|
||||
|
@@ -3,9 +3,11 @@ import {
|
||||
type Group,
|
||||
type GroupMember,
|
||||
type SelfInfo,
|
||||
type BuddyCategoryType
|
||||
type BuddyCategoryType,
|
||||
FriendV2
|
||||
} from './entities';
|
||||
import { isNumeric } from '@/common/utils/helper';
|
||||
import { LimitedHashTable } from '@/common/utils/MessageUnique';
|
||||
import { NTQQGroupApi } from '@/core/apis';
|
||||
|
||||
export const selfInfo: SelfInfo = {
|
||||
@@ -14,8 +16,6 @@ export const selfInfo: SelfInfo = {
|
||||
nick: '',
|
||||
online: true
|
||||
};
|
||||
// 未来只在此处保留 selfInfo stat
|
||||
// groupCode -> Group
|
||||
export const groups: Map<string, Group> = new Map<string, Group>();
|
||||
|
||||
export function deleteGroup(groupQQ: string) {
|
||||
@@ -26,9 +26,10 @@ export function deleteGroup(groupQQ: string) {
|
||||
// 群号 -> 群成员map(uid=>GroupMember)
|
||||
export const groupMembers: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
||||
|
||||
// uid -> Friend 下面这俩个准备移除 QQ里面自带缓存
|
||||
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
||||
export const rawFriends: Array<BuddyCategoryType> = []; // 带分组的好友列表
|
||||
|
||||
//转换列表
|
||||
//export const CachedIdMap = new LimitedHashTable<string, string>(1000);
|
||||
|
||||
export async function getGroup(qq: string | number): Promise<Group | undefined> {
|
||||
let group = groups.get(qq.toString());
|
||||
|
@@ -1,12 +1,18 @@
|
||||
import { QQLevel, Sex } from './user';
|
||||
|
||||
export enum GroupListUpdateType {
|
||||
REFRESHALL,
|
||||
GETALL,
|
||||
MODIFIED,
|
||||
REMOVE
|
||||
}
|
||||
export interface Group {
|
||||
groupCode: string,
|
||||
createTime?: string,//高版本才有
|
||||
maxMember: number,
|
||||
memberCount: number,
|
||||
groupName: string,
|
||||
groupStatus: 0,
|
||||
memberRole: 2,
|
||||
groupStatus: number,
|
||||
memberRole: number,
|
||||
isTop: boolean,
|
||||
toppedTimestamp: string,
|
||||
privilegeFlag: number, //65760
|
||||
@@ -53,4 +59,6 @@ export interface GroupMember {
|
||||
sex?: Sex
|
||||
qqLevel?: QQLevel
|
||||
isChangeRole: boolean;
|
||||
joinTime: string;
|
||||
lastSpeakTime: string;
|
||||
}
|
@@ -9,14 +9,14 @@ export interface BuddyCategoryType {
|
||||
categroyMbCount: number;
|
||||
buddyList: User[];
|
||||
}
|
||||
interface CoreInfo {
|
||||
export interface CoreInfo {
|
||||
uid: string;
|
||||
uin: string;
|
||||
nick: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
interface BaseInfo {
|
||||
export interface BaseInfo {
|
||||
qid: string;
|
||||
longNick: string;
|
||||
birthday_year: number;
|
||||
@@ -162,18 +162,21 @@ interface PhotoWall {
|
||||
picList: Pic[];
|
||||
}
|
||||
|
||||
interface SimpleInfo {
|
||||
uid: string;
|
||||
uin: string;
|
||||
export interface SimpleInfo {
|
||||
uid?: string;
|
||||
uin?: string;
|
||||
coreInfo: CoreInfo;
|
||||
baseInfo: BaseInfo;
|
||||
status: UserStatus;
|
||||
vasInfo: VasInfo;
|
||||
relationFlags: RelationFlags;
|
||||
otherFlags: any;
|
||||
intimate: any;
|
||||
status: UserStatus | null;
|
||||
vasInfo: VasInfo | null;
|
||||
relationFlags: RelationFlags | null;
|
||||
otherFlags: any | null;
|
||||
intimate: any | null;
|
||||
}
|
||||
export interface FriendV2 extends SimpleInfo {
|
||||
categoryId?: number;
|
||||
categroyName?: string;
|
||||
}
|
||||
|
||||
export interface UserDetailInfoListenerArg {
|
||||
uid: string;
|
||||
uin: string;
|
||||
@@ -272,7 +275,18 @@ export interface Friend extends User { }
|
||||
export enum BizKey {
|
||||
KPRIVILEGEICON,
|
||||
KPHOTOWALL
|
||||
}
|
||||
}
|
||||
export interface UserDetailInfoByUinV2 {
|
||||
result: number,
|
||||
errMsg: string,
|
||||
detail: {
|
||||
uid: string,
|
||||
uin: string,
|
||||
simpleInfo: SimpleInfo,
|
||||
commonExt: CommonExt,
|
||||
photoWall: null
|
||||
}
|
||||
}
|
||||
export interface UserDetailInfoByUin {
|
||||
result: number,
|
||||
errMsg: string,
|
||||
|
42
src/core/src/external/appid.json
vendored
Normal file
42
src/core/src/external/appid.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"3.1.2-13107": {
|
||||
"appid": 537146866,
|
||||
"qua": "V1_LNX_NQ_3.1.2-13107_RDM_B"
|
||||
},
|
||||
"3.2.10-25765": {
|
||||
"appid": 537234773,
|
||||
"qua": "V1_LNX_NQ_3.2.10_25765_GW_B"
|
||||
},
|
||||
"3.2.12-26702": {
|
||||
"appid": 537237950,
|
||||
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
|
||||
},
|
||||
"9.9.11-24815": {
|
||||
"appid": 537226656,
|
||||
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
|
||||
},
|
||||
"9.9.12-25493": {
|
||||
"appid": 537231759,
|
||||
"qua": "V1_WIN_NQ_9.9.12_25493_GW_B"
|
||||
},
|
||||
"9.9.12-25765": {
|
||||
"appid": 537234702,
|
||||
"qua": "V1_WIN_NQ_9.9.12_25765_GW_B"
|
||||
},
|
||||
"9.9.12-26299": {
|
||||
"appid": 537234826,
|
||||
"qua": "V1_WIN_NQ_9.9.12_26299_GW_B"
|
||||
},
|
||||
"9.9.12-26339": {
|
||||
"appid": 537234826,
|
||||
"qua": "V1_WIN_NQ_9.9.12_26339_GW_B"
|
||||
},
|
||||
"9.9.12-26466": {
|
||||
"appid": 537234826,
|
||||
"qua": "V1_WIN_NQ_9.9.12_26466_GW_B"
|
||||
},
|
||||
"9.9.15-26702": {
|
||||
"appid": 537237765,
|
||||
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
||||
}
|
||||
}
|
@@ -3,6 +3,8 @@ import { BuddyCategoryType, FriendRequestNotify } from '@/core/entities';
|
||||
export type OnBuddyChangeParams = BuddyCategoryType[]
|
||||
|
||||
interface IBuddyListener {
|
||||
onBuddyListChangedV2(arg: unknown): void,//V2版本 还没兼容
|
||||
|
||||
onBuddyListChange(arg: OnBuddyChangeParams): void,
|
||||
|
||||
onBuddyInfoChange(arg: unknown): void,
|
||||
@@ -44,6 +46,9 @@ export interface NodeIKernelBuddyListener extends IBuddyListener {
|
||||
}
|
||||
|
||||
export class BuddyListener implements IBuddyListener {
|
||||
onBuddyListChangedV2(arg: unknown): void {
|
||||
//throw new Error('Method not implemented.');
|
||||
}
|
||||
onAddBuddyNeedVerify(arg: unknown) {
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Group, GroupMember, GroupNotify } from '@/core/entities';
|
||||
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
|
||||
|
||||
interface IGroupListener {
|
||||
onGroupListUpdate(updateType: number, groupList: Group[]): void;
|
||||
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
|
||||
|
||||
onGroupExtListUpdate(...args: unknown[]): void;
|
||||
|
||||
@@ -96,7 +96,7 @@ export class GroupListener implements IGroupListener {
|
||||
onGroupFirstBulletinNotify(...args: unknown[]) {
|
||||
}
|
||||
|
||||
onGroupListUpdate(updateType: number, groupList: Group[]) {
|
||||
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]) {
|
||||
}
|
||||
|
||||
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]) {
|
||||
@@ -202,7 +202,7 @@ export class DebugGroupListener implements IGroupListener {
|
||||
console.log('onGroupNotifiesUnreadCountUpdated:', ...args);
|
||||
}
|
||||
|
||||
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]){
|
||||
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]) {
|
||||
console.log('onGroupSingleScreenNotifies:');
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,16 @@ export enum BuddyListReqType {
|
||||
}
|
||||
export interface NodeIKernelBuddyService {
|
||||
// 26702 以上
|
||||
getBuddyListV2(callFrom: string, reqType: BuddyListReqType): Promise<GeneralCallResult>;
|
||||
getBuddyListV2(callFrom: string, reqType: BuddyListReqType): Promise<GeneralCallResult & {
|
||||
data: Array<{
|
||||
categoryId: number,
|
||||
categorySortId: number,
|
||||
categroyName: string,
|
||||
categroyMbCount: number,
|
||||
onlineCount: number,
|
||||
buddyUids: Array<string>
|
||||
}>
|
||||
}>;
|
||||
//26702 以上
|
||||
getBuddyListFromCache(callFrom: string): Promise<Array<
|
||||
{
|
||||
@@ -22,7 +31,7 @@ export interface NodeIKernelBuddyService {
|
||||
addKernelBuddyListener(listener: NodeIKernelBuddyListener): number;
|
||||
|
||||
getAllBuddyCount(): number;
|
||||
|
||||
|
||||
removeKernelBuddyListener(listener: unknown): void;
|
||||
|
||||
/**
|
||||
|
@@ -8,17 +8,50 @@ import {
|
||||
} from '@/core/entities';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
|
||||
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
|
||||
|
||||
export interface NodeIKernelGroupService {
|
||||
getMemberCommonInfo(Req: {
|
||||
groupCode: string,
|
||||
startUin: string,
|
||||
identifyFlag: string,
|
||||
uinList: string[],
|
||||
memberCommonFilter: {
|
||||
memberUin: number,
|
||||
uinFlag: number,
|
||||
uinFlagExt: number,
|
||||
uinMobileFlag: number,
|
||||
shutUpTime: number,
|
||||
privilege: number,
|
||||
},
|
||||
memberNum: number,
|
||||
filterMethod: string,
|
||||
onlineFlag: string,
|
||||
realSpecialTitleFlag: number
|
||||
}): Promise<unknown>;
|
||||
//26702
|
||||
getGroupMemberLevelInfo(groupCode: string): Promise<unknown>;
|
||||
//26702
|
||||
getGroupHonorList(groupCodes: Array<string>): unknown;
|
||||
getUinByUids(uins: string[]): Promise<unknown>;
|
||||
getUidByUins(uins: string[]): Promise<unknown>;
|
||||
|
||||
getUinByUids(uins: string[]): Promise<{
|
||||
errCode: number,
|
||||
errMsg: string,
|
||||
uins: Map<string, string>
|
||||
}>;
|
||||
|
||||
getUidByUins(uins: string[]): Promise<{
|
||||
errCode: number,
|
||||
errMsg: string,
|
||||
uids: Map<string, string>
|
||||
}>;
|
||||
//26702(其实更早 但是我不知道)
|
||||
checkGroupMemberCache(arrayList: Array<string>): Promise<unknown>;
|
||||
|
||||
//26702(其实更早 但是我不知道)
|
||||
getGroupLatestEssenceList(groupCode: string): Promise<unknown>;
|
||||
//26702(其实更早 但是我不知道)
|
||||
|
||||
//26702(其实更早 但是我不知道)
|
||||
shareDigest(Req: {
|
||||
appId: string,
|
||||
appType: number,
|
||||
@@ -44,7 +77,19 @@ export interface NodeIKernelGroupService {
|
||||
//26702(其实更早 但是我不知道)
|
||||
fetchGroupEssenceList(Req: { groupCode: string, pageStart: number, pageLimit: number }, Arg: unknown): Promise<unknown>;
|
||||
//26702
|
||||
getAllMemberList(groupCode: string, refresh: boolean): Promise<unknown>;
|
||||
getAllMemberList(groupCode: string, forceFetch: boolean): Promise<{
|
||||
errCode: number,
|
||||
errMsg: string,
|
||||
result: {
|
||||
ids: Array<{
|
||||
uid: string,
|
||||
index: number//0
|
||||
}>,
|
||||
infos: {},
|
||||
finish: true,
|
||||
hasRobot: false
|
||||
}
|
||||
}>;
|
||||
|
||||
setHeader(uid: string, path: string): unknown;
|
||||
|
||||
@@ -54,7 +99,7 @@ export interface NodeIKernelGroupService {
|
||||
|
||||
createMemberListScene(groupCode: string, scene: string): string;
|
||||
|
||||
destroyMemberListScene(): void;
|
||||
destroyMemberListScene(SceneId:string): void;
|
||||
//About Arg (a) name: lastId 根据手Q来看为object {index:?(number),uid:string}
|
||||
getNextMemberList(sceneId: string, a: undefined, num: number): Promise<{
|
||||
errCode: number, errMsg: string,
|
||||
@@ -65,9 +110,9 @@ export interface NodeIKernelGroupService {
|
||||
|
||||
monitorMemberList(): unknown;
|
||||
|
||||
searchMember(uid: string): unknown;
|
||||
searchMember(sceneId: string, keywords: string[]): unknown;
|
||||
|
||||
getMemberInfo(uid: string): unknown;
|
||||
getMemberInfo(group_id: string, uids: string[], forceFetch: boolean): Promise<GeneralCallResult>;
|
||||
//getMemberInfo [ '56729xxxx', [ 'u_4Nj08cwW5Hxxxxx' ], true ]
|
||||
|
||||
kickMember(groupCode: string, memberUids: string[], refuseForever: boolean, kickReason: string): Promise<void>;
|
||||
|
@@ -5,7 +5,7 @@ import { GeneralCallResult } from '@/core/services/common';
|
||||
export interface QueryMsgsParams {
|
||||
chatInfo: Peer,
|
||||
filterMsgType: [],
|
||||
filterSendersUid: [],
|
||||
filterSendersUid: string[],
|
||||
filterMsgFromTime: string,
|
||||
filterMsgToTime: string,
|
||||
pageLimit: number,
|
||||
@@ -477,7 +477,19 @@ export interface NodeIKernelMsgService {
|
||||
|
||||
setMsgEmojiLikes(...args: unknown[]): unknown;
|
||||
|
||||
getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, cookie: string, bForward: boolean, number: number): Promise<unknown>;
|
||||
getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, cookie: string, bForward: boolean, number: number): Promise<{
|
||||
result: number,
|
||||
errMsg: string,
|
||||
emojiLikesList:
|
||||
Array<{
|
||||
tinyId: string,
|
||||
nickName: string,
|
||||
headUrl: string
|
||||
}>,
|
||||
cookie: string,
|
||||
isLastPage: boolean,
|
||||
isFirstPage: boolean
|
||||
}>;
|
||||
|
||||
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
||||
|
||||
@@ -592,7 +604,7 @@ export interface NodeIKernelMsgService {
|
||||
// this.selfPhone = str5;
|
||||
// this.gameSession = tempChatGameSession;
|
||||
prepareTempChat(args: unknown): unknown;//主动临时消息 不做
|
||||
|
||||
|
||||
sendSsoCmdReqByContend(cmd: string, param: string): Promise<unknown>;
|
||||
|
||||
//chattype,uid->Promise<any>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { AnyCnameRecord } from 'node:dns';
|
||||
import { BizKey, ModifyProfileParams, UserDetailInfoByUin } from '../entities';
|
||||
import { BaseInfo, BizKey, CoreInfo, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
|
||||
import { NodeIKernelProfileListener } from '../listeners';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
export enum UserDetailSource {
|
||||
@@ -14,6 +14,21 @@ export enum ProfileBizType {
|
||||
KOTHER
|
||||
}
|
||||
export interface NodeIKernelProfileService {
|
||||
|
||||
getUidByUin(callfrom: string, uin: Array<string>): Promise<Map<string,string>>;//uin->uid
|
||||
|
||||
getUinByUid(callfrom: string, uid: Array<string>): Promise<Map<string,string>>;
|
||||
// {
|
||||
// coreInfo: CoreInfo,
|
||||
// baseInfo: BaseInfo,
|
||||
// status: null,
|
||||
// vasInfo: null,
|
||||
// relationFlags: null,
|
||||
// otherFlags: null,
|
||||
// intimate: null
|
||||
// }
|
||||
getCoreAndBaseInfo(callfrom: string, uids: string[]): Promise<Map<string, SimpleInfo>>;
|
||||
|
||||
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
|
||||
|
||||
addKernelProfileListener(listener: NodeIKernelProfileListener): number;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Peer } from "../entities";
|
||||
import { ChatType, Peer } from "../entities";
|
||||
import { NodeIKernelRecentContactListener } from "../listeners/NodeIKernelRecentContactListener";
|
||||
import { GeneralCallResult } from "./common";
|
||||
export interface FSABRecentContactParams {
|
||||
@@ -39,7 +39,13 @@ export interface NodeIKernelRecentContactService {
|
||||
errCode: number,
|
||||
errMsg: string,
|
||||
sortedContactList: Array<number>,
|
||||
changedList: Array<any>
|
||||
changedList: Array<{
|
||||
remark: any;
|
||||
peerName: any;
|
||||
sendMemberName: any;
|
||||
sendNickName: any;
|
||||
peerUid: string; peerUin: string, msgTime: string, chatType: ChatType, msgId: string
|
||||
}>
|
||||
}
|
||||
}>; // 1 arguments
|
||||
|
||||
|
@@ -21,7 +21,7 @@ export interface NodeIKernelRobotService {
|
||||
|
||||
removeKernelRobotListener(ListenerId: number): unknown;
|
||||
|
||||
getAllRobotFriendsFromCache(): unknown;
|
||||
getAllRobotFriendsFromCache(): Promise<unknown>;
|
||||
|
||||
fetchAllRobots(arg1: unknown, arg2: unknown): unknown;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
export interface NodeIKernelUixConvertService {
|
||||
getUin(uid: string[]): Promise<{ uidInfo: Map<string, string> }>;
|
||||
getUin(uid: string[]): Promise<{ uinInfo: Map<string, string> }>;
|
||||
|
||||
getUid(uin: string[]): Promise<{ uinInfo: Map<string, string> }>;
|
||||
getUid(uin: string[]): Promise<{ uidInfo: Map<string, string> }>;
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
import { appid, qqPkgInfo, qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
|
||||
import { getFullQQVesion, QQVersionAppid } from '@/common/utils/QQBasicInfo';
|
||||
import { hostname, systemName, systemVersion } from '@/common/utils/system';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
@@ -34,42 +34,42 @@ export interface WrapperSessionInitConfig {
|
||||
account_path: string // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
|
||||
}
|
||||
clientVer: string // 9.9.8-22355
|
||||
a2: '',
|
||||
d2: '',
|
||||
d2Key: '',
|
||||
machineId: '',
|
||||
platform: 3, // 3是Windows?
|
||||
a2: string,
|
||||
d2: string,
|
||||
d2Key: string,
|
||||
machineId: string,
|
||||
platform: PlatformType, // 3是Windows?
|
||||
platVer: string, // 系统版本号, 应该可以固定
|
||||
appid: string,
|
||||
rdeliveryConfig: {
|
||||
appKey: '',
|
||||
systemId: 0,
|
||||
appId: '',
|
||||
logicEnvironment: '',
|
||||
platform: 3,
|
||||
language: '',
|
||||
sdkVersion: '',
|
||||
userId: '',
|
||||
appVersion: '',
|
||||
osVersion: '',
|
||||
bundleId: '',
|
||||
serverUrl: '',
|
||||
fixedAfterHitKeys: ['']
|
||||
appKey: string,
|
||||
systemId: number,
|
||||
appId: string,
|
||||
logicEnvironment: string,
|
||||
platform: PlatformType,
|
||||
language: string,
|
||||
sdkVersion: string,
|
||||
userId: string,
|
||||
appVersion: string,
|
||||
osVersion: string,
|
||||
bundleId: string,
|
||||
serverUrl: string,
|
||||
fixedAfterHitKeys: string[]
|
||||
}
|
||||
'defaultFileDownloadPath': string, // 这个可以通过环境变量获取?
|
||||
'deviceInfo': {
|
||||
'guid': string,
|
||||
'buildVer': string,
|
||||
'localId': 2052,
|
||||
'devName': string,
|
||||
'devType': string,
|
||||
'vendorName': '',
|
||||
'osVer': string,
|
||||
'vendorOsName': string,
|
||||
'setMute': false,
|
||||
'vendorType': 0
|
||||
defaultFileDownloadPath: string, // 这个可以通过环境变量获取?
|
||||
deviceInfo: {
|
||||
guid: string,
|
||||
buildVer: string,
|
||||
localId: number,
|
||||
devName: string,
|
||||
devType: string,
|
||||
vendorName: string,
|
||||
osVer: string,
|
||||
vendorOsName: string,
|
||||
setMute: boolean,
|
||||
vendorType: VendorType
|
||||
},
|
||||
'deviceConfig': '{"appearance":{"isSplitViewMode":true},"msg":{}}'
|
||||
deviceConfig: '{"appearance":{"isSplitViewMode":true},"msg":{}}'
|
||||
}
|
||||
|
||||
export const sessionConfig: WrapperSessionInitConfig | any = {};
|
||||
@@ -79,7 +79,7 @@ export async function genSessionConfig(selfUin: string, selfUid: string, account
|
||||
fs.mkdirSync(downloadPath, { recursive: true });
|
||||
let guid: string = await getMachineId();
|
||||
//console.log(guid);
|
||||
// guid = '52afb776-82f6-4e59-9d38-44705b112d0a';
|
||||
// guid = '52afb776-82f6-4e59-9d38-44705b112d0a';
|
||||
//let guid: string = await getMachineId();
|
||||
const config: WrapperSessionInitConfig = {
|
||||
selfUin,
|
||||
@@ -87,20 +87,20 @@ export async function genSessionConfig(selfUin: string, selfUid: string, account
|
||||
desktopPathConfig: {
|
||||
account_path // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
|
||||
},
|
||||
clientVer: qqVersionConfigInfo.curVersion, // 9.9.8-22355
|
||||
clientVer: getFullQQVesion(), // 9.9.8-22355
|
||||
a2: '',
|
||||
d2: '',
|
||||
d2Key: '',
|
||||
machineId: '',
|
||||
platform: 3, // 3是Windows?
|
||||
platform: PlatformType.KWINDOWS, // 3是Windows?
|
||||
platVer: systemVersion, // 系统版本号, 应该可以固定
|
||||
appid: appid,
|
||||
appid: QQVersionAppid,
|
||||
rdeliveryConfig: {
|
||||
appKey: '',
|
||||
systemId: 0,
|
||||
appId: '',
|
||||
logicEnvironment: '',
|
||||
platform: 3,
|
||||
platform: PlatformType.KWINDOWS,
|
||||
language: '',
|
||||
sdkVersion: '',
|
||||
userId: '',
|
||||
@@ -110,22 +110,21 @@ export async function genSessionConfig(selfUin: string, selfUid: string, account
|
||||
serverUrl: '',
|
||||
fixedAfterHitKeys: ['']
|
||||
},
|
||||
'defaultFileDownloadPath': downloadPath,
|
||||
'deviceInfo': {
|
||||
defaultFileDownloadPath: downloadPath,
|
||||
deviceInfo: {
|
||||
guid,
|
||||
'buildVer': qqPkgInfo.version,
|
||||
'localId': 2052,
|
||||
'devName': hostname,
|
||||
'devType': systemName,
|
||||
'vendorName': '',
|
||||
'osVer': systemVersion,
|
||||
'vendorOsName': systemName,
|
||||
'setMute': false,
|
||||
'vendorType': 0
|
||||
buildVer: getFullQQVesion(),
|
||||
localId: 2052,
|
||||
devName: hostname,
|
||||
devType: systemName,
|
||||
vendorName: '',
|
||||
osVer: systemVersion,
|
||||
vendorOsName: systemName,
|
||||
setMute: false,
|
||||
vendorType: VendorType.KNOSETONIOS
|
||||
},
|
||||
'deviceConfig': '{"appearance":{"isSplitViewMode":true},"msg":{}}'
|
||||
deviceConfig: '{"appearance":{"isSplitViewMode":true},"msg":{}}'
|
||||
};
|
||||
Object.assign(sessionConfig, config);
|
||||
// log(sessionConfig);
|
||||
return config;
|
||||
}
|
||||
|
@@ -26,7 +26,6 @@ import {
|
||||
NodeIKernelRichMediaService,
|
||||
NodeIKernelAvatarService,
|
||||
} from './services';
|
||||
import { qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
|
||||
import { NodeIKernelStorageCleanService } from './services/NodeIKernelStorageCleanService';
|
||||
import { NodeIKernelRobotService } from './services/NodeIKernelRobotService';
|
||||
import { dirname } from "node:path"
|
||||
@@ -43,6 +42,7 @@ import { NodeIKernelRecentContactService } from './services/NodeIKernelRecentCon
|
||||
import { NodeIKernelMSFService } from './services/NodeIKernelMSFService';
|
||||
import { NodeIkernelTestPerformanceService } from './services/NodeIkernelTestPerformanceService';
|
||||
import { NodeIKernelECDHService } from './services/NodeIKernelECDHService';
|
||||
import { getFullQQVesion } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -292,7 +292,7 @@ export interface WrapperNodeApi {
|
||||
|
||||
let wrapperNodePath = path.resolve(path.dirname(process.execPath), './resources/app/wrapper.node');
|
||||
if (!fs.existsSync(wrapperNodePath)) {
|
||||
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${qqVersionConfigInfo.curVersion}/wrapper.node`);
|
||||
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${getFullQQVesion()}/wrapper.node`);
|
||||
}
|
||||
const nativemodule: any = { exports: {} };
|
||||
process.dlopen(nativemodule, wrapperNodePath);
|
||||
|
@@ -92,9 +92,9 @@ napCatCore.getQuickLoginList().then((res) => {
|
||||
|
||||
WebUiDataRuntime.setQQQuickLoginCall(async (uin: string) => {
|
||||
const QuickLogin: Promise<{ result: boolean, message: string }> = new Promise((resolve, reject) => {
|
||||
if (quickLoginQQ) {
|
||||
log('正在快速登录 ', quickLoginQQ);
|
||||
napCatCore.quickLogin(quickLoginQQ).then(res => {
|
||||
if (uin) {
|
||||
log('正在快速登录 ', uin);
|
||||
napCatCore.quickLogin(uin).then(res => {
|
||||
if (res.loginErrorInfo.errMsg) {
|
||||
resolve({ result: false, message: res.loginErrorInfo.errMsg });
|
||||
}
|
||||
|
32
src/onebot11/action/extends/FetchEmojioLike.ts
Normal file
32
src/onebot11/action/extends/FetchEmojioLike.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
//getMsgEmojiLikesList
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { NTQQMsgApi } from '@/core/apis';
|
||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
user_id: { type: 'string' },
|
||||
group_id: { type: 'string' },
|
||||
emojiId: { type: 'string' },
|
||||
emojiType: { type: 'string' },
|
||||
message_id: { type: ['string', 'number'] },
|
||||
count: { type: 'number' }
|
||||
},
|
||||
required: ['emojiId', 'emojiType', 'message_id']
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export class FetchEmojioLike extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.FetchEmojioLike;
|
||||
PayloadSchema = SchemaData;
|
||||
protected async _handle(payload: Payload) {
|
||||
let msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||
if(!msgIdPeer) throw new Error('消息不存在');
|
||||
let msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
|
||||
const ret = await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer,msg.msgSeq,payload.emojiId,payload.emojiType,payload.count);
|
||||
return ret;
|
||||
}
|
||||
}
|
@@ -1,12 +1,19 @@
|
||||
import { rawFriends } from '@/core/data';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { BuddyCategoryType } from '@/core/entities/';
|
||||
import { NTQQFriendApi } from '@/core';
|
||||
import { OB11Constructor } from '@/onebot11/constructor';
|
||||
|
||||
export class GetFriendWithCategory extends BaseAction<void, Array<BuddyCategoryType>> {
|
||||
export class GetFriendWithCategory extends BaseAction<void, any> {
|
||||
actionName = ActionName.GetFriendsWithCategory;
|
||||
|
||||
protected async _handle(payload: void) {
|
||||
return rawFriends;
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
//全新逻辑
|
||||
return OB11Constructor.friendsV2(await NTQQFriendApi.getBuddyV2ExWithCate(true));
|
||||
} else {
|
||||
throw new Error('this ntqq version not support, must be 26702 or later');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import { ActionName } from '../types';
|
||||
import { NTQQUserApi } from '@/core/apis/user';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { calcQQLevel } from '@/common/utils/qqlevel';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
@@ -20,24 +21,46 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
|
||||
actionName = ActionName.GoCQHTTP_GetStrangerInfo;
|
||||
|
||||
protected async _handle(payload: Payload): Promise<OB11User> {
|
||||
const user_id = payload.user_id.toString();
|
||||
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
|
||||
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
|
||||
if (!uid || uid.indexOf('*') != -1) {
|
||||
const ret = {
|
||||
...extendData,
|
||||
user_id: parseInt(extendData.info.uin) || 0,
|
||||
nickname: extendData.info.nick,
|
||||
sex: OB11UserSex.unknown,
|
||||
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
|
||||
qid: extendData.info.qid,
|
||||
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
|
||||
login_days: 0,
|
||||
uid: ''
|
||||
};
|
||||
return ret;
|
||||
if (!requireMinNTQQBuild('26702')) {
|
||||
const user_id = payload.user_id.toString();
|
||||
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
|
||||
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
|
||||
if (!uid || uid.indexOf('*') != -1) {
|
||||
const ret = {
|
||||
...extendData,
|
||||
user_id: parseInt(extendData.info.uin) || 0,
|
||||
nickname: extendData.info.nick,
|
||||
sex: OB11UserSex.unknown,
|
||||
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
|
||||
qid: extendData.info.qid,
|
||||
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
|
||||
login_days: 0,
|
||||
uid: ''
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
||||
return OB11Constructor.stranger(data);
|
||||
} else {
|
||||
const user_id = payload.user_id.toString();
|
||||
const extendData = await NTQQUserApi.getUserDetailInfoByUinV2(user_id);
|
||||
//console.log(extendData);
|
||||
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
|
||||
if (!uid || uid.indexOf('*') != -1) {
|
||||
const ret = {
|
||||
...extendData,
|
||||
user_id: parseInt(extendData.detail.uin) || 0,
|
||||
nickname: extendData.detail.simpleInfo.coreInfo.nick,
|
||||
sex: OB11UserSex.unknown,
|
||||
age: 0,
|
||||
level: extendData.detail.commonExt.qqLevel && calcQQLevel(extendData.detail.commonExt.qqLevel) || 0,
|
||||
login_days: 0,
|
||||
uid: ''
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
||||
return OB11Constructor.stranger(data);
|
||||
}
|
||||
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
||||
return OB11Constructor.stranger(data);
|
||||
}
|
||||
}
|
||||
|
@@ -2,10 +2,8 @@ import { OB11Group } from '../../types';
|
||||
import { OB11Constructor } from '../../constructor';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { groups } from '@/core/data';
|
||||
import { NTQQGroupApi } from '@/core/apis';
|
||||
import { Group } from '@/core/entities';
|
||||
import { log } from '@/common/utils/log';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
// no_cache get时传字符串
|
||||
const SchemaData = {
|
||||
@@ -21,11 +19,7 @@ class GetGroupList extends BaseAction<Payload, OB11Group[]> {
|
||||
actionName = ActionName.GetGroupList;
|
||||
PayloadSchema = SchemaData;
|
||||
protected async _handle(payload: Payload) {
|
||||
let groupList: Group[] = Array.from(groups.values());
|
||||
if (groupList.length === 0 || payload?.no_cache === true || payload.no_cache === 'true') {
|
||||
groupList = await NTQQGroupApi.getGroups(true);
|
||||
// log('get groups', groups);
|
||||
}
|
||||
let groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
|
||||
return OB11Constructor.groups(groupList);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { OB11GroupMember } from '../../types';
|
||||
import { getGroup, getGroupMember, groupMembers, selfInfo } from '@/core/data';
|
||||
import { OB11Constructor } from '../../constructor';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
@@ -8,8 +7,8 @@ import { logDebug } from '@/common/utils/log';
|
||||
import { WebApi } from '@/core/apis/webapi';
|
||||
import { NTQQGroupApi } from '@/core';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
|
||||
// no_cache get时传字符串
|
||||
import { getGroupMember, selfInfo } from '@/core/data';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -26,17 +25,12 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||
actionName = ActionName.GetGroupMemberInfo;
|
||||
PayloadSchema = SchemaData;
|
||||
protected async _handle(payload: Payload) {
|
||||
const group = await getGroup(payload.group_id.toString());
|
||||
const role = (await getGroupMember(payload.group_id, selfInfo.uin))?.role;
|
||||
const isPrivilege = role === 3 || role === 4;
|
||||
if (!group) {
|
||||
throw (`群(${payload.group_id})不存在`);
|
||||
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||
let uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||
if (!uid) {
|
||||
throw (`Uin2Uid Error ${payload.user_id}不存在`);
|
||||
}
|
||||
if (payload.no_cache == true || payload.no_cache === 'true') {
|
||||
groupMembers.set(group.groupCode, await NTQQGroupApi.getGroupMembers(payload.group_id.toString()));
|
||||
}
|
||||
const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString());
|
||||
//早返回
|
||||
let member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
|
||||
if (!member) {
|
||||
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||
}
|
||||
@@ -47,26 +41,37 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||
} catch (e) {
|
||||
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||
}
|
||||
const date = Math.round(Date.now() / 1000);
|
||||
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||
if (isPrivilege) {
|
||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||
retMember.qage = webGroupMembers[i]?.qage;
|
||||
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||
if (!requireMinNTQQBuild('26702')) {
|
||||
let SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), selfInfo.uid, isNocache);
|
||||
let isPrivilege = false;
|
||||
if (SelfInfoInGroup) {
|
||||
isPrivilege = SelfInfoInGroup.role === 3 || SelfInfoInGroup.role === 4;
|
||||
}
|
||||
if (isPrivilege) {
|
||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||
retMember.qage = webGroupMembers[i]?.qage;
|
||||
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||
const last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||
retMember.last_sent_time = parseInt(last_send_time);
|
||||
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||
const last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||
retMember.last_sent_time = parseInt(last_send_time);
|
||||
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||
}
|
||||
}
|
||||
retMember.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString());
|
||||
retMember.join_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString());
|
||||
}
|
||||
return retMember;
|
||||
}
|
||||
|
84
src/onebot11/action/group/GetGroupMemberInfoOld.ts
Normal file
84
src/onebot11/action/group/GetGroupMemberInfoOld.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { OB11GroupMember } from '../../types';
|
||||
import { getGroup, getGroupMember, groupMembers, selfInfo } from '@/core/data';
|
||||
import { OB11Constructor } from '../../constructor';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { NTQQUserApi } from '@/core/apis/user';
|
||||
import { logDebug } from '@/common/utils/log';
|
||||
import { WebApi } from '@/core/apis/webapi';
|
||||
import { NTQQGroupApi } from '@/core';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
// no_cache get时传字符串
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
group_id: { type: ['number', 'string'] },
|
||||
user_id: { type: ['number', 'string'] },
|
||||
no_cache: { type: ['boolean', 'string'] },
|
||||
},
|
||||
required: ['group_id', 'user_id']
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||
actionName = ActionName.GetGroupMemberInfo;
|
||||
PayloadSchema = SchemaData;
|
||||
protected async _handle(payload: Payload) {
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
let V2Data = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), payload.user_id.toString(), payload.no_cache == true || payload.no_cache === 'true');
|
||||
if (V2Data) {
|
||||
return OB11Constructor.groupMember(payload.group_id.toString(), V2Data);
|
||||
} else {
|
||||
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||
}
|
||||
}
|
||||
const group = await getGroup(payload.group_id.toString());
|
||||
const role = (await getGroupMember(payload.group_id, selfInfo.uin))?.role;
|
||||
const isPrivilege = role === 3 || role === 4;
|
||||
if (!group) {
|
||||
throw (`群(${payload.group_id})不存在`);
|
||||
}
|
||||
if (payload.no_cache == true || payload.no_cache === 'true') {
|
||||
groupMembers.set(group.groupCode, await NTQQGroupApi.getGroupMembers(payload.group_id.toString()));
|
||||
}
|
||||
const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString());
|
||||
//早返回
|
||||
if (!member) {
|
||||
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||
}
|
||||
//console.log('GetGroupMemberInfo', JSON.stringify(await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), member.uid, true), null, 4));
|
||||
try {
|
||||
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
||||
logDebug('群成员详细信息结果', info);
|
||||
Object.assign(member, info);
|
||||
} catch (e) {
|
||||
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||
}
|
||||
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||
if (isPrivilege) {
|
||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||
retMember.qage = webGroupMembers[i]?.qage;
|
||||
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||
const last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||
retMember.last_sent_time = parseInt(last_send_time);
|
||||
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||
}
|
||||
}
|
||||
}
|
||||
return retMember;
|
||||
}
|
||||
}
|
||||
export default GetGroupMemberInfo;
|
@@ -1,4 +1,4 @@
|
||||
import { getGroup, getGroupMember, groupMembers, selfInfo } from '@/core/data';
|
||||
import { getGroup, getGroupMember, selfInfo } from '@/core/data';
|
||||
import { OB11GroupMember } from '../../types';
|
||||
import { OB11Constructor } from '../../constructor';
|
||||
import BaseAction from '../BaseAction';
|
||||
@@ -6,6 +6,7 @@ import { ActionName } from '../types';
|
||||
import { NTQQGroupApi } from '@/core';
|
||||
import { WebApi } from '@/core/apis/webapi';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
@@ -22,20 +23,16 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
||||
actionName = ActionName.GetGroupMemberList;
|
||||
PayloadSchema = SchemaData;
|
||||
protected async _handle(payload: Payload) {
|
||||
const role = (await getGroupMember(payload.group_id, selfInfo.uin))?.role;
|
||||
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||
|
||||
const group = await getGroup(payload.group_id.toString());
|
||||
const GroupList = await NTQQGroupApi.getGroups(isNocache);
|
||||
const group = GroupList.find(item => item.groupCode == payload.group_id);
|
||||
if (!group) {
|
||||
throw (`群${payload.group_id}不存在`);
|
||||
}
|
||||
|
||||
// 从Data里面获取
|
||||
let _groupMembers: OB11GroupMember[] = OB11Constructor.groupMembers(group);
|
||||
if (payload.no_cache == true || payload.no_cache === 'true') {
|
||||
// webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());'
|
||||
const _groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
|
||||
groupMembers.set(group.groupCode, _groupMembers);
|
||||
}
|
||||
let groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
|
||||
let _groupMembers = Array.from(groupMembers.values())
|
||||
.map(item => { return OB11Constructor.groupMember(group.groupCode, item); });
|
||||
|
||||
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
|
||||
// 转为Map 方便索引
|
||||
@@ -47,31 +44,47 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
||||
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
||||
}
|
||||
|
||||
const isPrivilege = role === 3 || role === 4;
|
||||
if (isPrivilege) {
|
||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||
if (!webGroupMembers[i]?.uin) {
|
||||
continue;
|
||||
if (!requireMinNTQQBuild('26702')) {
|
||||
const selfRole = groupMembers.get(selfInfo.uid)?.role;
|
||||
const isPrivilege = selfRole === 3 || selfRole === 4;
|
||||
|
||||
if (isPrivilege) {
|
||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||
if (!webGroupMembers[i]?.uin) {
|
||||
continue;
|
||||
}
|
||||
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
|
||||
if (MemberData) {
|
||||
MemberData.join_time = webGroupMembers[i]?.join_time;
|
||||
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||
MemberData.qage = webGroupMembers[i]?.qage;
|
||||
MemberData.level = webGroupMembers[i]?.lv.level.toString();
|
||||
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
||||
}
|
||||
}
|
||||
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
|
||||
if (MemberData) {
|
||||
MemberData.join_time = webGroupMembers[i]?.join_time;
|
||||
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||
MemberData.qage = webGroupMembers[i]?.qage;
|
||||
MemberData.level = webGroupMembers[i]?.lv.level.toString();
|
||||
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
||||
} else {
|
||||
if (isNocache) {
|
||||
const DateMap = await NTQQGroupApi.getGroupMemberLastestSendTimeCache(payload.group_id.toString());//开始从本地拉取
|
||||
for (const DateUin of DateMap.keys()) {
|
||||
const MemberData = MemberMap.get(parseInt(DateUin));
|
||||
if (MemberData) {
|
||||
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);
|
||||
//join_time 有基础数据兜底
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_groupMembers.forEach(item => {
|
||||
item.last_sent_time = date;
|
||||
item.join_time = date;
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const DateMap = await NTQQGroupApi.getGroupMemberLastestSendTimeCache(payload.group_id.toString());//开始从本地拉取
|
||||
for (const DateUin of DateMap.keys()) {
|
||||
const MemberData = MemberMap.get(parseInt(DateUin));
|
||||
if (MemberData) {
|
||||
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);
|
||||
//join_time 有基础数据兜底
|
||||
}
|
||||
}
|
||||
_groupMembers.forEach(async item => {
|
||||
item.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.lastSpeakTime || date.toString());
|
||||
item.join_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.joinTime || date.toString());
|
||||
});
|
||||
}
|
||||
// 还原索引到Array 一同返回
|
||||
|
||||
|
@@ -78,8 +78,10 @@ import SetGroupHeader from './extends/SetGroupHeader';
|
||||
import { FetchCustomFace } from './extends/FetchCustomFace';
|
||||
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile';
|
||||
import TestApi01 from './extends/TestApi01';
|
||||
import { FetchEmojioLike } from './extends/FetchEmojioLike';
|
||||
|
||||
export const actionHandlers = [
|
||||
new FetchEmojioLike(),
|
||||
new RebootNormal(),
|
||||
new GetFile(),
|
||||
new Debug(),
|
||||
|
@@ -53,7 +53,7 @@ export async function handleForwardNode(destPeer: Peer, messageNodes: OB11Messag
|
||||
const nodeId = messageNode.data.id;
|
||||
// 有nodeId表示一个子转发消息卡片
|
||||
if (nodeId) {
|
||||
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId));
|
||||
const nodeMsg = MessageUnique.getPeerByMsgId(nodeId);
|
||||
if (!needClone) {
|
||||
nodeMsgIds.push(nodeMsg!.MsgId);
|
||||
} else {
|
||||
|
@@ -10,6 +10,7 @@ import { ActionName, BaseCheckResult } from '@/onebot11/action/types';
|
||||
import { getGroup } from '@/core/data';
|
||||
import { ChatType, ElementType, Group, NTQQFileApi, NTQQFriendApi, NTQQMsgApi, NTQQUserApi, Peer, SendMessageElement, } from '@/core';
|
||||
import fs from 'node:fs';
|
||||
import fsPromise from 'node:fs/promises';
|
||||
import { logDebug, logError } from '@/common/utils/log';
|
||||
import { decodeCQCode } from '@/onebot11/cqcode';
|
||||
import createSendElements from './create-send-elements';
|
||||
@@ -71,14 +72,7 @@ export async function sendMsg(peer: Peer, sendElements: SendMessageElement[], de
|
||||
logDebug('发送消息id获取失败', e);
|
||||
returnMsg!.id = 0;
|
||||
}
|
||||
|
||||
deleteAfterSentFiles.map((f) => {
|
||||
try {
|
||||
fs.unlinkSync(f);
|
||||
} catch (e) {
|
||||
logError('发送消息删除文件失败', e);
|
||||
}
|
||||
});
|
||||
deleteAfterSentFiles.map((f) => { fsPromise.unlink(f).then().catch(e => logError('发送消息删除文件失败', e)); });
|
||||
return returnMsg;
|
||||
}
|
||||
|
||||
@@ -134,7 +128,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||
return { valid: false, message: `群${payload.group_id}不存在` };
|
||||
}
|
||||
if (payload.user_id && payload.message_type !== 'group') {
|
||||
const uid = await NTQQUserApi.getUidByUin(payload.user_id);
|
||||
const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||
const isBuddy = await NTQQFriendApi.isBuddy(uid!);
|
||||
// 此处有问题
|
||||
if (!isBuddy) {
|
||||
|
@@ -102,5 +102,6 @@ export enum ActionName {
|
||||
SetGroupHeader = 'set_group_head',
|
||||
FetchCustomFace = 'fetch_custom_face',
|
||||
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
|
||||
TestApi01 = 'test_api_01'
|
||||
TestApi01 = 'test_api_01',
|
||||
FetchEmojioLike = "fetch_emoji_like"
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { NTQQFriendApi } from '@/core';
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
|
||||
// no_cache get时传字符串
|
||||
@@ -20,6 +21,10 @@ export default class GetFriendList extends BaseAction<Payload, OB11User[]> {
|
||||
actionName = ActionName.GetFriendList;
|
||||
PayloadSchema = SchemaData;
|
||||
protected async _handle(payload: Payload) {
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
//全新逻辑
|
||||
return OB11Constructor.friendsV2(await NTQQFriendApi.getBuddyV2(payload?.no_cache === true || payload?.no_cache === 'true'));
|
||||
}
|
||||
if (friends.size === 0 || payload?.no_cache === true || payload?.no_cache === 'true') {
|
||||
const _friends = await NTQQFriendApi.getFriends(true);
|
||||
// log('强制刷新好友列表,结果: ', _friends)
|
||||
|
@@ -1,12 +1,52 @@
|
||||
|
||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||
import BaseAction from '../BaseAction';
|
||||
import { ActionName } from '../types';
|
||||
import { NTQQUserApi } from '@/core';
|
||||
import { NTQQMsgApi, NTQQUserApi } from '@/core';
|
||||
import { OB11Constructor } from '@/onebot11/constructor';
|
||||
|
||||
export default class GetRecentContact extends BaseAction<void, any> {
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
count: { type: ['number', 'string'] }
|
||||
}
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
|
||||
export default class GetRecentContact extends BaseAction<Payload, any> {
|
||||
actionName = ActionName.GetRecentContact;
|
||||
protected async _handle(payload: void) {
|
||||
//没有效果
|
||||
return await NTQQUserApi.getRecentContactListSnapShot(10);
|
||||
PayloadSchema = SchemaData;
|
||||
protected async _handle(payload: Payload) {
|
||||
let ret = await NTQQUserApi.getRecentContactListSnapShot(parseInt((payload.count || 10).toString()));
|
||||
let data = await Promise.all(ret.info.changedList.map(async (t) => {
|
||||
let FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
|
||||
if (FastMsg.msgList.length > 0) {
|
||||
//扩展ret.info.changedList
|
||||
let lastestMsg = await OB11Constructor.message(FastMsg.msgList[0]);
|
||||
return {
|
||||
lastestMsg: lastestMsg,
|
||||
peerUin: t.peerUin,
|
||||
remark: t.remark,
|
||||
msgTime: t.msgTime,
|
||||
chatType: t.chatType,
|
||||
msgId: t.msgId,
|
||||
sendNickName: t.sendNickName,
|
||||
sendMemberName: t.sendMemberName,
|
||||
peerName: t.peerName
|
||||
};
|
||||
}
|
||||
return {
|
||||
peerUin: t.peerUin,
|
||||
remark: t.remark,
|
||||
msgTime: t.msgTime,
|
||||
chatType: t.chatType,
|
||||
msgId: t.msgId,
|
||||
sendNickName: t.sendNickName,
|
||||
sendMemberName: t.sendMemberName,
|
||||
peerName: t.peerName
|
||||
};
|
||||
}));
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import {
|
||||
ChatType,
|
||||
FaceIndex,
|
||||
Friend,
|
||||
FriendV2,
|
||||
GrayTipElementSubType,
|
||||
Group,
|
||||
GroupMember,
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
RawMessage,
|
||||
SelfInfo,
|
||||
Sex,
|
||||
SimpleInfo,
|
||||
TipGroupElementType,
|
||||
User,
|
||||
VideoElement
|
||||
@@ -90,8 +92,9 @@ export class OB11Constructor {
|
||||
}
|
||||
else if (msg.chatType == ChatType.friend) {
|
||||
resMsg.sub_type = 'friend';
|
||||
const user = await NTQQUserApi.getUserDetailInfoByUin(msg.senderUin!);
|
||||
resMsg.sender.nickname = user.info.nick;
|
||||
resMsg.sender.nickname = (await NTQQUserApi.getUserDetailInfo(msg.senderUid)).nick;
|
||||
//const user = await NTQQUserApi.getUserDetailInfoByUin(msg.senderUin!);
|
||||
//resMsg.sender.nickname = user.info.nick;
|
||||
}
|
||||
else if (msg.chatType == ChatType.temp) {
|
||||
resMsg.sub_type = 'group';
|
||||
@@ -150,21 +153,20 @@ export class OB11Constructor {
|
||||
message_data['type'] = OB11MessageDataType.reply;
|
||||
//log("收到回复消息", element.replyElement);
|
||||
try {
|
||||
//做这么多都是因为NC速度太快 可能nt还没有写入数据库
|
||||
//const records = msg.records.find(msgRecord => msgRecord.msgId === element.replyElement.sourceMsgIdInRecords);
|
||||
const records = msg.records.find(msgRecord => msgRecord.msgId === element.replyElement.sourceMsgIdInRecords);
|
||||
const peer = {
|
||||
chatType: msg.chatType,
|
||||
peerUid: msg.peerUid,
|
||||
guildId: '',
|
||||
};
|
||||
let replyMsg: RawMessage | undefined;
|
||||
|
||||
if (!records) throw new Error('找不到回复消息');
|
||||
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, element.replyElement.replayMsgSeq, 1, true, true)).msgList[0];
|
||||
if (!replyMsg || element.replyElement.replayMsgSeq !== replyMsg.msgSeq) {
|
||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0];
|
||||
}
|
||||
|
||||
if (!replyMsg || element.replyElement.replayMsgSeq !== replyMsg.msgSeq) {
|
||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||
throw new Error('回复消息消息验证失败');
|
||||
}
|
||||
message_data['data']['id'] = MessageUnique.createMsg({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, replyMsg.msgId)?.toString();
|
||||
@@ -206,13 +208,13 @@ export class OB11Constructor {
|
||||
chatType: msg.chatType,
|
||||
guildId: '',
|
||||
},
|
||||
msg.msgId,
|
||||
msg.msgSeq,
|
||||
msg.senderUid,
|
||||
element.elementId,
|
||||
element.elementType.toString(),
|
||||
FileElement.fileSize,
|
||||
FileElement.fileName
|
||||
msg.msgId,
|
||||
msg.msgSeq,
|
||||
msg.senderUid,
|
||||
element.elementId,
|
||||
element.elementType.toString(),
|
||||
FileElement.fileSize,
|
||||
FileElement.fileName
|
||||
);
|
||||
}
|
||||
else if (element.videoElement) {
|
||||
@@ -253,13 +255,13 @@ export class OB11Constructor {
|
||||
chatType: msg.chatType,
|
||||
guildId: '',
|
||||
},
|
||||
msg.msgId,
|
||||
msg.msgSeq,
|
||||
msg.senderUid,
|
||||
element.elementId,
|
||||
element.elementType.toString(),
|
||||
videoElement.fileSize || '0',
|
||||
videoElement.fileName
|
||||
msg.msgId,
|
||||
msg.msgSeq,
|
||||
msg.senderUid,
|
||||
element.elementId,
|
||||
element.elementType.toString(),
|
||||
videoElement.fileSize || '0',
|
||||
videoElement.fileName
|
||||
);
|
||||
}
|
||||
else if (element.pttElement) {
|
||||
@@ -274,13 +276,13 @@ export class OB11Constructor {
|
||||
chatType: msg.chatType,
|
||||
guildId: '',
|
||||
},
|
||||
msg.msgId,
|
||||
msg.msgSeq,
|
||||
msg.senderUid,
|
||||
element.elementId,
|
||||
element.elementType.toString(),
|
||||
element.pttElement.fileSize || '0',
|
||||
element.pttElement.fileUuid || ''
|
||||
msg.msgId,
|
||||
msg.msgSeq,
|
||||
msg.senderUid,
|
||||
element.elementId,
|
||||
element.elementType.toString(),
|
||||
element.pttElement.fileSize || '0',
|
||||
element.pttElement.fileUuid || ''
|
||||
);
|
||||
//以uuid作为文件名
|
||||
}
|
||||
@@ -456,6 +458,7 @@ export class OB11Constructor {
|
||||
});
|
||||
}
|
||||
if (grayTipElement) {
|
||||
//console.log('收到群提示消息', grayTipElement);
|
||||
if (grayTipElement.xmlElement?.templId === '10382') {
|
||||
const emojiLikeData = new fastXmlParser.XMLParser({
|
||||
ignoreAttributes: false,
|
||||
@@ -552,7 +555,24 @@ export class OB11Constructor {
|
||||
nickname: selfInfo.nick,
|
||||
};
|
||||
}
|
||||
|
||||
static friendsV2(friends: FriendV2[]): OB11User[] {
|
||||
const data: OB11User[] = [];
|
||||
friends.forEach(friend => {
|
||||
const sexValue = this.sex(friend.baseInfo.sex!);
|
||||
data.push({
|
||||
...friend.baseInfo,
|
||||
...friend.coreInfo,
|
||||
user_id: parseInt(friend.coreInfo.uin),
|
||||
nickname: friend.coreInfo.nick,
|
||||
remark: friend.coreInfo.nick,
|
||||
sex: sexValue,
|
||||
level: 0,
|
||||
categroyName: friend.categroyName,
|
||||
categoryId: friend.categoryId
|
||||
});
|
||||
});
|
||||
return data;
|
||||
}
|
||||
static friends(friends: Friend[]): OB11User[] {
|
||||
const data: OB11User[] = [];
|
||||
friends.forEach(friend => {
|
||||
|
@@ -22,7 +22,7 @@ import { ob11ReverseWebsockets } from '@/onebot11/server/ws/ReverseWebsocket';
|
||||
import { getGroup, getGroupMember, groupMembers, selfInfo, tempGroupCodeMap } from '@/core/data';
|
||||
import { BuddyListener, GroupListener, NodeIKernelBuddyListener } from '@/core/listeners';
|
||||
import { OB11FriendRequestEvent } from '@/onebot11/event/request/OB11FriendRequest';
|
||||
import { NTQQGroupApi, NTQQUserApi } from '@/core/apis';
|
||||
import { NTQQGroupApi, NTQQUserApi, WebApi } from '@/core/apis';
|
||||
import { log, logDebug, logError, setLogSelfInfo } from '@/common/utils/log';
|
||||
import { OB11GroupRequestEvent } from '@/onebot11/event/request/OB11GroupRequest';
|
||||
import { OB11GroupAdminNoticeEvent } from '@/onebot11/event/notice/OB11GroupAdminNoticeEvent';
|
||||
@@ -98,6 +98,8 @@ export class NapCatOnebot11 {
|
||||
// .map(x => x.toString(16).padStart(2, '0'))
|
||||
// .join('');
|
||||
// }
|
||||
// const hex = buf2hex(Buffer.from(protobufData));
|
||||
// console.log(hex);
|
||||
// // let Data: Data = {
|
||||
// // header: {
|
||||
// // GroupNumber: 0,
|
||||
@@ -192,6 +194,7 @@ export class NapCatOnebot11 {
|
||||
// // 732 17 GroupRecall
|
||||
// // 732 20 GroupCommonTips
|
||||
// // 732 21 EssenceMessage
|
||||
// // 732 16 16 GrayTips ->Busi->GroupEmjioLike
|
||||
// } catch (e) {
|
||||
// log('解析SysMsg异常', e);
|
||||
// // console.log(e);
|
||||
@@ -209,6 +212,7 @@ export class NapCatOnebot11 {
|
||||
// 临时会话更新 tempGroupCodeMap uid -> source/GroupCode
|
||||
};
|
||||
msgListener.onRecvMsg = async (msg) => {
|
||||
|
||||
//console.log('ob11 onRecvMsg', JSON.stringify(msg, null, 2));
|
||||
// logDebug('收到消息', msg);
|
||||
for (const m of msg) {
|
||||
@@ -290,7 +294,7 @@ export class NapCatOnebot11 {
|
||||
}
|
||||
};
|
||||
groupListener.onMemberInfoChange = async (groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
|
||||
//console.log("ob11 onMemberInfoChange", groupCode, changeType, members)
|
||||
// console.log("ob11 onMemberInfoChange", groupCode, changeType, members)
|
||||
if (changeType === 1) {
|
||||
let member;
|
||||
for (const [key, value] of members) {
|
||||
|
@@ -10,8 +10,8 @@ import { postOB11Event } from '@/onebot11/server/postOB11Event';
|
||||
class OB11HTTPServer extends HttpServerBase {
|
||||
name = 'OneBot V11 server';
|
||||
|
||||
handleFailed(res: Response, payload: any, e: any) {
|
||||
res.send(OB11Response.error(e.stack.toString(), 200));
|
||||
handleFailed(res: Response, payload: any, e: Error) {
|
||||
res.send(OB11Response.error(e?.stack?.toString() || e.message || "Error Handle", 200));
|
||||
}
|
||||
|
||||
protected listen(port: number, host: string) {
|
||||
|
@@ -7,6 +7,8 @@ export interface OB11User {
|
||||
age?: number;
|
||||
qid?: string;
|
||||
login_days?: number;
|
||||
categroyName?:string;
|
||||
categoryId?:number;
|
||||
}
|
||||
|
||||
export enum OB11UserSex {
|
||||
|
@@ -1 +1 @@
|
||||
export const version = '1.7.6';
|
||||
export const version = '1.8.0';
|
||||
|
@@ -29,7 +29,7 @@ async function onSettingWindowCreated(view: Element) {
|
||||
SettingItem(
|
||||
'<span id="napcat-update-title">Napcat</span>',
|
||||
undefined,
|
||||
SettingButton('V1.7.6', 'napcat-update-button', 'secondary')
|
||||
SettingButton('V1.8.0', 'napcat-update-button', 'secondary')
|
||||
),
|
||||
]),
|
||||
SettingList([
|
||||
|
@@ -163,7 +163,7 @@ async function onSettingWindowCreated(view) {
|
||||
SettingItem(
|
||||
'<span id="napcat-update-title">Napcat</span>',
|
||||
void 0,
|
||||
SettingButton("V1.7.6", "napcat-update-button", "secondary")
|
||||
SettingButton("V1.8.0", "napcat-update-button", "secondary")
|
||||
)
|
||||
]),
|
||||
SettingList([
|
||||
|
Reference in New Issue
Block a user