mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9a64b8bdb6 | ||
![]() |
c01e493bd2 | ||
![]() |
890236af23 | ||
![]() |
ea678d805d | ||
![]() |
29699418ff | ||
![]() |
87bb36c39f | ||
![]() |
7cb85ed73c | ||
![]() |
c647771a6d | ||
![]() |
6c3d737219 | ||
![]() |
a1f38fed7a | ||
![]() |
c36bb77286 | ||
![]() |
1a1acdc3c9 | ||
![]() |
ef8e60c405 | ||
![]() |
31c1cc47bf | ||
![]() |
351fed7359 | ||
![]() |
f49e7cbe57 | ||
![]() |
7da8ea5e99 | ||
![]() |
8fa6a12a7c | ||
![]() |
1070278eaf | ||
![]() |
16b1a6b153 | ||
![]() |
096a7534e0 | ||
![]() |
b0897187d2 | ||
![]() |
885d94882d | ||
![]() |
11a3341e13 | ||
![]() |
231890f78a | ||
![]() |
a272feda6a | ||
![]() |
bc936a0ca7 | ||
![]() |
28030c2d13 | ||
![]() |
7fe9176286 | ||
![]() |
1105f9b8d6 | ||
![]() |
e4653defa8 | ||
![]() |
1c62a1e839 | ||
![]() |
3a3bbfe201 |
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
|
1.1. "Contributor"
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
means each individual or legal entity that creates, contributes to
|
||||||
in the Software without restriction, including without limitation the rights
|
the creation of, or owns Covered Software.
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
1.2. "Contributor Version"
|
||||||
copies or substantial portions of the Software.
|
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
|
1.3. "Contribution"
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
means Covered Software of a particular Contributor.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
1.4. "Covered Software"
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
means Source Code Form to which the initial Contributor has attached
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
SOFTWARE.
|
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,4 +1,4 @@
|
|||||||
# v1.7.7
|
# v1.8.2
|
||||||
|
|
||||||
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
|
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
|
||||||
|
|
||||||
@@ -6,6 +6,8 @@ QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
|
|||||||
Way03/Way05
|
Way03/Way05
|
||||||
|
|
||||||
## 新增与调整
|
## 新增与调整
|
||||||
1. 支持 9.9.15/3.2.12 版本QQ
|
1. 多层转发消息接收/发送
|
||||||
|
2. 消息列表排序修正
|
||||||
|
|
||||||
|
|
||||||
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.7.7",
|
"version": "1.8.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch:dev": "vite --mode development",
|
"watch:dev": "vite --mode development",
|
||||||
"watch:prod": "vite --mode production",
|
"watch:prod": "vite --mode production",
|
||||||
|
@@ -99,8 +99,10 @@ class MessageUniqueWrapper {
|
|||||||
}
|
}
|
||||||
createMsg(peer: Peer, msgId: string): number | undefined {
|
createMsg(peer: Peer, msgId: string): number | undefined {
|
||||||
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||||
const hash = crypto.createHash('md5').update(key);
|
const hash = crypto.createHash('md5').update(key).digest();
|
||||||
const shortId = hash.digest().readInt32BE(0);
|
//设置第一个bit为0 保证shortId为正数
|
||||||
|
hash[0] &= 0x7f;
|
||||||
|
const shortId = hash.readInt32BE(0);
|
||||||
//减少性能损耗
|
//减少性能损耗
|
||||||
// const isExist = this.msgIdMap.getKey(shortId);
|
// const isExist = this.msgIdMap.getKey(shortId);
|
||||||
// if (isExist && isExist === msgId) {
|
// if (isExist && isExist === msgId) {
|
||||||
|
@@ -6,15 +6,15 @@ import AppidTable from '@/core/external/appid.json';
|
|||||||
import { log } from './log';
|
import { log } from './log';
|
||||||
|
|
||||||
//基础目录获取
|
//基础目录获取
|
||||||
export let QQMainPath = process.execPath;
|
export const QQMainPath = process.execPath;
|
||||||
export let QQPackageInfoPath: string = path.join(path.dirname(QQMainPath), 'resources', 'app', 'package.json');
|
export const QQPackageInfoPath: string = path.join(path.dirname(QQMainPath), 'resources', 'app', 'package.json');
|
||||||
export let QQVersionConfigPath: string | undefined = getQQVersionConfigPath(QQMainPath);
|
export const QQVersionConfigPath: string | undefined = getQQVersionConfigPath(QQMainPath);
|
||||||
|
|
||||||
//基础信息获取 无快更则启用默认模板填充
|
//基础信息获取 无快更则启用默认模板填充
|
||||||
export let isQuickUpdate: boolean = !!QQVersionConfigPath;
|
export const isQuickUpdate: boolean = !!QQVersionConfigPath;
|
||||||
export let QQVersionConfig: QQVersionConfigType = isQuickUpdate ? JSON.parse(fs.readFileSync(QQVersionConfigPath!).toString()) : getDefaultQQVersionConfigInfo();
|
export const QQVersionConfig: QQVersionConfigType = isQuickUpdate ? JSON.parse(fs.readFileSync(QQVersionConfigPath!).toString()) : getDefaultQQVersionConfigInfo();
|
||||||
export let QQPackageInfo: QQPackageInfoType = JSON.parse(fs.readFileSync(QQPackageInfoPath).toString());
|
export const QQPackageInfo: QQPackageInfoType = JSON.parse(fs.readFileSync(QQPackageInfoPath).toString());
|
||||||
export let { appid: QQVersionAppid, qua: QQVersionQua } = getAppidV2();
|
export const { appid: QQVersionAppid, qua: QQVersionQua } = getAppidV2();
|
||||||
|
|
||||||
//基础函数
|
//基础函数
|
||||||
export function getQQBuildStr() {
|
export function getQQBuildStr() {
|
||||||
@@ -31,17 +31,18 @@ export function getQUAInternal() {
|
|||||||
return systemPlatform === 'linux' ? `V1_LNX_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B` : `V1_WIN_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B`;
|
return systemPlatform === 'linux' ? `V1_LNX_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B` : `V1_WIN_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B`;
|
||||||
}
|
}
|
||||||
export function getAppidV2(): { appid: string, qua: string } {
|
export function getAppidV2(): { appid: string, qua: string } {
|
||||||
let appidTbale = AppidTable as unknown as QQAppidTableType;
|
const appidTbale = AppidTable as unknown as QQAppidTableType;
|
||||||
try {
|
try {
|
||||||
let data = appidTbale[getFullQQVesion()];
|
const data = appidTbale[getFullQQVesion()];
|
||||||
if (data) {
|
if (data) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
log('[QQ版本兼容性检测] 版本兼容性不佳,可能会导致一些功能无法正常使用', e);
|
log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`);
|
||||||
}
|
}
|
||||||
// 以下是兜底措施
|
// 以下是兜底措施
|
||||||
|
log(`[QQ版本兼容性检测] ${getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`);
|
||||||
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: getQUAInternal() };
|
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: getQUAInternal() };
|
||||||
}
|
}
|
||||||
// platform_type: 3,
|
// platform_type: 3,
|
||||||
|
@@ -181,7 +181,7 @@ type Uri2LocalRes = {
|
|||||||
isLocal: boolean
|
isLocal: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uri2local(uri: string, fileName: string | null = null): Promise<Uri2LocalRes> {
|
export async function uri2local(UriOrPath: string, fileName: string | null = null): Promise<Uri2LocalRes> {
|
||||||
const res = {
|
const res = {
|
||||||
success: false,
|
success: false,
|
||||||
errMsg: '',
|
errMsg: '',
|
||||||
@@ -190,26 +190,29 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
path: '',
|
path: '',
|
||||||
isLocal: false
|
isLocal: false
|
||||||
};
|
};
|
||||||
if (!fileName) {
|
if (!fileName) fileName = randomUUID();
|
||||||
fileName = randomUUID();
|
let filePath = path.join(getTempDir(), fileName);//临时目录
|
||||||
}
|
|
||||||
let filePath = path.join(getTempDir(), fileName);
|
|
||||||
let url = null;
|
let url = null;
|
||||||
|
//区分path和uri
|
||||||
try {
|
try {
|
||||||
url = new URL(uri);
|
if (fs.existsSync(UriOrPath)) url = new URL('file://' + UriOrPath);
|
||||||
} catch (e: any) {
|
} catch (error: any) { }
|
||||||
res.errMsg = `uri ${uri} 解析失败,` + e.toString() + ` 可能${uri}不存在`;
|
try {
|
||||||
|
url = new URL(UriOrPath);
|
||||||
|
} catch (error: any) { }
|
||||||
|
|
||||||
|
//验证url
|
||||||
|
if (!url) {
|
||||||
|
res.errMsg = `UriOrPath ${UriOrPath} 解析失败,可能${UriOrPath}不存在`;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// log("uri protocol", url.protocol, uri);
|
|
||||||
if (url.protocol == 'base64:') {
|
if (url.protocol == 'base64:') {
|
||||||
// base64转成文件
|
// base64转成文件
|
||||||
const base64Data = uri.split('base64://')[1];
|
const base64Data = UriOrPath.split('base64://')[1];
|
||||||
try {
|
try {
|
||||||
const buffer = Buffer.from(base64Data, 'base64');
|
const buffer = Buffer.from(base64Data, 'base64');
|
||||||
fs.writeFileSync(filePath, buffer);
|
fs.writeFileSync(filePath, buffer);
|
||||||
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
res.errMsg = 'base64文件下载失败,' + e.toString();
|
res.errMsg = 'base64文件下载失败,' + e.toString();
|
||||||
return res;
|
return res;
|
||||||
@@ -218,7 +221,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
// 下载文件
|
// 下载文件
|
||||||
let buffer: Buffer | null = null;
|
let buffer: Buffer | null = null;
|
||||||
try {
|
try {
|
||||||
buffer = await httpDownload(uri);
|
buffer = await httpDownload(UriOrPath);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
res.errMsg = `${url}下载失败,` + e.toString();
|
res.errMsg = `${url}下载失败,` + e.toString();
|
||||||
return res;
|
return res;
|
||||||
@@ -252,6 +255,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// 26702执行forword file文件操作 不应该在这里乱来
|
||||||
// const cache = await dbUtil.getFileCacheByName(uri);
|
// const cache = await dbUtil.getFileCacheByName(uri);
|
||||||
// if (cache) {
|
// if (cache) {
|
||||||
// filePath = cache.path;
|
// filePath = cache.path;
|
||||||
@@ -259,7 +263,6 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
// filePath = uri;
|
// filePath = uri;
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
res.isLocal = true;
|
res.isLocal = true;
|
||||||
}
|
}
|
||||||
// else{
|
// else{
|
||||||
|
@@ -360,12 +360,12 @@ export async function promisePipeline(promises: Promise<any>[], callback: (resul
|
|||||||
callbackCalled = callback(result);
|
callbackCalled = callback(result);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in promise pipeline:", error);
|
console.error('Error in promise pipeline:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getQQVersionConfigPath(exePath: string = ""): string | undefined {
|
export function getQQVersionConfigPath(exePath: string = ''): string | undefined {
|
||||||
let configVersionInfoPath;
|
let configVersionInfoPath;
|
||||||
if (os.platform() !== 'linux') {
|
if (os.platform() !== 'linux') {
|
||||||
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');
|
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');
|
||||||
|
@@ -17,7 +17,7 @@ async function LoadMessageIdList(Peer: Peer, msgId: string) {
|
|||||||
}
|
}
|
||||||
async function loadMessageUnique() {
|
async function loadMessageUnique() {
|
||||||
if (groups.size > 100) {
|
if (groups.size > 100) {
|
||||||
logWarn('群数量大于100,可能会导致性能问题');
|
logWarn('[性能检测] 群数量大于100,可能会导致性能问题');
|
||||||
}
|
}
|
||||||
let predict = (groups.size + friends.size / 2) / 5;
|
let predict = (groups.size + friends.size / 2) / 5;
|
||||||
predict = predict < 20 ? 20 : predict;
|
predict = predict < 20 ? 20 : predict;
|
||||||
@@ -67,6 +67,13 @@ setTimeout(() => {
|
|||||||
// }, 25000)
|
// }, 25000)
|
||||||
|
|
||||||
export class NTQQMsgApi {
|
export class NTQQMsgApi {
|
||||||
|
static async FetchLongMsg(peer: Peer, msgId: string) {
|
||||||
|
return napCatCore.session.getMsgService().fetchLongMsg(peer, msgId);
|
||||||
|
}
|
||||||
|
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;
|
// static napCatCore: NapCatCore | null = null;
|
||||||
// enum BaseEmojiType {
|
// enum BaseEmojiType {
|
||||||
// NORMAL_EMOJI,
|
// NORMAL_EMOJI,
|
||||||
@@ -87,20 +94,25 @@ export class NTQQMsgApi {
|
|||||||
} | undefined> {
|
} | undefined> {
|
||||||
return napCatCore.session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId);
|
return napCatCore.session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId);
|
||||||
}
|
}
|
||||||
static async getLastestMsgByUids(peer: Peer, count: number = 20) {
|
static async ForwardMsg(peer: Peer, msgIds: string[]) {
|
||||||
|
return napCatCore.session.getMsgService().forwardMsg(msgIds, peer, [peer], new Map());
|
||||||
|
}
|
||||||
|
static async getLastestMsgByUids(peer: Peer, count: number = 20, isReverseOrder: boolean = false) {
|
||||||
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
chatInfo: peer,
|
chatInfo: peer,
|
||||||
filterMsgType: [],
|
filterMsgType: [],
|
||||||
filterSendersUid: [],
|
filterSendersUid: [],
|
||||||
filterMsgToTime: '0',
|
filterMsgToTime: '0',
|
||||||
filterMsgFromTime: '0',
|
filterMsgFromTime: '0',
|
||||||
isReverseOrder: false,
|
isReverseOrder: isReverseOrder,
|
||||||
isIncludeCurrent: true,
|
isIncludeCurrent: true,
|
||||||
pageLimit: 1,
|
pageLimit: count,
|
||||||
});
|
});
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
static async getMsgsByMsgId(peer: Peer, msgIds: string[]) {
|
static async getMsgsByMsgId(peer: Peer | undefined, msgIds: string[] | undefined) {
|
||||||
|
if (!peer) throw new Error('peer is not allowed');
|
||||||
|
if (!msgIds) throw new Error('msgIds is not allowed');
|
||||||
return await napCatCore.session.getMsgService().getMsgsByMsgId(peer, msgIds);
|
return await napCatCore.session.getMsgService().getMsgsByMsgId(peer, msgIds);
|
||||||
}
|
}
|
||||||
static async getSingleMsg(peer: Peer, seq: string) {
|
static async getSingleMsg(peer: Peer, seq: string) {
|
||||||
@@ -145,9 +157,9 @@ export class NTQQMsgApi {
|
|||||||
);
|
);
|
||||||
return data[1].item;
|
return data[1].item;
|
||||||
}
|
}
|
||||||
static async getMsgHistory(peer: Peer, msgId: string, count: number) {
|
static async getMsgHistory(peer: Peer, msgId: string, count: number, isReverseOrder: boolean = false) {
|
||||||
// 消息时间从旧到新
|
// 消息时间从旧到新
|
||||||
return napCatCore.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, true);
|
return napCatCore.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, isReverseOrder);
|
||||||
}
|
}
|
||||||
static async recallMsg(peer: Peer, msgIds: string[]) {
|
static async recallMsg(peer: Peer, msgIds: string[]) {
|
||||||
await napCatCore.session.getMsgService().recallMsg({
|
await napCatCore.session.getMsgService().recallMsg({
|
||||||
@@ -165,7 +177,9 @@ export class NTQQMsgApi {
|
|||||||
// const msgId = BigInt("0x" + buffer.toString('hex')).toString();
|
// const msgId = BigInt("0x" + buffer.toString('hex')).toString();
|
||||||
// return msgId;
|
// return msgId;
|
||||||
// }
|
// }
|
||||||
|
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||||
|
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||||
|
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||||
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
|
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
|
||||||
let data = await NTEventDispatch.CallNormalEvent<
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||||
|
@@ -19,6 +19,43 @@ export interface CustomMusicSignPostData {
|
|||||||
image?: string,
|
image?: string,
|
||||||
singer?: 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 {
|
// export class MusicSign {
|
||||||
// private readonly url: string;
|
// private readonly url: string;
|
||||||
|
@@ -322,6 +322,7 @@ export class NapCatCore {
|
|||||||
if (groupMembers.has(groupCode)) {
|
if (groupMembers.has(groupCode)) {
|
||||||
const existMembers = groupMembers.get(groupCode)!;
|
const existMembers = groupMembers.get(groupCode)!;
|
||||||
arg.infos.forEach((member, uid) => {
|
arg.infos.forEach((member, uid) => {
|
||||||
|
//console.log('onMemberListChange', member);
|
||||||
const existMember = existMembers.get(uid);
|
const existMember = existMembers.get(uid);
|
||||||
if (existMember) {
|
if (existMember) {
|
||||||
Object.assign(existMember, member);
|
Object.assign(existMember, member);
|
||||||
|
@@ -7,7 +7,7 @@ export enum GroupListUpdateType {
|
|||||||
}
|
}
|
||||||
export interface Group {
|
export interface Group {
|
||||||
groupCode: string,
|
groupCode: string,
|
||||||
createTime?:string,//高版本才有
|
createTime?: string,//高版本才有
|
||||||
maxMember: number,
|
maxMember: number,
|
||||||
memberCount: number,
|
memberCount: number,
|
||||||
groupName: string,
|
groupName: string,
|
||||||
@@ -59,4 +59,6 @@ export interface GroupMember {
|
|||||||
sex?: Sex
|
sex?: Sex
|
||||||
qqLevel?: QQLevel
|
qqLevel?: QQLevel
|
||||||
isChangeRole: boolean;
|
isChangeRole: boolean;
|
||||||
|
joinTime: string;
|
||||||
|
lastSpeakTime: string;
|
||||||
}
|
}
|
@@ -731,10 +731,12 @@ export interface MultiForwardMsgElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RawMessage {
|
export interface RawMessage {
|
||||||
|
parentMsgPeer: Peer;
|
||||||
|
parentMsgIdList:string[];
|
||||||
|
id?: number;//扩展字段 用于处理OB11 ID
|
||||||
guildId: string;
|
guildId: string;
|
||||||
msgRandom: string;
|
msgRandom: string;
|
||||||
// int32, 自己维护的消息id
|
|
||||||
id?: number;
|
|
||||||
|
|
||||||
msgId: string;
|
msgId: string;
|
||||||
|
|
||||||
|
8
src/core/src/external/appid.json
vendored
8
src/core/src/external/appid.json
vendored
@@ -11,6 +11,10 @@
|
|||||||
"appid": 537237950,
|
"appid": 537237950,
|
||||||
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
|
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
|
||||||
},
|
},
|
||||||
|
"3.2.12-26740": {
|
||||||
|
"appid": 537237950,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_26740_GW_B"
|
||||||
|
},
|
||||||
"9.9.11-24815": {
|
"9.9.11-24815": {
|
||||||
"appid": 537226656,
|
"appid": 537226656,
|
||||||
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
|
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
|
||||||
@@ -38,5 +42,9 @@
|
|||||||
"9.9.15-26702": {
|
"9.9.15-26702": {
|
||||||
"appid": 537237765,
|
"appid": 537237765,
|
||||||
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.15-26740": {
|
||||||
|
"appid": 537237765,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -477,7 +477,19 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
setMsgEmojiLikes(...args: unknown[]): unknown;
|
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;
|
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -592,7 +604,7 @@ export interface NodeIKernelMsgService {
|
|||||||
// this.selfPhone = str5;
|
// this.selfPhone = str5;
|
||||||
// this.gameSession = tempChatGameSession;
|
// this.gameSession = tempChatGameSession;
|
||||||
prepareTempChat(args: unknown): unknown;//主动临时消息 不做
|
prepareTempChat(args: unknown): unknown;//主动临时消息 不做
|
||||||
|
|
||||||
sendSsoCmdReqByContend(cmd: string, param: string): Promise<unknown>;
|
sendSsoCmdReqByContend(cmd: string, param: string): Promise<unknown>;
|
||||||
|
|
||||||
//chattype,uid->Promise<any>
|
//chattype,uid->Promise<any>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Peer } from "../entities";
|
import { ChatType, Peer } from "../entities";
|
||||||
import { NodeIKernelRecentContactListener } from "../listeners/NodeIKernelRecentContactListener";
|
import { NodeIKernelRecentContactListener } from "../listeners/NodeIKernelRecentContactListener";
|
||||||
import { GeneralCallResult } from "./common";
|
import { GeneralCallResult } from "./common";
|
||||||
export interface FSABRecentContactParams {
|
export interface FSABRecentContactParams {
|
||||||
@@ -39,7 +39,13 @@ export interface NodeIKernelRecentContactService {
|
|||||||
errCode: number,
|
errCode: number,
|
||||||
errMsg: string,
|
errMsg: string,
|
||||||
sortedContactList: Array<number>,
|
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
|
}>; // 1 arguments
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if(!msgIdPeer) throw new Error('消息不存在');
|
||||||
|
const msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
|
||||||
|
const ret = await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer,msg.msgSeq,payload.emojiId,payload.emojiType,payload.count);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@@ -20,8 +20,8 @@ export class OCRImage extends BaseAction<Payload, any> {
|
|||||||
actionName = ActionName.OCRImage;
|
actionName = ActionName.OCRImage;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
const { path, isLocal, errMsg } = (await uri2local(payload.image));
|
const { path, isLocal, errMsg,success } = (await uri2local(payload.image));
|
||||||
if (errMsg) {
|
if (!success) {
|
||||||
throw `OCR ${payload.image}失败,image字段可能格式不正确`;
|
throw `OCR ${payload.image}失败,image字段可能格式不正确`;
|
||||||
}
|
}
|
||||||
if (path) {
|
if (path) {
|
||||||
|
@@ -26,8 +26,8 @@ export default class SetGroupHeader extends BaseAction<Payload, any> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
protected async _handle(payload: Payload): Promise<any> {
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
const { path, isLocal, errMsg } = (await uri2local(payload.file));
|
const { path, isLocal, errMsg,success } = (await uri2local(payload.file));
|
||||||
if (errMsg) {
|
if (!success) {
|
||||||
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
||||||
}
|
}
|
||||||
if (path) {
|
if (path) {
|
||||||
|
@@ -24,8 +24,8 @@ export default class SetAvatar extends BaseAction<Payload, null> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
protected async _handle(payload: Payload): Promise<null> {
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
const { path, isLocal, errMsg } = (await uri2local(payload.file));
|
const { path, isLocal, errMsg,success } = (await uri2local(payload.file));
|
||||||
if (errMsg) {
|
if (!success) {
|
||||||
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
||||||
}
|
}
|
||||||
if (path) {
|
if (path) {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { OB11Message, OB11User } from '../../types';
|
import { OB11Message, OB11User } from '../../types';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { ChatType } from '@/core/entities';
|
import { ChatType, RawMessage } from '@/core/entities';
|
||||||
import { NTQQMsgApi } from '@/core/apis/msg';
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
@@ -17,9 +17,10 @@ const SchemaData = {
|
|||||||
properties: {
|
properties: {
|
||||||
user_id: { type: ['number', 'string'] },
|
user_id: { type: ['number', 'string'] },
|
||||||
message_seq: { type: 'number' },
|
message_seq: { type: 'number' },
|
||||||
count: { type: 'number' }
|
count: { type: 'number' },
|
||||||
|
reverseOrder: { type: 'boolean' }
|
||||||
},
|
},
|
||||||
required: ['user_id', 'message_seq', 'count']
|
required: ['user_id']
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
type Payload = FromSchema<typeof SchemaData>;
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
@@ -28,21 +29,28 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
|||||||
actionName = ActionName.GetFriendMsgHistory;
|
actionName = ActionName.GetFriendMsgHistory;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload): Promise<Response> {
|
protected async _handle(payload: Payload): Promise<Response> {
|
||||||
|
//处理参数
|
||||||
const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
if (!uid) {
|
const MsgCount = payload.count || 20;
|
||||||
throw `记录${payload.user_id}不存在`;
|
const isReverseOrder = payload.reverseOrder || true;
|
||||||
}
|
if (!uid) throw `记录${payload.user_id}不存在`;
|
||||||
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq))?.MsgId || '0';
|
|
||||||
const friend = await NTQQFriendApi.isBuddy(uid);
|
const friend = await NTQQFriendApi.isBuddy(uid);
|
||||||
const historyResult = (await NTQQMsgApi.getMsgHistory({
|
const peer = { chatType: friend ? ChatType.friend : ChatType.temp, peerUid: uid };
|
||||||
chatType: friend ? ChatType.friend : ChatType.temp,
|
|
||||||
peerUid: uid
|
//拉取消息
|
||||||
}, startMsgId, parseInt(payload.count?.toString()) || 20));
|
let msgList: RawMessage[];
|
||||||
//logDebug(historyResult);
|
if (!payload.message_seq || payload.message_seq == 0) {
|
||||||
const msgList = historyResult.msgList;
|
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, MsgCount)).msgList;
|
||||||
|
} else {
|
||||||
|
const startMsgId = MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq)?.MsgId;
|
||||||
|
if (!startMsgId) throw `消息${payload.message_seq}不存在`;
|
||||||
|
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList;
|
||||||
|
}
|
||||||
|
if(isReverseOrder) msgList.reverse();
|
||||||
await Promise.all(msgList.map(async msg => {
|
await Promise.all(msgList.map(async msg => {
|
||||||
msg.id = await MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
}));
|
}));
|
||||||
|
//转换消息
|
||||||
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
||||||
return { 'messages': ob11MsgList };
|
return { 'messages': ob11MsgList };
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import BaseAction from '../BaseAction';
|
|||||||
import { OB11Message, OB11User } from '../../types';
|
import { OB11Message, OB11User } from '../../types';
|
||||||
import { getGroup, groups } from '@/core/data';
|
import { getGroup, groups } from '@/core/data';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { ChatType, RawMessage } from '@/core/entities';
|
import { ChatType, Peer, RawMessage } from '@/core/entities';
|
||||||
import { NTQQMsgApi } from '@/core/apis/msg';
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
@@ -16,7 +16,8 @@ const SchemaData = {
|
|||||||
properties: {
|
properties: {
|
||||||
group_id: { type: ['number', 'string'] },
|
group_id: { type: ['number', 'string'] },
|
||||||
message_seq: { type: 'number' },
|
message_seq: { type: 'number' },
|
||||||
count: { type: 'number' }
|
count: { type: 'number' },
|
||||||
|
reverseOrder: { type: 'boolean' }
|
||||||
},
|
},
|
||||||
required: ['group_id']
|
required: ['group_id']
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@@ -27,25 +28,28 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
|
|||||||
actionName = ActionName.GoCQHTTP_GetGroupMsgHistory;
|
actionName = ActionName.GoCQHTTP_GetGroupMsgHistory;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload): Promise<Response> {
|
protected async _handle(payload: Payload): Promise<Response> {
|
||||||
|
//处理参数
|
||||||
const group = await getGroup(payload.group_id.toString());
|
const group = await getGroup(payload.group_id.toString());
|
||||||
if (!group) {
|
const isReverseOrder = payload.reverseOrder || true;
|
||||||
throw `群${payload.group_id}不存在`;
|
const MsgCount = payload.count || 20;
|
||||||
}
|
const peer: Peer = { chatType: ChatType.group, peerUid: payload.group_id.toString() };
|
||||||
let targetMsgShortId, count = parseInt(payload.count?.toString() ?? '20');
|
if (!group) throw `群${payload.group_id}不存在`;
|
||||||
const peer = {
|
|
||||||
chatType: ChatType.group,
|
//拉取消息
|
||||||
peerUid: group.groupCode
|
|
||||||
};
|
|
||||||
let msgList: RawMessage[];
|
let msgList: RawMessage[];
|
||||||
if (!payload.message_seq) {
|
if (!payload.message_seq || payload.message_seq == 0) {
|
||||||
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, count)).msgList;
|
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, MsgCount)).msgList;
|
||||||
} else {
|
} else {
|
||||||
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(targetMsgShortId ?? (payload.message_seq ?? 0)))?.MsgId || '0';
|
const startMsgId = MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq)?.MsgId;
|
||||||
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, count)).msgList;
|
if (!startMsgId) throw `消息${payload.message_seq}不存在`;
|
||||||
|
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList;
|
||||||
}
|
}
|
||||||
|
if(isReverseOrder) msgList.reverse();
|
||||||
await Promise.all(msgList.map(async msg => {
|
await Promise.all(msgList.map(async msg => {
|
||||||
msg.id = await MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
//转换消息
|
||||||
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
||||||
return { 'messages': ob11MsgList };
|
return { 'messages': ob11MsgList };
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
|||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
group_id: { type: [ 'number' , 'string' ] },
|
group_id: { type: ['number', 'string'] },
|
||||||
content: { type: 'string' },
|
content: { type: 'string' },
|
||||||
image: { type: 'string' },
|
image: { type: 'string' },
|
||||||
pinned: { type: 'number' },
|
pinned: { type: 'number' },
|
||||||
@@ -24,8 +24,8 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
|
|||||||
let UploadImage: { id: string, width: number, height: number } | undefined = undefined;
|
let UploadImage: { id: string, width: number, height: number } | undefined = undefined;
|
||||||
if (payload.image) {
|
if (payload.image) {
|
||||||
//公告图逻辑
|
//公告图逻辑
|
||||||
const { errMsg, path, isLocal } = (await uri2local(payload.image));
|
const { errMsg, path, isLocal, success } = (await uri2local(payload.image));
|
||||||
if (errMsg) {
|
if (!success) {
|
||||||
throw `群公告${payload.image}设置失败,image字段可能格式不正确`;
|
throw `群公告${payload.image}设置失败,image字段可能格式不正确`;
|
||||||
}
|
}
|
||||||
if (!path) {
|
if (!path) {
|
||||||
|
@@ -34,7 +34,7 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
|
|||||||
file = `file://${file}`;
|
file = `file://${file}`;
|
||||||
}
|
}
|
||||||
const downloadResult = await uri2local(file);
|
const downloadResult = await uri2local(file);
|
||||||
if (downloadResult.errMsg) {
|
if (!downloadResult.success) {
|
||||||
throw new Error(downloadResult.errMsg);
|
throw new Error(downloadResult.errMsg);
|
||||||
}
|
}
|
||||||
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name, payload.folder_id);
|
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name, payload.folder_id);
|
||||||
|
@@ -41,7 +41,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
|
|||||||
file = `file://${file}`;
|
file = `file://${file}`;
|
||||||
}
|
}
|
||||||
const downloadResult = await uri2local(file);
|
const downloadResult = await uri2local(file);
|
||||||
if (downloadResult.errMsg) {
|
if (!downloadResult.success) {
|
||||||
throw new Error(downloadResult.errMsg);
|
throw new Error(downloadResult.errMsg);
|
||||||
}
|
}
|
||||||
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name);
|
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name);
|
||||||
|
@@ -19,7 +19,7 @@ class GetGroupList extends BaseAction<Payload, OB11Group[]> {
|
|||||||
actionName = ActionName.GetGroupList;
|
actionName = ActionName.GetGroupList;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
let groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
|
const groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
|
||||||
return OB11Constructor.groups(groupList);
|
return OB11Constructor.groups(groupList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,8 @@ import { logDebug } from '@/common/utils/log';
|
|||||||
import { WebApi } from '@/core/apis/webapi';
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
import { NTQQGroupApi } from '@/core';
|
import { NTQQGroupApi } from '@/core';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { selfInfo } from '@/core/data';
|
import { getGroupMember, selfInfo } from '@/core/data';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@@ -25,11 +26,11 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
|||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||||
let uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
throw (`Uin2Uid Error ${payload.user_id}不存在`);
|
throw (`Uin2Uid Error ${payload.user_id}不存在`);
|
||||||
}
|
}
|
||||||
let member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
|
const member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
|
||||||
if (!member) {
|
if (!member) {
|
||||||
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
}
|
}
|
||||||
@@ -40,31 +41,37 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||||
}
|
}
|
||||||
|
const date = Math.round(Date.now() / 1000);
|
||||||
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||||
let SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), selfInfo.uid, isNocache);
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
let isPrivilege = false;
|
const SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), selfInfo.uid, isNocache);
|
||||||
if (SelfInfoInGroup) {
|
let isPrivilege = false;
|
||||||
isPrivilege = SelfInfoInGroup.role === 3 || SelfInfoInGroup.role === 4;
|
if (SelfInfoInGroup) {
|
||||||
}
|
isPrivilege = SelfInfoInGroup.role === 3 || SelfInfoInGroup.role === 4;
|
||||||
if (isPrivilege) {
|
}
|
||||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
if (isPrivilege) {
|
||||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
retMember.join_time = webGroupMembers[i]?.join_time;
|
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||||
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||||
retMember.qage = webGroupMembers[i]?.qage;
|
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||||
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
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 {
|
} else {
|
||||||
const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
retMember.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString());
|
||||||
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
retMember.join_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString());
|
||||||
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;
|
return retMember;
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
|||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
if (requireMinNTQQBuild('26702')) {
|
if (requireMinNTQQBuild('26702')) {
|
||||||
let V2Data = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), payload.user_id.toString(), payload.no_cache == true || payload.no_cache === 'true');
|
const V2Data = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), payload.user_id.toString(), payload.no_cache == true || payload.no_cache === 'true');
|
||||||
if (V2Data) {
|
if (V2Data) {
|
||||||
return OB11Constructor.groupMember(payload.group_id.toString(), V2Data);
|
return OB11Constructor.groupMember(payload.group_id.toString(), V2Data);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { selfInfo } from '@/core/data';
|
import { getGroup, getGroupMember, selfInfo } from '@/core/data';
|
||||||
import { OB11GroupMember } from '../../types';
|
import { OB11GroupMember } from '../../types';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
@@ -6,6 +6,7 @@ import { ActionName } from '../types';
|
|||||||
import { NTQQGroupApi } from '@/core';
|
import { NTQQGroupApi } from '@/core';
|
||||||
import { WebApi } from '@/core/apis/webapi';
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -29,7 +30,7 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
if (!group) {
|
if (!group) {
|
||||||
throw (`群${payload.group_id}不存在`);
|
throw (`群${payload.group_id}不存在`);
|
||||||
}
|
}
|
||||||
let groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
|
const groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
|
||||||
let _groupMembers = Array.from(groupMembers.values())
|
let _groupMembers = Array.from(groupMembers.values())
|
||||||
.map(item => { return OB11Constructor.groupMember(group.groupCode, item); });
|
.map(item => { return OB11Constructor.groupMember(group.groupCode, item); });
|
||||||
|
|
||||||
@@ -43,40 +44,47 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selfRole = groupMembers.get(selfInfo.uid)?.role;
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
const isPrivilege = selfRole === 3 || selfRole === 4;
|
const selfRole = groupMembers.get(selfInfo.uid)?.role;
|
||||||
|
const isPrivilege = selfRole === 3 || selfRole === 4;
|
||||||
|
|
||||||
if (isPrivilege) {
|
if (isPrivilege) {
|
||||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
if (!webGroupMembers[i]?.uin) {
|
if (!webGroupMembers[i]?.uin) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
|
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) {
|
if (MemberData) {
|
||||||
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);
|
MemberData.join_time = webGroupMembers[i]?.join_time;
|
||||||
//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 {
|
} else {
|
||||||
_groupMembers.forEach(item => {
|
if (isNocache) {
|
||||||
item.last_sent_time = date;
|
const DateMap = await NTQQGroupApi.getGroupMemberLastestSendTimeCache(payload.group_id.toString());//开始从本地拉取
|
||||||
item.join_time = date;
|
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 {
|
||||||
|
_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 一同返回
|
// 还原索引到Array 一同返回
|
||||||
|
|
||||||
|
@@ -78,8 +78,10 @@ import SetGroupHeader from './extends/SetGroupHeader';
|
|||||||
import { FetchCustomFace } from './extends/FetchCustomFace';
|
import { FetchCustomFace } from './extends/FetchCustomFace';
|
||||||
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile';
|
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile';
|
||||||
import TestApi01 from './extends/TestApi01';
|
import TestApi01 from './extends/TestApi01';
|
||||||
|
import { FetchEmojioLike } from './extends/FetchEmojioLike';
|
||||||
|
|
||||||
export const actionHandlers = [
|
export const actionHandlers = [
|
||||||
|
new FetchEmojioLike(),
|
||||||
new RebootNormal(),
|
new RebootNormal(),
|
||||||
new GetFile(),
|
new GetFile(),
|
||||||
new Debug(),
|
new Debug(),
|
||||||
|
@@ -3,12 +3,15 @@ import { ActionName } from '../types';
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
import { sleep } from '@/common/utils/helper';
|
||||||
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
|
import { NodeIKernelMsgListener } from '@/core';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
message_id: {
|
message_id: {
|
||||||
oneOf:[
|
oneOf: [
|
||||||
{ type: 'number' },
|
{ type: 'number' },
|
||||||
{ type: 'string' }
|
{ type: 'string' }
|
||||||
]
|
]
|
||||||
@@ -23,9 +26,27 @@ class DeleteMsg extends BaseAction<Payload, void> {
|
|||||||
actionName = ActionName.DeleteMsg;
|
actionName = ActionName.DeleteMsg;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
const msg = await MessageUnique.getMsgIdAndPeerByShortId(Number(payload.message_id));
|
const msg = MessageUnique.getMsgIdAndPeerByShortId(Number(payload.message_id));
|
||||||
if (msg) {
|
if (msg) {
|
||||||
|
let ret = NTEventDispatch.RegisterListen<NodeIKernelMsgListener['onMsgInfoListUpdate']>
|
||||||
|
(
|
||||||
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
(msgs) => {
|
||||||
|
if (msgs.find(m => m.msgId === msg.MsgId && m.recallTime !== '0')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
).catch(e => new Promise<undefined>((resolve, reject) => { resolve(undefined) }));
|
||||||
await NTQQMsgApi.recallMsg(msg.Peer, [msg.MsgId]);
|
await NTQQMsgApi.recallMsg(msg.Peer, [msg.MsgId]);
|
||||||
|
let data = await ret;
|
||||||
|
if (!data) {
|
||||||
|
throw new Error('Recall failed');
|
||||||
|
}
|
||||||
|
//await sleep(100);
|
||||||
|
//await NTQQMsgApi.getMsgsByMsgId(msg.Peer, [msg.MsgId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import {
|
|||||||
IdMusicSignPostData,
|
IdMusicSignPostData,
|
||||||
NTQQFileApi,
|
NTQQFileApi,
|
||||||
NTQQMsgApi,
|
NTQQMsgApi,
|
||||||
|
Peer,
|
||||||
SendArkElement,
|
SendArkElement,
|
||||||
SendMessageElement,
|
SendMessageElement,
|
||||||
SendMsgElementConstructor,
|
SendMsgElementConstructor,
|
||||||
@@ -17,19 +18,19 @@ import { uri2local } from '@/common/utils/file';
|
|||||||
import { ob11Config } from '@/onebot11/config';
|
import { ob11Config } from '@/onebot11/config';
|
||||||
import { RequestUtil } from '@/common/utils/request';
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
import { MessageUnique } from '@/common/utils/MessageUnique';
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
console.log(process.pid)
|
||||||
export type MessageContext = {
|
export type MessageContext = {
|
||||||
group?: Group,
|
|
||||||
deleteAfterSentFiles: string[],
|
deleteAfterSentFiles: string[],
|
||||||
|
peer:Peer
|
||||||
}
|
}
|
||||||
async function handleOb11FileLikeMessage(
|
async function handleOb11FileLikeMessage(
|
||||||
{ data: inputdata }: OB11MessageFileBase,
|
{ data: inputdata }: OB11MessageFileBase,
|
||||||
{ deleteAfterSentFiles }: MessageContext
|
{ deleteAfterSentFiles }: MessageContext
|
||||||
) {
|
) {
|
||||||
//有的奇怪的框架将url作为参数 而不是file 此时优先url
|
//有的奇怪的框架将url作为参数 而不是file 此时优先url
|
||||||
const { path, isLocal, fileName, errMsg } = (await uri2local(inputdata?.url || inputdata.file));
|
const { path, isLocal, fileName, errMsg,success } = (await uri2local(inputdata?.url || inputdata.file));
|
||||||
|
|
||||||
if (errMsg) {
|
if (!success) {
|
||||||
logError('文件下载失败', errMsg);
|
logError('文件下载失败', errMsg);
|
||||||
throw Error('文件下载失败' + errMsg);
|
throw Error('文件下载失败' + errMsg);
|
||||||
}
|
}
|
||||||
@@ -52,12 +53,12 @@ const _handlers: {
|
|||||||
[OB11MessageDataType.text]: async ({ data: { text } }) => SendMsgElementConstructor.text(text),
|
[OB11MessageDataType.text]: async ({ data: { text } }) => SendMsgElementConstructor.text(text),
|
||||||
|
|
||||||
[OB11MessageDataType.at]: async ({ data: { qq: atQQ } }, context) => {
|
[OB11MessageDataType.at]: async ({ data: { qq: atQQ } }, context) => {
|
||||||
if (!context.group) return undefined;
|
if (!context.peer) return undefined;
|
||||||
|
|
||||||
if (atQQ === 'all') return SendMsgElementConstructor.at(atQQ, atQQ, AtType.atAll, '全体成员');
|
if (atQQ === 'all') return SendMsgElementConstructor.at(atQQ, atQQ, AtType.atAll, '全体成员');
|
||||||
|
|
||||||
// then the qq is a group member
|
// then the qq is a group member
|
||||||
const atMember = await getGroupMember(context.group.groupCode, atQQ);
|
const atMember = await getGroupMember(context.peer.peerUid, atQQ);
|
||||||
return atMember ?
|
return atMember ?
|
||||||
SendMsgElementConstructor.at(atQQ, atMember.uid, AtType.atUser, atMember.cardName || atMember.nick) :
|
SendMsgElementConstructor.at(atQQ, atMember.uid, AtType.atUser, atMember.cardName || atMember.nick) :
|
||||||
undefined;
|
undefined;
|
||||||
@@ -206,7 +207,7 @@ const handlers = <{
|
|||||||
|
|
||||||
export default async function createSendElements(
|
export default async function createSendElements(
|
||||||
messageData: OB11MessageData[],
|
messageData: OB11MessageData[],
|
||||||
group?: Group,
|
peer: Peer,
|
||||||
ignoreTypes: OB11MessageDataType[] = []
|
ignoreTypes: OB11MessageDataType[] = []
|
||||||
) {
|
) {
|
||||||
const deleteAfterSentFiles: string[] = [];
|
const deleteAfterSentFiles: string[] = [];
|
||||||
@@ -217,7 +218,7 @@ export default async function createSendElements(
|
|||||||
}
|
}
|
||||||
const callResult = handlers[sendMsg.type](
|
const callResult = handlers[sendMsg.type](
|
||||||
sendMsg,
|
sendMsg,
|
||||||
{ group, deleteAfterSentFiles }
|
{ peer, deleteAfterSentFiles }
|
||||||
)?.catch(undefined);
|
)?.catch(undefined);
|
||||||
callResultList.push(callResult);
|
callResultList.push(callResult);
|
||||||
}
|
}
|
||||||
@@ -228,7 +229,7 @@ export default async function createSendElements(
|
|||||||
|
|
||||||
export async function createSendElementsParallel(
|
export async function createSendElementsParallel(
|
||||||
messageData: OB11MessageData[],
|
messageData: OB11MessageData[],
|
||||||
group?: Group,
|
peer: Peer,
|
||||||
ignoreTypes: OB11MessageDataType[] = []
|
ignoreTypes: OB11MessageDataType[] = []
|
||||||
) {
|
) {
|
||||||
const deleteAfterSentFiles: string[] = [];
|
const deleteAfterSentFiles: string[] = [];
|
||||||
@@ -236,7 +237,7 @@ export async function createSendElementsParallel(
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
messageData.map(async sendMsg => ignoreTypes.includes(sendMsg.type) ?
|
messageData.map(async sendMsg => ignoreTypes.includes(sendMsg.type) ?
|
||||||
undefined :
|
undefined :
|
||||||
handlers[sendMsg.type](sendMsg, { group, deleteAfterSentFiles }))
|
handlers[sendMsg.type](sendMsg, { peer, deleteAfterSentFiles }))
|
||||||
).then(
|
).then(
|
||||||
results => results.filter(
|
results => results.filter(
|
||||||
element => element !== undefined
|
element => element !== undefined
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChatType, ElementType, Group, NTQQMsgApi, Peer, RawMessage, SendMessageElement } from '@/core';
|
import { ChatType, ElementType, Group, NTQQMsgApi, Peer, RawMessage, SendMessageElement } from '@/core';
|
||||||
import { OB11MessageNode } from '@/onebot11/types';
|
import { OB11MessageDataType, OB11MessageNode } from '@/onebot11/types';
|
||||||
import { selfInfo } from '@/core/data';
|
import { selfInfo } from '@/core/data';
|
||||||
import createSendElements from '@/onebot11/action/msg/SendMsg/create-send-elements';
|
import createSendElements from '@/onebot11/action/msg/SendMsg/create-send-elements';
|
||||||
import { logDebug, logError } from '@/common/utils/log';
|
import { logDebug, logError } from '@/common/utils/log';
|
||||||
@@ -24,135 +24,92 @@ async function cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
|
|||||||
if (sendElements.length === 0) {
|
if (sendElements.length === 0) {
|
||||||
logDebug('需要clone的消息无法解析,将会忽略掉', msg);
|
logDebug('需要clone的消息无法解析,将会忽略掉', msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
//logDebug('克隆消息', sendElements);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const nodeMsg = await NTQQMsgApi.sendMsg(selfPeer, sendElements, true);
|
const nodeMsg = await NTQQMsgApi.sendMsg(selfPeer, sendElements, true);
|
||||||
await sleep(500); // 防止风控
|
|
||||||
return nodeMsg;
|
return nodeMsg;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, '克隆转发消息失败,将忽略本条消息', msg);
|
logError(e, '克隆转发消息失败,将忽略本条消息', msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[], group: Group | undefined): Promise<RawMessage | null> {
|
export async function handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> {
|
||||||
const selfPeer = {
|
const selfPeer = {
|
||||||
chatType: ChatType.friend,
|
chatType: ChatType.friend,
|
||||||
peerUid: selfInfo.uid
|
peerUid: selfInfo.uid
|
||||||
};
|
};
|
||||||
let nodeMsgIds: string[] = [];
|
let nodeMsgIds: string[] = [];
|
||||||
|
|
||||||
// 先判断一遍是不是id和自定义混用
|
|
||||||
const needClone =
|
|
||||||
messageNodes.filter(node => node.data.id).length &&
|
|
||||||
messageNodes.filter(node => !node.data.id).length;
|
|
||||||
|
|
||||||
for (const messageNode of messageNodes) {
|
for (const messageNode of messageNodes) {
|
||||||
// 一个node表示一个人的消息
|
|
||||||
const nodeId = messageNode.data.id;
|
const nodeId = messageNode.data.id;
|
||||||
// 有nodeId表示一个子转发消息卡片
|
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId));
|
//对Mgsid和OB11ID混用情况兜底
|
||||||
if (!needClone) {
|
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
|
||||||
nodeMsgIds.push(nodeMsg!.MsgId);
|
if (!nodeMsg) {
|
||||||
} else {
|
logError('转发消息失败,未找到消息', nodeId);
|
||||||
if (nodeMsg!.Peer.peerUid !== selfInfo.uid) {
|
continue;
|
||||||
// need cloning
|
|
||||||
const rawClone = await NTQQMsgApi.getMsgsByMsgId(nodeMsg?.Peer!, [nodeMsg?.MsgId!]);
|
|
||||||
const clonedMsg = await cloneMsg(rawClone.msgList[0]);
|
|
||||||
if (clonedMsg) {
|
|
||||||
nodeMsgIds.push(clonedMsg.msgId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
nodeMsgIds.push(nodeMsg.MsgId);
|
||||||
} else {
|
} else {
|
||||||
// 自定义的消息
|
// 自定义的消息
|
||||||
// 提取消息段,发给自己生成消息id
|
|
||||||
try {
|
try {
|
||||||
const { sendElements } = await createSendElements(normalize(messageNode.data.content), group);
|
let OB11Data = normalize(messageNode.data.content);
|
||||||
//logDebug('开始生成转发节点', sendElements);
|
//筛选node消息
|
||||||
const sendElementsSplit: SendMessageElement[][] = [];
|
let isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息
|
||||||
let splitIndex = 0;
|
if (isNodeMsg !== 0) {
|
||||||
for (const sendElement of sendElements) {
|
if (isNodeMsg !== OB11Data.length) { logError('子消息中包含非node消息 跳过不合法部分'); continue; }
|
||||||
if (!sendElementsSplit[splitIndex]) {
|
const nodeMsg = await handleForwardNode(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
|
||||||
sendElementsSplit[splitIndex] = [];
|
if (nodeMsg) { nodeMsgIds.push(nodeMsg.msgId); MessageUnique.createMsg(selfPeer, nodeMsg.msgId) };
|
||||||
}
|
//完成子卡片生成跳过后续
|
||||||
|
continue;
|
||||||
if (sendElement.elementType === ElementType.FILE || sendElement.elementType === ElementType.VIDEO) {
|
|
||||||
if (sendElementsSplit[splitIndex].length > 0) {
|
|
||||||
splitIndex++;
|
|
||||||
}
|
|
||||||
sendElementsSplit[splitIndex] = [sendElement];
|
|
||||||
splitIndex++;
|
|
||||||
} else {
|
|
||||||
sendElementsSplit[splitIndex].push(sendElement);
|
|
||||||
}
|
|
||||||
//logDebug(sendElementsSplit);
|
|
||||||
}
|
}
|
||||||
// log("分割后的转发节点", sendElementsSplit)
|
const { sendElements } = await createSendElements(OB11Data, destPeer);
|
||||||
|
//拆分消息
|
||||||
|
let MixElement = sendElements.filter(element => element.elementType !== ElementType.FILE && element.elementType !== ElementType.VIDEO);
|
||||||
|
let SingleElement = sendElements.filter(element => element.elementType === ElementType.FILE || element.elementType === ElementType.VIDEO).map(e => [e]);
|
||||||
|
let AllElement: SendMessageElement[][] = [MixElement, ...SingleElement].filter(e => e !== undefined && e.length !== 0);
|
||||||
const MsgNodeList: Promise<RawMessage | undefined>[] = [];
|
const MsgNodeList: Promise<RawMessage | undefined>[] = [];
|
||||||
for (const sendElementsSplitElement of sendElementsSplit) {
|
for (const sendElementsSplitElement of AllElement) {
|
||||||
MsgNodeList.push(sendMsg(selfPeer, sendElementsSplitElement, [], true));
|
MsgNodeList.push(sendMsg(selfPeer, sendElementsSplitElement, [], true).catch(e => new Promise((resolve, reject) => { resolve(undefined) })));
|
||||||
await sleep(Math.trunc(sendElementsSplit.length / 10) * 100);
|
|
||||||
//await sleep(10);
|
|
||||||
}
|
}
|
||||||
for (const msgNode of MsgNodeList) {
|
(await Promise.allSettled(MsgNodeList)).map((result) => {
|
||||||
const result = await msgNode;
|
if (result.status === 'fulfilled' && result.value) {
|
||||||
if (result) {
|
nodeMsgIds.push(result.value.msgId);
|
||||||
nodeMsgIds.push(result.msgId);
|
MessageUnique.createMsg(selfPeer, result.value.msgId);
|
||||||
}
|
}
|
||||||
//logDebug('转发节点生成成功', result.msgId);
|
});
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logDebug('生成转发消息节点失败', e);
|
logDebug('生成转发消息节点失败', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 检查srcPeer是否一致,不一致则需要克隆成自己的消息, 让所有srcPeer都变成自己的,使其保持一致才能够转发
|
|
||||||
const nodeMsgArray: Array<RawMessage> = [];
|
const nodeMsgArray: Array<RawMessage> = [];
|
||||||
|
|
||||||
let srcPeer: Peer | undefined = undefined;
|
let srcPeer: Peer | undefined = undefined;
|
||||||
let needSendSelf = false;
|
let needSendSelf = false;
|
||||||
for (const msgId of nodeMsgIds) {
|
//检测是否处于同一个Peer 不在同一个peer则全部消息由自身发送
|
||||||
const nodeMsgPeer = await MessageUnique.getPeerByMsgId(msgId);
|
for (let msgId of nodeMsgIds) {
|
||||||
const nodeMsg = (await NTQQMsgApi.getMsgsByMsgId(nodeMsgPeer?.Peer!, [msgId])).msgList[0];
|
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId);
|
||||||
if (nodeMsg) {
|
if (!nodeMsgPeer) {
|
||||||
nodeMsgArray.push(nodeMsg);
|
logError('转发消息失败,未找到消息', msgId);
|
||||||
if (!srcPeer) {
|
continue;
|
||||||
srcPeer = { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
|
|
||||||
} else if (srcPeer.peerUid !== nodeMsg.peerUid) {
|
|
||||||
needSendSelf = true;
|
|
||||||
srcPeer = selfPeer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const nodeMsg = (await NTQQMsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
|
||||||
|
srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
|
||||||
|
if (srcPeer.peerUid !== nodeMsg.peerUid) {
|
||||||
|
needSendSelf = true;
|
||||||
|
}
|
||||||
|
nodeMsgArray.push(nodeMsg);
|
||||||
}
|
}
|
||||||
// logDebug('nodeMsgArray', nodeMsgArray);
|
|
||||||
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
|
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
|
||||||
|
let retMsgIds: string[] = [];
|
||||||
if (needSendSelf) {
|
if (needSendSelf) {
|
||||||
//logDebug('需要克隆转发消息');
|
|
||||||
for (const [index, msg] of nodeMsgArray.entries()) {
|
for (const [index, msg] of nodeMsgArray.entries()) {
|
||||||
if (msg.peerUid !== selfInfo.uid) {
|
if (msg.peerUid === selfInfo.uid) continue;
|
||||||
const clonedMsg = await cloneMsg(msg);
|
const ClonedMsg = await cloneMsg(msg);
|
||||||
if (clonedMsg) {
|
if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId);
|
||||||
nodeMsgIds[index] = clonedMsg.msgId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
retMsgIds = nodeMsgIds;
|
||||||
}
|
}
|
||||||
// elements之间用换行符分隔
|
if (nodeMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
|
||||||
// let _sendForwardElements: SendMessageElement[] = []
|
|
||||||
// for(let i = 0; i < sendForwardElements.length; i++){
|
|
||||||
// _sendForwardElements.push(sendForwardElements[i])
|
|
||||||
// _sendForwardElements.push(SendMsgElementConstructor.text("\n\n"))
|
|
||||||
// }
|
|
||||||
// const nodeMsg = await NTQQApi.sendMsg(selfPeer, _sendForwardElements, true);
|
|
||||||
// nodeIds.push(nodeMsg.msgId)
|
|
||||||
// await sleep(500);
|
|
||||||
// 开发转发
|
|
||||||
if (nodeMsgIds.length === 0) {
|
|
||||||
throw Error('转发消息失败,生成节点为空');
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
logDebug('开发转发', srcPeer, destPeer, nodeMsgIds);
|
logDebug('开发转发', srcPeer, destPeer, nodeMsgIds);
|
||||||
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds);
|
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds);
|
||||||
|
@@ -76,9 +76,7 @@ export async function sendMsg(peer: Peer, sendElements: SendMessageElement[], de
|
|||||||
return returnMsg;
|
return returnMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createContext(payload: OB11PostSendMsg, contextMode: ContextMode): Promise<{
|
async function createContext(payload: OB11PostSendMsg, contextMode: ContextMode): Promise<Peer> {
|
||||||
peer: Peer, group?: Group
|
|
||||||
}> {
|
|
||||||
// This function determines the type of message by the existence of user_id / group_id,
|
// This function determines the type of message by the existence of user_id / group_id,
|
||||||
// not message_type.
|
// not message_type.
|
||||||
// This redundant design of Ob11 here should be blamed.
|
// This redundant design of Ob11 here should be blamed.
|
||||||
@@ -86,11 +84,8 @@ async function createContext(payload: OB11PostSendMsg, contextMode: ContextMode)
|
|||||||
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
|
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
|
||||||
const group = (await getGroup(payload.group_id))!; // checked before
|
const group = (await getGroup(payload.group_id))!; // checked before
|
||||||
return {
|
return {
|
||||||
peer: {
|
chatType: ChatType.group,
|
||||||
chatType: ChatType.group,
|
peerUid: group.groupCode
|
||||||
peerUid: group.groupCode
|
|
||||||
},
|
|
||||||
group: group,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
|
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
|
||||||
@@ -98,10 +93,8 @@ async function createContext(payload: OB11PostSendMsg, contextMode: ContextMode)
|
|||||||
const isBuddy = await NTQQFriendApi.isBuddy(Uid!);
|
const isBuddy = await NTQQFriendApi.isBuddy(Uid!);
|
||||||
//console.log("[调试代码] UIN:", payload.user_id, " UID:", Uid, " IsBuddy:", isBuddy);
|
//console.log("[调试代码] UIN:", payload.user_id, " UID:", Uid, " IsBuddy:", isBuddy);
|
||||||
return {
|
return {
|
||||||
peer: {
|
chatType: isBuddy ? ChatType.friend : ChatType.temp,
|
||||||
chatType: isBuddy ? ChatType.friend : ChatType.temp,
|
peerUid: Uid!
|
||||||
peerUid: Uid!
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw '请指定 group_id 或 user_id';
|
throw '请指定 group_id 或 user_id';
|
||||||
@@ -139,7 +132,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
protected async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
||||||
const { peer, group } = await createContext(payload, this.contextMode);
|
const peer = await createContext(payload, this.contextMode);
|
||||||
|
|
||||||
const messages = normalize(
|
const messages = normalize(
|
||||||
payload.message,
|
payload.message,
|
||||||
@@ -147,9 +140,9 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
||||||
const returnMsg = await handleForwardNode(peer, messages as OB11MessageNode[], group);
|
const returnMsg = await handleForwardNode(peer, messages as OB11MessageNode[]);
|
||||||
if (returnMsg) {
|
if (returnMsg) {
|
||||||
const msgShortId = await MessageUnique.createMsg({ guildId: '', peerUid: peer.peerUid, chatType: peer.chatType }, returnMsg!.msgId);
|
const msgShortId = MessageUnique.createMsg({ guildId: '', peerUid: peer.peerUid, chatType: peer.chatType }, returnMsg!.msgId);
|
||||||
return { message_id: msgShortId! };
|
return { message_id: msgShortId! };
|
||||||
} else {
|
} else {
|
||||||
throw Error('发送转发消息失败');
|
throw Error('发送转发消息失败');
|
||||||
@@ -163,7 +156,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
// log("send msg:", peer, sendElements)
|
// log("send msg:", peer, sendElements)
|
||||||
|
|
||||||
const { sendElements, deleteAfterSentFiles } = await createSendElements(messages, group);
|
const { sendElements, deleteAfterSentFiles } = await createSendElements(messages, peer);
|
||||||
//console.log(peer, JSON.stringify(sendElements,null,2));
|
//console.log(peer, JSON.stringify(sendElements,null,2));
|
||||||
const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles);
|
const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles);
|
||||||
return { message_id: returnMsg!.id! };
|
return { message_id: returnMsg!.id! };
|
||||||
|
@@ -102,5 +102,6 @@ export enum ActionName {
|
|||||||
SetGroupHeader = 'set_group_head',
|
SetGroupHeader = 'set_group_head',
|
||||||
FetchCustomFace = 'fetch_custom_face',
|
FetchCustomFace = 'fetch_custom_face',
|
||||||
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
|
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
|
||||||
TestApi01 = 'test_api_01'
|
TestApi01 = 'test_api_01',
|
||||||
|
FetchEmojioLike = 'fetch_emoji_like'
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,52 @@
|
|||||||
|
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
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;
|
actionName = ActionName.GetRecentContact;
|
||||||
protected async _handle(payload: void) {
|
PayloadSchema = SchemaData;
|
||||||
//没有效果
|
protected async _handle(payload: Payload) {
|
||||||
return await NTQQUserApi.getRecentContactListSnapShot(10);
|
const ret = await NTQQUserApi.getRecentContactListSnapShot(parseInt((payload.count || 10).toString()));
|
||||||
|
const data = await Promise.all(ret.info.changedList.map(async (t) => {
|
||||||
|
const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
|
||||||
|
if (FastMsg.msgList.length > 0) {
|
||||||
|
//扩展ret.info.changedList
|
||||||
|
const 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -65,7 +65,7 @@ export class OB11Constructor {
|
|||||||
real_id: msg.id!,
|
real_id: msg.id!,
|
||||||
message_type: msg.chatType == ChatType.group ? 'group' : 'private',
|
message_type: msg.chatType == ChatType.group ? 'group' : 'private',
|
||||||
sender: {
|
sender: {
|
||||||
user_id: parseInt(msg.senderUin!),
|
user_id: parseInt(msg.senderUin || '0'),
|
||||||
nickname: msg.sendNickName,
|
nickname: msg.sendNickName,
|
||||||
card: msg.sendMemberName || '',
|
card: msg.sendMemberName || '',
|
||||||
},
|
},
|
||||||
@@ -153,21 +153,22 @@ export class OB11Constructor {
|
|||||||
message_data['type'] = OB11MessageDataType.reply;
|
message_data['type'] = OB11MessageDataType.reply;
|
||||||
//log("收到回复消息", element.replyElement);
|
//log("收到回复消息", element.replyElement);
|
||||||
try {
|
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 = {
|
const peer = {
|
||||||
chatType: msg.chatType,
|
chatType: msg.chatType,
|
||||||
peerUid: msg.peerUid,
|
peerUid: msg.peerUid,
|
||||||
guildId: '',
|
guildId: '',
|
||||||
};
|
};
|
||||||
let replyMsg: RawMessage | undefined;
|
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];
|
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];
|
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0];
|
||||||
}
|
}
|
||||||
|
if (msg.peerUin == '284840486') {
|
||||||
if (!replyMsg || element.replyElement.replayMsgSeq !== replyMsg.msgSeq) {
|
//合并消息内侧 消息具体定位不到
|
||||||
|
}
|
||||||
|
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
|
||||||
throw new Error('回复消息消息验证失败');
|
throw new Error('回复消息消息验证失败');
|
||||||
}
|
}
|
||||||
message_data['data']['id'] = MessageUnique.createMsg({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, replyMsg.msgId)?.toString();
|
message_data['data']['id'] = MessageUnique.createMsg({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, replyMsg.msgId)?.toString();
|
||||||
@@ -222,7 +223,11 @@ export class OB11Constructor {
|
|||||||
const videoElement: VideoElement = element.videoElement;
|
const videoElement: VideoElement = element.videoElement;
|
||||||
//读取视频链接并兜底
|
//读取视频链接并兜底
|
||||||
let videoUrl;//Array
|
let videoUrl;//Array
|
||||||
|
if (msg.peerUin = '284840486') {
|
||||||
|
//合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
|
||||||
videoUrl = await NTQQFileApi.getVideoUrl({
|
videoUrl = await NTQQFileApi.getVideoUrl({
|
||||||
chatType: msg.chatType,
|
chatType: msg.chatType,
|
||||||
peerUid: msg.peerUid,
|
peerUid: msg.peerUid,
|
||||||
@@ -328,6 +333,27 @@ export class OB11Constructor {
|
|||||||
else if (element.multiForwardMsgElement) {
|
else if (element.multiForwardMsgElement) {
|
||||||
message_data['type'] = OB11MessageDataType.forward;
|
message_data['type'] = OB11MessageDataType.forward;
|
||||||
message_data['data']['id'] = msg.msgId;
|
message_data['data']['id'] = msg.msgId;
|
||||||
|
const ParentMsgPeer = msg.parentMsgPeer ?? { chatType: msg.chatType, guildId: '', peerUid: msg.peerUid };
|
||||||
|
//判断是否在合并消息内
|
||||||
|
msg.parentMsgIdList = msg.parentMsgIdList ?? [];
|
||||||
|
//首次列表不存在则开始创建
|
||||||
|
msg.parentMsgIdList.push(msg.msgId);
|
||||||
|
//let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2];
|
||||||
|
//加入自身MsgId
|
||||||
|
let MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList;
|
||||||
|
//拉取下级消息
|
||||||
|
if (!MultiMsgs) continue;
|
||||||
|
//拉取失败则跳过
|
||||||
|
message_data['data']['content'] = [];
|
||||||
|
for (let MultiMsg of MultiMsgs) {
|
||||||
|
//对每条拉取的消息传递ParentMsgPeer修正Peer
|
||||||
|
MultiMsg.parentMsgPeer = ParentMsgPeer;
|
||||||
|
MultiMsg.parentMsgIdList = msg.parentMsgIdList;
|
||||||
|
MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId);//该ID仅用查看 无法调用
|
||||||
|
let msgList = await OB11Constructor.message(MultiMsg);
|
||||||
|
message_data['data']['content'].push(msgList);
|
||||||
|
//console.log("合并消息", msgList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((message_data.type as string) !== 'unknown' && message_data.data) {
|
if ((message_data.type as string) !== 'unknown' && message_data.data) {
|
||||||
const cqCode = encodeCQCode(message_data);
|
const cqCode = encodeCQCode(message_data);
|
||||||
@@ -459,6 +485,7 @@ export class OB11Constructor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (grayTipElement) {
|
if (grayTipElement) {
|
||||||
|
//console.log('收到群提示消息', grayTipElement);
|
||||||
if (grayTipElement.xmlElement?.templId === '10382') {
|
if (grayTipElement.xmlElement?.templId === '10382') {
|
||||||
const emojiLikeData = new fastXmlParser.XMLParser({
|
const emojiLikeData = new fastXmlParser.XMLParser({
|
||||||
ignoreAttributes: false,
|
ignoreAttributes: false,
|
||||||
|
@@ -2,7 +2,7 @@ import { OB11Message } from '@/onebot11/types';
|
|||||||
import { log } from '@/common/utils/log';
|
import { log } from '@/common/utils/log';
|
||||||
import { getGroup, getGroupMember, selfInfo } from '@/core/data';
|
import { getGroup, getGroupMember, selfInfo } from '@/core/data';
|
||||||
import exp from 'constants';
|
import exp from 'constants';
|
||||||
import { Group } from '@/core';
|
import { Group, NTQQMsgApi } from '@/core';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
const spSegColor = chalk.blue;// for special segment
|
const spSegColor = chalk.blue;// for special segment
|
||||||
@@ -24,8 +24,13 @@ export async function logMessage(ob11Message: OB11Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ob11Message.message_type === 'group') {
|
if (ob11Message.message_type === 'group') {
|
||||||
group = await getGroup(ob11Message.group_id!);
|
if (ob11Message.group_id == 284840486) {
|
||||||
prefix += `群[${group?.groupName}(${ob11Message.group_id})] `;
|
group = await getGroup(ob11Message.group_id!);
|
||||||
|
prefix += `转发消息[外部来源] `;
|
||||||
|
} else {
|
||||||
|
group = await getGroup(ob11Message.group_id!);
|
||||||
|
prefix += `群[${group?.groupName}(${ob11Message.group_id})] `;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let msgChain = '';
|
let msgChain = '';
|
||||||
if (Array.isArray(ob11Message.message)) {
|
if (Array.isArray(ob11Message.message)) {
|
||||||
@@ -66,6 +71,13 @@ export async function logMessage(ob11Message: OB11Message) {
|
|||||||
else if (segment.type === 'video') {
|
else if (segment.type === 'video') {
|
||||||
msgParts.push(spSegColor(`[视频|${segment.data.url}]`));
|
msgParts.push(spSegColor(`[视频|${segment.data.url}]`));
|
||||||
}
|
}
|
||||||
|
else if (segment.type === 'forward') {
|
||||||
|
msgParts.push(spSegColor(`[转发|${segment.data.id}|消息开始]`));
|
||||||
|
segment.data.content.forEach((msg) => {
|
||||||
|
logMessage(msg);
|
||||||
|
});
|
||||||
|
msgParts.push(spSegColor(`[转发|${segment.data.id}|消息结束]`));
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
msgParts.push(spSegColor(`[未实现|${JSON.stringify(segment)}]`));
|
msgParts.push(spSegColor(`[未实现|${JSON.stringify(segment)}]`));
|
||||||
}
|
}
|
||||||
|
@@ -98,6 +98,8 @@ export class NapCatOnebot11 {
|
|||||||
// .map(x => x.toString(16).padStart(2, '0'))
|
// .map(x => x.toString(16).padStart(2, '0'))
|
||||||
// .join('');
|
// .join('');
|
||||||
// }
|
// }
|
||||||
|
// const hex = buf2hex(Buffer.from(protobufData));
|
||||||
|
// console.log(hex);
|
||||||
// // let Data: Data = {
|
// // let Data: Data = {
|
||||||
// // header: {
|
// // header: {
|
||||||
// // GroupNumber: 0,
|
// // GroupNumber: 0,
|
||||||
@@ -192,6 +194,7 @@ export class NapCatOnebot11 {
|
|||||||
// // 732 17 GroupRecall
|
// // 732 17 GroupRecall
|
||||||
// // 732 20 GroupCommonTips
|
// // 732 20 GroupCommonTips
|
||||||
// // 732 21 EssenceMessage
|
// // 732 21 EssenceMessage
|
||||||
|
// // 732 16 16 GrayTips ->Busi->GroupEmjioLike
|
||||||
// } catch (e) {
|
// } catch (e) {
|
||||||
// log('解析SysMsg异常', e);
|
// log('解析SysMsg异常', e);
|
||||||
// // console.log(e);
|
// // console.log(e);
|
||||||
@@ -209,7 +212,7 @@ export class NapCatOnebot11 {
|
|||||||
// 临时会话更新 tempGroupCodeMap uid -> source/GroupCode
|
// 临时会话更新 tempGroupCodeMap uid -> source/GroupCode
|
||||||
};
|
};
|
||||||
msgListener.onRecvMsg = async (msg) => {
|
msgListener.onRecvMsg = async (msg) => {
|
||||||
|
|
||||||
//console.log('ob11 onRecvMsg', JSON.stringify(msg, null, 2));
|
//console.log('ob11 onRecvMsg', JSON.stringify(msg, null, 2));
|
||||||
// logDebug('收到消息', msg);
|
// logDebug('收到消息', msg);
|
||||||
for (const m of msg) {
|
for (const m of msg) {
|
||||||
@@ -291,7 +294,7 @@ export class NapCatOnebot11 {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
groupListener.onMemberInfoChange = async (groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
|
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) {
|
if (changeType === 1) {
|
||||||
let member;
|
let member;
|
||||||
for (const [key, value] of members) {
|
for (const [key, value] of members) {
|
||||||
|
@@ -11,7 +11,7 @@ class OB11HTTPServer extends HttpServerBase {
|
|||||||
name = 'OneBot V11 server';
|
name = 'OneBot V11 server';
|
||||||
|
|
||||||
handleFailed(res: Response, payload: any, e: Error) {
|
handleFailed(res: Response, payload: any, e: Error) {
|
||||||
res.send(OB11Response.error(e?.stack?.toString() || e.message || "Error Handle", 200));
|
res.send(OB11Response.error(e?.stack?.toString() || e.message || 'Error Handle', 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected listen(port: number, host: string) {
|
protected listen(port: number, host: string) {
|
||||||
|
@@ -159,7 +159,7 @@ async function handleMsg(msg: OB11Message, quickAction: QuickAction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
replyMessage = replyMessage.concat(normalize(reply, quickAction.auto_escape));
|
replyMessage = replyMessage.concat(normalize(reply, quickAction.auto_escape));
|
||||||
const { sendElements, deleteAfterSentFiles } = await createSendElements(replyMessage, group);
|
const { sendElements, deleteAfterSentFiles } = await createSendElements(replyMessage, peer);
|
||||||
sendMsg(peer, sendElements, deleteAfterSentFiles, false).then().catch(logError);
|
sendMsg(peer, sendElements, deleteAfterSentFiles, false).then().catch(logError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -181,7 +181,8 @@ export interface OB11MessageMarkdown {
|
|||||||
export interface OB11MessageForward {
|
export interface OB11MessageForward {
|
||||||
type: OB11MessageDataType.forward
|
type: OB11MessageDataType.forward
|
||||||
data: {
|
data: {
|
||||||
id: string
|
id: string,
|
||||||
|
content: OB11Message[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const version = '1.7.7';
|
export const version = '1.8.2';
|
||||||
|
@@ -29,7 +29,7 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
undefined,
|
undefined,
|
||||||
SettingButton('V1.7.7', 'napcat-update-button', 'secondary')
|
SettingButton('V1.8.2', 'napcat-update-button', 'secondary')
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -163,7 +163,7 @@ async function onSettingWindowCreated(view) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
void 0,
|
void 0,
|
||||||
SettingButton("V1.7.7", "napcat-update-button", "secondary")
|
SettingButton("V1.8.2", "napcat-update-button", "secondary")
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
Reference in New Issue
Block a user