Full Code of lz1998/rs-qq for AI

master 46c44a3eda7a cached
263 files
894.4 KB
258.1k tokens
1085 symbols
1 requests
Download .txt
Showing preview only (977K chars total). Download the full file or copy to clipboard to get everything.
Repository: lz1998/rs-qq
Branch: master
Commit: 46c44a3eda7a
Files: 263
Total size: 894.4 KB

Directory structure:
gitextract_sa4m0udb/

├── .gitignore
├── Cargo.toml
├── LICENSE
├── examples/
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── password_login/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       └── main.rs
│   ├── qrcode_login/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       └── main.rs
│   ├── ricq-axum-api/
│   │   ├── Cargo.toml
│   │   ├── README.md
│   │   └── src/
│   │       ├── bin/
│   │       │   └── main.rs
│   │       ├── handler/
│   │       │   ├── bot.rs
│   │       │   ├── mod.rs
│   │       │   ├── password.rs
│   │       │   └── qrcode.rs
│   │       ├── lib.rs
│   │       ├── processor.rs
│   │       └── u8_protocol.rs
│   └── token_login/
│       ├── Cargo.toml
│       └── src/
│           └── main.rs
├── ricq/
│   ├── Cargo.toml
│   ├── README.md
│   └── src/
│       ├── client/
│       │   ├── api/
│       │   │   ├── friend.rs
│       │   │   ├── group.rs
│       │   │   ├── login.rs
│       │   │   └── mod.rs
│       │   ├── event.rs
│       │   ├── handler/
│       │   │   └── mod.rs
│       │   ├── highway/
│       │   │   ├── codec.rs
│       │   │   ├── mod.rs
│       │   │   └── net.rs
│       │   ├── mod.rs
│       │   ├── net.rs
│       │   ├── processor/
│       │   │   ├── c2c/
│       │   │   │   ├── friend_msg.rs
│       │   │   │   ├── friend_system_msg.rs
│       │   │   │   ├── group_system_msg.rs
│       │   │   │   ├── mod.rs
│       │   │   │   ├── new_member.rs
│       │   │   │   └── temp_session.rs
│       │   │   ├── config_push_svc.rs
│       │   │   ├── message_svc.rs
│       │   │   ├── mod.rs
│       │   │   ├── online_push.rs
│       │   │   ├── reg_prxy_svc.rs
│       │   │   ├── stat_svc.rs
│       │   │   └── wtlogin.rs
│       │   ├── qimei.rs
│       │   └── tcp.rs
│       ├── config.rs
│       ├── ext/
│       │   ├── common.rs
│       │   ├── image.rs
│       │   ├── login.rs
│       │   ├── mod.rs
│       │   └── reconnect.rs
│       ├── lib.rs
│       ├── qsign.rs
│       └── structs/
│           ├── image_info.rs
│           └── mod.rs
├── ricq-core/
│   ├── Cargo.toml
│   ├── build.rs
│   └── src/
│       ├── binary/
│       │   ├── binary_reader.rs
│       │   ├── binary_writer.rs
│       │   ├── mod.rs
│       │   └── packet_writer.rs
│       ├── command/
│       │   ├── common.rs
│       │   ├── config_push_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── friendlist/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── group_anonymous_generate_nick/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── group_member_card/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── heartbeat/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   ├── img_store/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── long_conn/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── longmsg/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   ├── message_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── mod.rs
│       │   ├── multi_msg/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── oidb_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── online_push/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── pb_message_svc/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   ├── profile_service/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── ptt_center_svr/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── ptt_store/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── reg_prxy_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── signature/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   ├── stat_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── summary_card/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── visitor_svc/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   └── wtlogin/
│       │       ├── builder.rs
│       │       ├── decoder.rs
│       │       ├── mod.rs
│       │       ├── tlv_reader.rs
│       │       └── tlv_writer.rs
│       ├── common.rs
│       ├── crypto/
│       │   ├── encrypt.rs
│       │   ├── mod.rs
│       │   └── qqtea.rs
│       ├── error.rs
│       ├── hex.rs
│       ├── highway/
│       │   └── mod.rs
│       ├── jce/
│       │   └── mod.rs
│       ├── lib.rs
│       ├── msg/
│       │   ├── elem/
│       │   │   ├── anonymous.rs
│       │   │   ├── at.rs
│       │   │   ├── face.rs
│       │   │   ├── flash_image.rs
│       │   │   ├── friend_image.rs
│       │   │   ├── group_image.rs
│       │   │   ├── light_app.rs
│       │   │   ├── market_face.rs
│       │   │   ├── mod.rs
│       │   │   ├── reply.rs
│       │   │   ├── rich_msg.rs
│       │   │   ├── text.rs
│       │   │   └── video_file.rs
│       │   ├── fragment.rs
│       │   ├── macros.rs
│       │   └── mod.rs
│       ├── pb/
│       │   ├── cmd0x346/
│       │   │   └── cmd0x346.proto
│       │   ├── cmd0x352/
│       │   │   └── cmd0x352.proto
│       │   ├── cmd0x388/
│       │   │   └── cmd0x388.proto
│       │   ├── cmd0x3bb/
│       │   │   └── cmd0x3bb.proto
│       │   ├── cmd0x6ff/
│       │   │   ├── smbcmd0x519.proto
│       │   │   └── subcmd0x501.proto
│       │   ├── cmd0x899/
│       │   │   └── cmd0x899.proto
│       │   ├── data.proto
│       │   ├── longmsg/
│       │   │   └── longmsg.proto
│       │   ├── mod.rs
│       │   ├── msf/
│       │   │   └── register_proxy.proto
│       │   ├── msg/
│       │   │   ├── TextMsgExt.proto
│       │   │   ├── head.proto
│       │   │   ├── msg.proto
│       │   │   ├── objmsg.proto
│       │   │   └── report.proto
│       │   ├── msgtype0x210/
│       │   │   └── subMsgType0x27.proto
│       │   ├── multimsg/
│       │   │   └── multimsg.proto
│       │   ├── notify/
│       │   │   └── group0x857.proto
│       │   ├── oidb/
│       │   │   ├── oidb.proto
│       │   │   ├── oidb0x6d6.proto
│       │   │   ├── oidb0x6d8.proto
│       │   │   ├── oidb0x758.proto
│       │   │   ├── oidb0x769.proto
│       │   │   ├── oidb0x88d.proto
│       │   │   ├── oidb0x8a7.proto
│       │   │   ├── oidb0x8fc.proto
│       │   │   ├── oidb0x990.proto
│       │   │   ├── oidb0xb77.proto
│       │   │   ├── oidb0xe07.proto
│       │   │   ├── oidb0xeac.proto
│       │   │   └── oidb0xeb7.proto
│       │   ├── online_status/
│       │   │   └── OnlineStatusExtInfo.java.proto
│       │   ├── profilecard/
│       │   │   ├── busi.proto
│       │   │   └── gate.proto
│       │   ├── short_video/
│       │   │   └── short_video.proto
│       │   ├── sig_act/
│       │   │   └── sig_act.proto
│       │   └── structmsg/
│       │       └── structmsg.proto
│       ├── protocol/
│       │   ├── device.rs
│       │   ├── mod.rs
│       │   ├── oicq.rs
│       │   ├── packet.rs
│       │   ├── qimei.rs
│       │   ├── sig.rs
│       │   ├── transport.rs
│       │   └── version.rs
│       ├── structs.rs
│       ├── token.rs
│       ├── utils/
│       │   ├── mod.rs
│       │   └── option_set.rs
│       └── wtlogin.rs
├── ricq-guild/
│   ├── Cargo.toml
│   ├── README.md
│   ├── build.rs
│   └── src/
│       ├── client/
│       │   ├── builder.rs
│       │   ├── decoder.rs
│       │   ├── mod.rs
│       │   └── processor.rs
│       ├── lib.rs
│       └── protocol/
│           ├── core/
│           │   ├── cmd0x346/
│           │   │   └── cmd0x346.proto
│           │   ├── cmd0x352/
│           │   │   └── cmd0x352.proto
│           │   ├── cmd0x388/
│           │   │   └── cmd0x388.proto
│           │   ├── cmd0x3bb/
│           │   │   └── cmd0x3bb.proto
│           │   ├── cmd0x6ff/
│           │   │   ├── smbcmd0x519.proto
│           │   │   └── subcmd0x501.proto
│           │   ├── cmd0x899/
│           │   │   └── cmd0x899.proto
│           │   ├── data.proto
│           │   ├── longmsg/
│           │   │   └── longmsg.proto
│           │   ├── msf/
│           │   │   └── register_proxy.proto
│           │   ├── msg/
│           │   │   ├── TextMsgExt.proto
│           │   │   ├── head.proto
│           │   │   ├── msg.proto
│           │   │   ├── objmsg.proto
│           │   │   └── report.proto
│           │   ├── msgtype0x210/
│           │   │   └── subMsgType0x27.proto
│           │   ├── multimsg/
│           │   │   └── multimsg.proto
│           │   ├── notify/
│           │   │   └── group0x857.proto
│           │   ├── oidb/
│           │   │   ├── oidb.proto
│           │   │   ├── oidb0x6d6.proto
│           │   │   ├── oidb0x758.proto
│           │   │   ├── oidb0x769.proto
│           │   │   ├── oidb0x88d.proto
│           │   │   ├── oidb0x8a7.proto
│           │   │   ├── oidb0x8fc.proto
│           │   │   ├── oidb0x990.proto
│           │   │   ├── oidb0xb77.proto
│           │   │   ├── oidb0xe07.proto
│           │   │   ├── oidb0xeac.proto
│           │   │   └── oidb0xeb7.proto
│           │   ├── online_status/
│           │   │   └── OnlineStatusExtInfo.java.proto
│           │   ├── profilecard/
│           │   │   ├── busi.proto
│           │   │   └── gate.proto
│           │   ├── short_video/
│           │   │   └── short_video.proto
│           │   ├── sig_act/
│           │   │   └── sig_act.proto
│           │   └── structmsg/
│           │       └── structmsg.proto
│           ├── mod.rs
│           └── protobuf/
│               ├── GuildChannelBase.proto
│               ├── GuildFeedCloudMeta.proto
│               ├── GuildFeedCloudRead.proto
│               ├── GuildWriter.proto
│               ├── MsgResponsesSvr.proto
│               ├── common.proto
│               ├── msgpush.proto
│               ├── oidb0xf62.proto
│               ├── servtype.proto
│               ├── synclogic.proto
│               └── unknown.proto
└── rust-toolchain.toml

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.idea
/target
/examples/target
/examples/*/target
.DS_Store
Cargo.lock
.vscode
qrcode.png
device.json
test.png
*.amr
*.silk
*.token
*.mp4

================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "2"
members = [
    "ricq",
    "ricq-core",
    "ricq-guild"
]

[workspace.dependencies]
async-trait = "0.1"
bytes = "1"
cached = { version = "0.35", default-features = false }
derivative = "2"
flate2 = { version = "1", features = ["rust_backend"], default-features = false }
futures-util = "0.3"
image = "0.24"
jcers = "0.1"
md5 = "0.7"
prost = { version = "0.9", default-features = false }
rand = "0.8"
serde = "1"
tokio = "1"
tokio-util = "0.7"
tracing = "0.1"

byteorder = "1"
generic-array = "0.14"
p256 = { version = "0.10", default-features = false }
prost-types = "0.9"
thiserror = "1"

dynamic-protobuf = "0"
reqwest = "0.11"
crypto-common = "0.1"
serde_json = "1.0"
block-padding = "0.3"
spki = "0.7"
base64 = "0.21"
aes = "0.8"
rsa = "0.9"
x509-cert = "0.2"
chrono = "0.4"
cbc = "0.1"
sha2 = "0.10"


================================================
FILE: LICENSE
================================================
Mozilla Public License Version 2.0
==================================

1. Definitions
--------------

1.1. "Contributor"
    means each individual or legal entity that creates, contributes to
    the creation of, or owns Covered Software.

1.2. "Contributor Version"
    means the combination of the Contributions of others (if any) used
    by a Contributor and that particular Contributor's Contribution.

1.3. "Contribution"
    means Covered Software of a particular Contributor.

1.4. "Covered Software"
    means Source Code Form to which the initial Contributor has attached
    the notice in Exhibit A, the Executable Form of such Source Code
    Form, and Modifications of such Source Code Form, in each case
    including portions thereof.

1.5. "Incompatible With Secondary Licenses"
    means

    (a) that the initial Contributor has attached the notice described
        in Exhibit B to the Covered Software; or

    (b) that the Covered Software was made available under the terms of
        version 1.1 or earlier of the License, but not also under the
        terms of a Secondary License.

1.6. "Executable Form"
    means any form of the work other than Source Code Form.

1.7. "Larger Work"
    means a work that combines Covered Software with other material, in
    a separate file or files, that is not Covered Software.

1.8. "License"
    means this document.

1.9. "Licensable"
    means having the right to grant, to the maximum extent possible,
    whether at the time of the initial grant or subsequently, any and
    all of the rights conveyed by this License.

1.10. "Modifications"
    means any of the following:

    (a) any file in Source Code Form that results from an addition to,
        deletion from, or modification of the contents of Covered
        Software; or

    (b) any new file in Source Code Form that contains any Covered
        Software.

1.11. "Patent Claims" of a Contributor
    means any patent claim(s), including without limitation, method,
    process, and apparatus claims, in any patent Licensable by such
    Contributor that would be infringed, but for the grant of the
    License, by the making, using, selling, offering for sale, having
    made, import, or transfer of either its Contributions or its
    Contributor Version.

1.12. "Secondary License"
    means either the GNU General Public License, Version 2.0, the GNU
    Lesser General Public License, Version 2.1, the GNU Affero General
    Public License, Version 3.0, or any later versions of those
    licenses.

1.13. "Source Code Form"
    means the form of the work preferred for making modifications.

1.14. "You" (or "Your")
    means an individual or a legal entity exercising rights under this
    License. For legal entities, "You" includes any entity that
    controls, is controlled by, or is under common control with You. For
    purposes of this definition, "control" means (a) the power, direct
    or indirect, to cause the direction or management of such entity,
    whether by contract or otherwise, or (b) ownership of more than
    fifty percent (50%) of the outstanding shares or beneficial
    ownership of such entity.

2. License Grants and Conditions
--------------------------------

2.1. Grants

Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:

(a) under intellectual property rights (other than patent or trademark)
    Licensable by such Contributor to use, reproduce, make available,
    modify, display, perform, distribute, and otherwise exploit its
    Contributions, either on an unmodified basis, with Modifications, or
    as part of a Larger Work; and

(b) under Patent Claims of such Contributor to make, use, sell, offer
    for sale, have made, import, and otherwise transfer either its
    Contributions or its Contributor Version.

2.2. Effective Date

The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.

2.3. Limitations on Grant Scope

The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:

(a) for any code that a Contributor has removed from Covered Software;
    or

(b) for infringements caused by: (i) Your and any other third party's
    modifications of Covered Software, or (ii) the combination of its
    Contributions with other software (except as part of its Contributor
    Version); or

(c) under Patent Claims infringed by Covered Software in the absence of
    its Contributions.

This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).

2.4. Subsequent Licenses

No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).

2.5. Representation

Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.

2.6. Fair Use

This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.

2.7. Conditions

Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.

3. Responsibilities
-------------------

3.1. Distribution of Source Form

All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.

3.2. Distribution of Executable Form

If You distribute Covered Software in Executable Form then:

(a) such Covered Software must also be made available in Source Code
    Form, as described in Section 3.1, and You must inform recipients of
    the Executable Form how they can obtain a copy of such Source Code
    Form by reasonable means in a timely manner, at a charge no more
    than the cost of distribution to the recipient; and

(b) You may distribute such Executable Form under the terms of this
    License, or sublicense it under different terms, provided that the
    license for the Executable Form does not attempt to limit or alter
    the recipients' rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work

You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).

3.4. Notices

You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms

You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.

4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------

If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.

5. Termination
--------------

5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.

5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.

************************************************************************
*                                                                      *
*  6. Disclaimer of Warranty                                           *
*  -------------------------                                           *
*                                                                      *
*  Covered Software is provided under this License on an "as is"       *
*  basis, without warranty of any kind, either expressed, implied, or  *
*  statutory, including, without limitation, warranties that the       *
*  Covered Software is free of defects, merchantable, fit for a        *
*  particular purpose or non-infringing. The entire risk as to the     *
*  quality and performance of the Covered Software is with You.        *
*  Should any Covered Software prove defective in any respect, You     *
*  (not any Contributor) assume the cost of any necessary servicing,   *
*  repair, or correction. This disclaimer of warranty constitutes an   *
*  essential part of this License. No use of any Covered Software is   *
*  authorized under this License except under this disclaimer.         *
*                                                                      *
************************************************************************

************************************************************************
*                                                                      *
*  7. Limitation of Liability                                          *
*  --------------------------                                          *
*                                                                      *
*  Under no circumstances and under no legal theory, whether tort      *
*  (including negligence), contract, or otherwise, shall any           *
*  Contributor, or anyone who distributes Covered Software as          *
*  permitted above, be liable to You for any direct, indirect,         *
*  special, incidental, or consequential damages of any character      *
*  including, without limitation, damages for lost profits, loss of    *
*  goodwill, work stoppage, computer failure or malfunction, or any    *
*  and all other commercial damages or losses, even if such party      *
*  shall have been informed of the possibility of such damages. This   *
*  limitation of liability shall not apply to liability for death or   *
*  personal injury resulting from such party's negligence to the       *
*  extent applicable law prohibits such limitation. Some               *
*  jurisdictions do not allow the exclusion or limitation of           *
*  incidental or consequential damages, so this exclusion and          *
*  limitation may not apply to You.                                    *
*                                                                      *
************************************************************************

8. Litigation
-------------

Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.

9. Miscellaneous
----------------

This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.

10. Versions of the License
---------------------------

10.1. New Versions

Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.

10.2. Effect of New Versions

You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.

10.3. Modified Versions

If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses

If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.

Exhibit A - Source Code Form License Notice
-------------------------------------------

  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------

  This Source Code Form is "Incompatible With Secondary Licenses", as
  defined by the Mozilla Public License, v. 2.0.


================================================
FILE: examples/.gitignore
================================================
static/

================================================
FILE: examples/Cargo.toml
================================================
[workspace]
members = ["*"]
exclude = ["target", "static"]

[profile.release]
opt-level = 'z'
debug = false
lto = true
incremental = false
codegen-units = 1
strip = true


================================================
FILE: examples/password_login/Cargo.toml
================================================
[package]
name = "password_login"
version = "0.1.0"
edition = "2021"

[dependencies]
ricq = { path = "../../ricq" }
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7", features = ["codec"] }
futures-util = "0.3"
tracing = "0.1"
serde_json = "1"
tracing-subscriber = { version = "0.3", features = ["fmt", "local-time"] }
time = { version = "0.3", features = ["macros", "local-offset"] }
rand = "0.8"

================================================
FILE: examples/password_login/src/main.rs
================================================
use std::sync::Arc;
use std::time::Duration;

use futures_util::StreamExt;
use rand::prelude::StdRng;
use rand::SeedableRng;
use tokio_util::codec::{FramedRead, LinesCodec};
use tracing::Level;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

use ricq::client::qimei::get_qimei;
use ricq::client::{Connector as _, DefaultConnector};
use ricq::ext::common::after_login;
use ricq::handler::DefaultHandler;
use ricq::qsign::QSignClient;
use ricq::structs::ExtOnlineStatus;
use ricq::{Client, Device, Protocol};
use ricq::{LoginDeviceLocked, LoginNeedCaptcha, LoginResponse, LoginSuccess, LoginUnknownStatus};

#[tokio::main(flavor = "current_thread")]
async fn main() {
    tracing_subscriber::registry()
        .with(
            tracing_subscriber::fmt::layer()
                .with_target(true)
                .with_timer(tracing_subscriber::fmt::time::OffsetTime::new(
                    time::UtcOffset::__from_hms_unchecked(8, 0, 0),
                    time::macros::format_description!(
                        "[year repr:last_two]-[month]-[day] [hour]:[minute]:[second]"
                    ),
                )),
        )
        .with(
            tracing_subscriber::filter::Targets::new()
                .with_target("ricq", Level::DEBUG)
                .with_target("password_login", Level::DEBUG),
        )
        .init();

    // load uin and password from env
    let uin: i64 = std::env::var("UIN")
        .expect("failed to read UIN from env")
        .parse()
        .expect("failed to parse UIN");
    let password = std::env::var("PASSWORD").expect("failed to read PASSWORD from env");

    let mut rng = StdRng::seed_from_u64(uin as u64);
    let version = Protocol::AndroidPhone.into();
    let mut device = Device::random_with_rng(&mut rng);
    let qimei = get_qimei(&mut rng, &device, &version)
        .await
        .expect("failed to get qimei");
    device.set_qimei(qimei);

    let client = Arc::new(Client::new(
        device,
        version,
        Arc::new(
            QSignClient::new(
                String::from("http://localhost:8080"),
                String::from("114514"),
                Duration::from_secs(60),
            )
            .unwrap(),
        ),
        DefaultHandler,
    ));
    let handle = tokio::spawn({
        let client = client.clone();
        // 连接所有服务器,哪个最快用哪个,可以使用 TcpStream::connect 代替
        let stream = DefaultConnector.connect(&client).await.unwrap();
        async move { client.start(stream).await }
    });

    tokio::task::yield_now().await; // 等一下,确保连上了
    let mut resp = client
        .password_login(uin, &password)
        .await
        .expect("failed to login with password");
    loop {
        match resp {
            LoginResponse::Success(LoginSuccess {
                ref account_info, ..
            }) => {
                tracing::info!("login success: {:?}", account_info);
                break;
            }
            LoginResponse::DeviceLocked(LoginDeviceLocked {
                ref sms_phone,
                ref verify_url,
                ref message,
                ..
            }) => {
                tracing::info!("device locked: {:?}", message);
                tracing::info!("sms_phone: {:?}", sms_phone);
                tracing::info!("verify_url: {:?}", verify_url);
                tracing::info!("手机打开url,处理完成后重启程序");
                std::process::exit(0);
                //也可以走短信验证
                // resp = client.request_sms().await.expect("failed to request sms");
            }
            LoginResponse::NeedCaptcha(LoginNeedCaptcha {
                ref verify_url,
                // 图片应该没了
                image_captcha: ref _image_captcha,
                ..
            }) => {
                tracing::info!("滑块URL: {:?}", verify_url);
                tracing::info!("请输入ticket:");
                let mut reader = FramedRead::new(tokio::io::stdin(), LinesCodec::new());
                let ticket = reader
                    .next()
                    .await
                    .transpose()
                    .expect("failed to read ticket")
                    .expect("failed to read ticket");
                resp = client
                    .submit_ticket(&ticket)
                    .await
                    .expect("failed to submit ticket");
            }
            LoginResponse::DeviceLockLogin { .. } => {
                resp = client
                    .device_lock_login()
                    .await
                    .expect("failed to login with device lock");
            }
            LoginResponse::AccountFrozen => {
                panic!("account frozen");
            }
            LoginResponse::TooManySMSRequest => {
                panic!("too many sms request");
            }
            LoginResponse::UnknownStatus(LoginUnknownStatus {
                ref status,
                ref tlv_map,
                ref message,
            }) => {
                panic!(
                    "unknown login status: {:?}, {:?}, {:?}",
                    message, status, tlv_map
                );
            }
        }
    }
    tracing::info!("{:?}", resp);
    after_login(&client).await;
    {
        tracing::info!("{:?}", client.get_friend_list().await);
        tracing::info!("{:?}", client.get_group_list().await);
    }
    let d = client.get_allowed_clients().await;
    tracing::info!("{:?}", d);

    // 等一下,收到 ConfigPushSvc.PushReq 才可以发
    // use ricq::msg::MessageChain;
    // tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    // let img_bytes = tokio::fs::read("test.png").await.unwrap();
    // let group_image = client
    //     .upload_group_image(982166018, img_bytes)
    //     .await
    //     .unwrap();
    // let mut chain = MessageChain::default();
    // chain.push(group_image);
    // client.send_group_message(982166018, chain).await.ok();
    let aaa = client
        .update_online_status(ExtOnlineStatus::StudyOnline)
        .await;
    println!("{:?}", aaa);

    // client.delete_essence_message(1095020555, 8114, 2107692422).await
    // let mem_info = client.get_group_member_info(335783090, 875543543).await;
    // println!("{:?}", mem_info);
    // let mem_list = client.get_group_member_list(335783090).await;
    // println!("{:?}", mem_list);
    handle.await.unwrap();
}


================================================
FILE: examples/qrcode_login/Cargo.toml
================================================
[package]
name = "qrcode_login"
version = "0.1.0"
edition = "2021"

[dependencies]
ricq = { path = "../../ricq" }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
bytes = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["fmt"] }


================================================
FILE: examples/qrcode_login/src/main.rs
================================================
use std::path::Path;
use std::sync::Arc;

use bytes::Bytes;
use tokio::time::{sleep, Duration};
use tracing::Level;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

use ricq::client::{Connector as _, DefaultConnector};
use ricq::ext::common::after_login;
use ricq::handler::DefaultHandler;
use ricq::qsign::QSignClient;
use ricq::{Client, Device, Protocol};
use ricq::{LoginResponse, QRCodeConfirmed, QRCodeImageFetch, QRCodeState};

#[tokio::main(flavor = "current_thread")]
async fn main() {
    tracing_subscriber::registry()
        .with(tracing_subscriber::fmt::layer().with_target(true))
        .with(
            tracing_subscriber::filter::Targets::new()
                .with_target("ricq", Level::DEBUG)
                .with_target("qrcode_login", Level::DEBUG),
        )
        .init();

    let device = match Path::new("device.json").exists() {
        true => serde_json::from_str(
            &tokio::fs::read_to_string("device.json")
                .await
                .expect("failed to read device.json"),
        )
        .expect("failed to parse device info"),
        false => {
            let d = Device::random();
            tokio::fs::write("device.json", serde_json::to_string(&d).unwrap())
                .await
                .expect("failed to write device info to file");
            d
        }
    };

    let client = Arc::new(Client::new(
        device,
        Protocol::AndroidWatch.into(),
        Arc::new(
            QSignClient::new(
                String::from("http://localhost:8080"),
                String::from("114514"),
                Duration::from_secs(60),
            )
            .unwrap(),
        ),
        DefaultHandler,
    ));
    let handle = tokio::spawn({
        let client = client.clone();
        let stream = DefaultConnector.connect(&client).await.unwrap();
        async move { client.start(stream).await }
    });
    tokio::task::yield_now().await; // 等一下,确保连上了
    let mut resp = client.fetch_qrcode().await.expect("failed to fetch qrcode");

    // // vvv 如果不关心二维码状态,可以用这个替换下面的 vvv
    // use ricq::ext::login::auto_query_qrcode;
    // match resp {
    //     QRCodeState::QRCodeImageFetch {
    //         ref image_data,
    //         ref sig,
    //     } => {
    //         tokio::fs::write("qrcode.png", &image_data).await.expect("failed to write file");
    //         if let Err(err) = auto_query_qrcode(&client, sig).await {
    //             panic!("登录失败 {}", err)
    //         };
    //     }
    //     _ => {
    //         panic!("resp error")
    //     }
    // }
    // // ^^^ 如果不关心二维码状态,可以用这个替换下面的 ^^^

    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

    // vvv 如果关心二维码状态,可以用这个 vvv
    let mut image_sig = Bytes::new();
    loop {
        match resp {
            QRCodeState::ImageFetch(QRCodeImageFetch {
                ref image_data,
                ref sig,
            }) => {
                tokio::fs::write("qrcode.png", &image_data)
                    .await
                    .expect("failed to write file");
                image_sig = sig.clone();
                tracing::info!("二维码: qrcode.png");
            }
            QRCodeState::WaitingForScan => {
                tracing::info!("二维码待扫描")
            }
            QRCodeState::WaitingForConfirm => {
                tracing::info!("二维码待确认")
            }
            QRCodeState::Timeout => {
                tracing::info!("二维码已超时,重新获取");
                if let QRCodeState::ImageFetch(QRCodeImageFetch {
                    ref image_data,
                    ref sig,
                }) = client.fetch_qrcode().await.expect("failed to fetch qrcode")
                {
                    tokio::fs::write("qrcode.png", &image_data)
                        .await
                        .expect("failed to write file");
                    image_sig = sig.clone();
                    tracing::info!("二维码: qrcode.png");
                }
            }
            QRCodeState::Confirmed(QRCodeConfirmed {
                ref tmp_pwd,
                ref tmp_no_pic_sig,
                ref tgt_qr,
                ..
            }) => {
                tracing::info!("二维码已确认");
                let mut login_resp = client
                    .qrcode_login(tmp_pwd, tmp_no_pic_sig, tgt_qr)
                    .await
                    .expect("failed to qrcode login");
                if let LoginResponse::DeviceLockLogin { .. } = login_resp {
                    login_resp = client
                        .device_lock_login()
                        .await
                        .expect("failed to device lock login");
                }
                tracing::info!("{:?}", login_resp);
                break;
            }
            QRCodeState::Canceled => {
                panic!("二维码已取消")
            }
        }
        sleep(Duration::from_secs(5)).await;
        resp = client
            .query_qrcode_result(&image_sig)
            .await
            .expect("failed to query qrcode result");
    }
    // ^^^ 如果不关心二维码状态,可以用这个 ^^^

    after_login(&client).await;
    {
        tracing::info!("{:?}", client.get_friend_list().await);
        tracing::info!("{:?}", client.get_group_list().await);
    }

    handle.await.unwrap();
}


================================================
FILE: examples/ricq-axum-api/Cargo.toml
================================================
[package]
name = "ricq-axum-api"
version = "0.1.0"
edition = "2021"
description = "ricq axum api"
license = "AGPL-3.0"
homepage = "https://github.com/lz1998/ricq"
repository = "https://github.com/lz1998/ricq"
readme = "README.md"
keywords = ["ricq", "axum", "http", "api"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tokio = { version = "1", features = ["full"] }
ricq = { path = "../../ricq" }
dashmap = "5.2"
bytes = "1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
axum = { version = "0.5", features = ["json"] }
async-trait = "0.1"
rand = "0.8"
tracing = "0.1"
base64 = "0.13"
tower = { version = "0.4" }
tower-http = { version = "0.3", features = ["fs"] }
tracing-subscriber = { version = "0.3", features = ["fmt", "local-time"] }
time = { version = "0.3", features = ["macros", "local-offset"] }

================================================
FILE: examples/ricq-axum-api/README.md
================================================
# ricq-axum-api

1. 下载 UI 文件,放在工作目录 static 文件夹

```bash
 wget https://github.com/lz1998/ricq-react-ui/releases/latest/download/static.zip && unzip static.zip && rm static.zip
```

2. 实现 Processor trait(仅包含登录后处理逻辑)

> 参考 [processor.rs](https://github.com/lz1998/ricq/blob/master/examples/ricq-axum-api/src/processor.rs#L28)

```rust
#[async_trait::async_trait]
pub trait Processor {
    async fn on_login_success(
        &self,
        client: Arc<Client>,
        event_receiver: broadcast::Receiver<QEvent>,
        credential: Credential,
        network_join_handle: JoinHandle<()>,
    );
    async fn list_client(&self) -> Vec<ClientInfo>;
    async fn delete_client(&self, uin: i64, protocol: u8);
} 
```

3. 创建 RicqAxumApi,并启动 axum 服务器

```rust
#![feature(async_closure)]

use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;

use axum::{
    routing::{get, get_service, post},
    Extension, Router,
};
use dashmap::DashMap;
use tower::ServiceBuilder;
use tower_http::services::ServeDir;

use ricq::Client;
use ricq_axum_api::handler::{bot, password, qrcode};
use ricq_axum_api::RicqAxumApi;

type ClientProcessor = DashMap<(i64, u8), Arc<Client>>;

#[tokio::main]
async fn main() {
    // 默认处理器,登录后什么也不做,仅作为容器
    let processor = ClientProcessor::default();
    let ricq_axum_api = Arc::new(RicqAxumApi::new(processor));

    let app = Router::new()
        .route("/ping", get(async move || "pong"))
        .nest(
            "/login",
            Router::new()
                .nest(
                    "/qrcode",
                    Router::new()
                        .route("/create", post(qrcode::create))
                        .route("/list", get(qrcode::list))
                        .route("/delete", post(qrcode::delete))
                        .route("/query", post(qrcode::query)),
                )
                .nest(
                    "/password",
                    Router::new()
                        .route("/create", post(password::login))
                        .route("/request_sms", post(password::request_sms))
                        .route("/submit_sms", post(password::submit_sms))
                        .route("/submit_ticket", post(password::submit_ticket))
                        .route("/list", get(password::list))
                        .route("/delete", post(password::delete)),
                ),
        )
        .nest(
            "/bot",
            Router::new()
                .route("/list", get(bot::list))
                .route("/delete", post(bot::delete)),
        )
        .fallback(get_service(ServeDir::new("static")).handle_error(handle_error))
        .layer(
            ServiceBuilder::new()
                .layer(Extension(ricq_axum_api))
                .into_inner(),
        );
    let addr = SocketAddr::from_str("0.0.0.0:9000").expect("failed to parse bind_addr");
    println!("listening on {}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn handle_error(_: std::io::Error) -> impl axum::response::IntoResponse {
    (axum::http::StatusCode::NOT_FOUND, "Something went wrong...")
}
```

4. 访问 `http://localhost:9000`


================================================
FILE: examples/ricq-axum-api/src/bin/main.rs
================================================
#![feature(async_closure)]

use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;

use axum::{
    routing::{get, get_service, post},
    Extension, Router,
};
use dashmap::DashMap;
use tokio::sync::broadcast;
use tokio::task::JoinHandle;
use tower::ServiceBuilder;
use tower_http::services::ServeDir;
use tracing::Level;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

use ricq::client::event::{FriendMessageEvent, GroupMessageEvent};
use ricq::client::{DefaultConnector, NetworkStatus};
use ricq::ext::common::after_login;
use ricq::ext::reconnect::{auto_reconnect, Credential};
use ricq::handler::QEvent;
use ricq::Client;
use ricq_axum_api::handler::{bot, password, qrcode};
use ricq_axum_api::processor::Processor;
use ricq_axum_api::u8_protocol::U8Protocol;
use ricq_axum_api::{ClientInfo, RicqAxumApi};

// 默认处理器
struct ClientProcessor(DashMap<(i64, u8), Arc<Client>>);

#[async_trait::async_trait]
impl Processor for ClientProcessor {
    async fn on_login_success(
        &self,
        client: Arc<Client>,
        mut event_receiver: broadcast::Receiver<QEvent>,
        credential: Credential,
        network_join_handle: JoinHandle<()>,
    ) {
        let uin = client.uin().await;
        let protocol = client.version().await.protocol.to_u8();
        self.0.insert((uin, protocol), client.clone());
        after_login(&client).await;

        tokio::spawn(async move {
            while let Ok(event) = event_receiver.recv().await {
                match event {
                    QEvent::GroupMessage(e) => {
                        let GroupMessageEvent {
                            inner: message,
                            client,
                        } = e;
                        tracing::info!(
                            "GROUP_MSG, code: {}, content: {}",
                            message.group_code,
                            message.elements.to_string()
                        );
                        client
                            .send_group_message(message.group_code, message.elements)
                            .await
                            .ok();
                    }
                    QEvent::FriendMessage(e) => {
                        let FriendMessageEvent {
                            inner: message,
                            client,
                        } = e;
                        tracing::info!(
                            "FRIEND_MSG, code: {}, content: {}",
                            message.from_uin,
                            message.elements.to_string()
                        );
                        client
                            .send_friend_message(message.from_uin, message.elements)
                            .await
                            .ok();
                    }
                    other => {
                        tracing::info!("{:?}", other)
                    }
                }
            }
        });

        // DONT BLOCK
        tokio::spawn(async move {
            network_join_handle.await.ok();
            auto_reconnect(
                client,
                credential,
                Duration::from_secs(10),
                10,
                DefaultConnector,
            )
            .await;
        });
    }

    async fn list_client(&self) -> Vec<ClientInfo> {
        let mut infos = Vec::new();
        for cli in self.0.iter() {
            let (uin, protocol) = cli.key();
            let client = cli.value();
            infos.push(ClientInfo {
                uin: *uin,
                nick: client.account_info.read().await.nickname.clone(),
                status: client.get_status(),
                protocol: *protocol,
            });
        }
        infos
    }

    async fn delete_client(&self, uin: i64, protocol: u8) {
        if let Some((_, client)) = self.0.remove(&(uin, protocol)) {
            client.stop(NetworkStatus::Stop);
        }
    }
}

#[tokio::main]
async fn main() {
    // 初始化日志
    tracing_subscriber::registry()
        .with(
            tracing_subscriber::fmt::layer()
                .with_target(true)
                .with_timer(tracing_subscriber::fmt::time::OffsetTime::new(
                    time::UtcOffset::__from_hms_unchecked(8, 0, 0),
                    time::macros::format_description!(
                        "[year repr:last_two]-[month]-[day] [hour]:[minute]:[second]"
                    ),
                )),
        )
        .with(
            tracing_subscriber::filter::Targets::new()
                .with_target("main", Level::DEBUG)
                .with_target("ricq", Level::DEBUG)
                .with_target("ricq_axum_api", Level::DEBUG),
        )
        .init();

    let processor = ClientProcessor(Default::default());
    let ricq_axum_api = Arc::new(RicqAxumApi::new(processor));

    let app = Router::new()
        .route("/ping", get(async move || "pong"))
        .nest(
            "/login",
            Router::new()
                .nest(
                    "/qrcode",
                    Router::new()
                        .route("/create", post(qrcode::create::<ClientProcessor>))
                        .route("/list", get(qrcode::list::<ClientProcessor>))
                        .route("/delete", post(qrcode::delete::<ClientProcessor>))
                        .route("/query", post(qrcode::query::<ClientProcessor>)),
                )
                .nest(
                    "/password",
                    Router::new()
                        .route("/create", post(password::login::<ClientProcessor>))
                        .route(
                            "/request_sms",
                            post(password::request_sms::<ClientProcessor>),
                        )
                        .route("/submit_sms", post(password::submit_sms::<ClientProcessor>))
                        .route(
                            "/submit_ticket",
                            post(password::submit_ticket::<ClientProcessor>),
                        )
                        .route("/list", get(password::list::<ClientProcessor>))
                        .route("/delete", post(password::delete::<ClientProcessor>)),
                ),
        )
        .nest(
            "/bot",
            Router::new()
                .route("/list", get(bot::list::<ClientProcessor>))
                .route("/delete", post(bot::delete::<ClientProcessor>)),
        )
        .fallback(get_service(ServeDir::new("static")).handle_error(handle_error))
        .layer(
            ServiceBuilder::new()
                .layer(Extension(ricq_axum_api))
                .into_inner(),
        );
    let addr = SocketAddr::from_str("0.0.0.0:9000").expect("failed to parse bind_addr");
    println!("listening on {}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn handle_error(_: std::io::Error) -> impl axum::response::IntoResponse {
    (axum::http::StatusCode::NOT_FOUND, "Something went wrong...")
}


================================================
FILE: examples/ricq-axum-api/src/handler/bot.rs
================================================
use std::sync::Arc;

use axum::http::StatusCode;
use axum::{Extension, Json};
use serde::{Deserialize, Serialize};

use crate::processor::Processor;
use crate::{ClientInfo, RicqAxumApi};

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ListBotResp {
    pub bots: Vec<ClientInfo>,
}

pub async fn list<P: Processor>(
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<ListBotResp>, StatusCode> {
    Ok(Json(ListBotResp {
        bots: ricq_axum_api.processor.list_client().await,
    }))
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct DeleteBotReq {
    uin: i64,
    protocol: u8,
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct DeleteBotResp {}

pub async fn delete<P: Processor>(
    Json(req): Json<DeleteBotReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<DeleteBotResp>, StatusCode> {
    ricq_axum_api
        .processor
        .delete_client(req.uin, req.protocol)
        .await;
    Ok(Json(DeleteBotResp {}))
}


================================================
FILE: examples/ricq-axum-api/src/handler/mod.rs
================================================
pub mod bot;
pub mod password;
pub mod qrcode;


================================================
FILE: examples/ricq-axum-api/src/handler/password.rs
================================================
use std::sync::Arc;
use std::time::Duration;

use axum::http::StatusCode;
use axum::{Extension, Json};
use rand::{prelude::StdRng, SeedableRng};
use serde::{Deserialize, Serialize};

use ricq::client::{Connector as _, DefaultConnector, NetworkStatus};
use ricq::ext::reconnect::{Credential, Password};
use ricq::qsign::QSignClient;
use ricq::version::get_version;
use ricq::{Client, Device, LoginDeviceLocked, LoginNeedCaptcha, LoginResponse, Protocol};

use crate::processor::Processor;
use crate::u8_protocol::U8Protocol;
use crate::{PasswordClient, RicqAxumApi};

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct CreateClientReq {
    pub uin: i64,
    pub protocol: u8,
    pub password: String,
    pub device_seed: Option<u64>,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SubmitTicketReq {
    pub uin: i64,
    pub protocol: u8,
    pub ticket: String,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct RequestSmsReq {
    pub uin: i64,
    pub protocol: u8,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SubmitSmsReq {
    pub uin: i64,
    pub protocol: u8,
    pub sms: String,
}

#[derive(Default, Debug, Clone, Serialize)]
pub struct PasswordLoginResp {
    pub state: String,
    pub captcha_url: Option<String>,
    pub verify_url: Option<String>,
    pub sms_phone: Option<String>,
    pub message: Option<String>,
}

impl From<LoginResponse> for PasswordLoginResp {
    fn from(login_response: LoginResponse) -> Self {
        let mut resp = PasswordLoginResp::default();
        match login_response {
            LoginResponse::Success(_) => {
                resp.state = "success".into();
            }
            LoginResponse::NeedCaptcha(LoginNeedCaptcha { ref verify_url, .. }) => {
                resp.state = "need_captcha".into();
                resp.captcha_url = verify_url.clone();
            }
            LoginResponse::AccountFrozen => {
                resp.state = "account_frozen".into();
            }
            LoginResponse::DeviceLocked(LoginDeviceLocked {
                ref verify_url,
                ref message,
                ref sms_phone,
                ..
            }) => {
                resp.state = "device_locked".into();
                resp.verify_url = verify_url.clone();
                resp.sms_phone = sms_phone.clone();
                resp.message = message.clone();
            }
            LoginResponse::TooManySMSRequest => {
                resp.state = "too_many_sms_request".into();
            }
            LoginResponse::DeviceLockLogin(_) => {
                resp.state = "device_lock_login".into();
            }
            LoginResponse::UnknownStatus(status) => {
                resp.state = "unknown".into();
                resp.message = Some(format!(
                    "status: {} message: {}",
                    status.status, status.message
                ));
            }
        };
        resp
    }
}

pub async fn login<P: Processor>(
    Json(req): Json<CreateClientReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<PasswordLoginResp>, StatusCode> {
    let mut rand_seed = req.device_seed.unwrap_or(req.uin as u64);
    if rand_seed == 0 {
        rand_seed = req.uin as u64;
    }
    let device = Device::random_with_rng(&mut StdRng::seed_from_u64(rand_seed));
    let protocol = Protocol::from_u8(req.protocol);
    let (sender, receiver) = tokio::sync::broadcast::channel(10);
    let cli = Arc::new(Client::new(
        device,
        get_version(protocol.clone()),
        Arc::new(
            QSignClient::new(
                String::from("http://localhost:8080"),
                String::from("114514"),
                Duration::from_secs(60),
            )
            .unwrap(),
        ),
        sender,
    ));
    let connector = DefaultConnector;
    let stream = connector
        .connect(&cli)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    let c = cli.clone();
    let network_join_handle = tokio::spawn(async move { c.start(stream).await });
    tokio::task::yield_now().await;
    let mut resp = cli
        .password_login(req.uin, &req.password)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    if let LoginResponse::DeviceLockLogin(_) = resp {
        resp = cli
            .device_lock_login()
            .await
            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    }
    let credential = Credential::Password(Password {
        uin: req.uin,
        password: req.password,
    });
    if let LoginResponse::Success(_) = resp {
        tracing::info!("login success: {} {:?}", req.uin, req.protocol);
        ricq_axum_api
            .processor
            .on_login_success(cli, receiver, credential, network_join_handle)
            .await;
    } else if let Some(old) = ricq_axum_api.password_clients.insert(
        (req.uin, protocol.to_u8()),
        PasswordClient {
            client: cli,
            login_response: resp.clone(),
            event_receiver: receiver,
            network_join_handle,
            credential,
        },
    ) {
        old.client.stop(NetworkStatus::Stop);
    }
    Ok(Json(PasswordLoginResp::from(resp)))
}

pub async fn submit_ticket<P: Processor>(
    Json(req): Json<SubmitTicketReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<PasswordLoginResp>, StatusCode> {
    let mut resp = ricq_axum_api
        .password_clients
        .get(&(req.uin, req.protocol))
        .ok_or(StatusCode::BAD_REQUEST)?
        .client
        .submit_ticket(&req.ticket)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    if let LoginResponse::DeviceLockLogin(_) = resp {
        resp = ricq_axum_api
            .password_clients
            .get(&(req.uin, req.protocol))
            .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
            .client
            .device_lock_login()
            .await
            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    }
    if let LoginResponse::Success(_) = resp {
        if let Some(((uin, protocol), client)) = ricq_axum_api
            .password_clients
            .remove(&(req.uin, req.protocol))
        {
            tracing::info!("login success: {} {:?}", uin, Protocol::from_u8(protocol));
            ricq_axum_api
                .processor
                .on_login_success(
                    client.client,
                    client.event_receiver,
                    client.credential,
                    client.network_join_handle,
                )
                .await;
        } else {
            tracing::warn!("failed to remove client: {}", req.uin);
        }
    } else {
        ricq_axum_api
            .password_clients
            .get_mut(&(req.uin, req.protocol))
            .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
            .login_response = resp.clone();
    }
    Ok(Json(PasswordLoginResp::from(resp)))
}

pub async fn request_sms<P: Processor>(
    Json(req): Json<RequestSmsReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<PasswordLoginResp>, StatusCode> {
    let resp = ricq_axum_api
        .password_clients
        .get(&(req.uin, req.protocol))
        .ok_or(StatusCode::BAD_REQUEST)?
        .client
        .request_sms()
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    ricq_axum_api
        .password_clients
        .get_mut(&(req.uin, req.protocol))
        .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
        .login_response = resp.clone();
    Ok(Json(PasswordLoginResp::from(resp)))
}

pub async fn submit_sms<P: Processor>(
    Json(req): Json<SubmitSmsReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<PasswordLoginResp>, StatusCode> {
    let mut resp = ricq_axum_api
        .password_clients
        .get(&(req.uin, req.protocol))
        .ok_or(StatusCode::BAD_REQUEST)?
        .client
        .submit_sms_code(&req.sms)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    if let LoginResponse::DeviceLockLogin(_) = resp {
        resp = ricq_axum_api
            .password_clients
            .get(&(req.uin, req.protocol))
            .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
            .client
            .device_lock_login()
            .await
            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    }
    if let LoginResponse::Success(_) = resp {
        let cli = ricq_axum_api
            .password_clients
            .remove(&(req.uin, req.protocol));
        if let Some(((uin, protocol), client)) = cli {
            tracing::info!("login success: {} {:?}", uin, Protocol::from_u8(protocol));
            ricq_axum_api
                .processor
                .on_login_success(
                    client.client,
                    client.event_receiver,
                    client.credential,
                    client.network_join_handle,
                )
                .await;
        } else {
            tracing::warn!("failed to remove client: {}", req.uin);
        }
    } else {
        ricq_axum_api
            .password_clients
            .get_mut(&(req.uin, req.protocol))
            .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
            .login_response = resp.clone();
    }
    Ok(Json(PasswordLoginResp::from(resp)))
}

#[derive(Default, Serialize)]
pub struct ListClientResp {
    pub clients: Vec<ListClientRespClient>,
}

#[derive(Default, Serialize)]
pub struct ListClientRespClient {
    pub uin: i64,
    pub protocol: u8,
    pub resp: PasswordLoginResp,
}

pub async fn list<P: Processor>(
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<ListClientResp>, StatusCode> {
    let mut clients = Vec::new();
    for c in ricq_axum_api.password_clients.iter() {
        clients.push(ListClientRespClient {
            uin: c.key().0,
            protocol: c.client.version().await.protocol.to_u8(),
            resp: PasswordLoginResp::from(c.login_response.clone()),
        })
    }
    Ok(Json(ListClientResp { clients }))
}

#[derive(Default, Serialize, Deserialize)]
pub struct DeleteClientReq {
    pub uin: i64,
    pub protocol: u8,
}

#[derive(Default, Serialize, Deserialize)]
pub struct DeleteClientResp {}

pub async fn delete<P: Processor>(
    Json(req): Json<DeleteClientReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<DeleteClientResp>, StatusCode> {
    if let Some((_, cli)) = ricq_axum_api
        .password_clients
        .remove(&(req.uin, req.protocol))
    {
        cli.client.stop(NetworkStatus::Stop);
    }
    Ok(Json(DeleteClientResp {}))
}


================================================
FILE: examples/ricq-axum-api/src/handler/qrcode.rs
================================================
use std::sync::Arc;
use std::time::Duration;

use axum::http::StatusCode;
use axum::{Extension, Json};
use bytes::Bytes;
use rand::{prelude::StdRng, SeedableRng};
use serde::{Deserialize, Serialize};

use ricq::client::NetworkStatus;
use ricq::client::{Connector as _, DefaultConnector};
use ricq::device::Device;
use ricq::ext::reconnect::Credential;
use ricq::qsign::QSignClient;
use ricq::version::{get_version, Protocol};
use ricq::{Client, LoginResponse, QRCodeState};

use crate::processor::Processor;
use crate::u8_protocol::U8Protocol;
use crate::QRCodeClient;
use crate::RicqAxumApi;

mod base64 {
    extern crate base64;

    use serde::{de, Deserialize, Deserializer, Serializer};

    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&base64::encode(bytes))
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = <&str>::deserialize(deserializer)?;
        base64::decode(s).map_err(de::Error::custom)
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CreateClientReq {
    pub device_seed: Option<u64>,
    pub protocol: u8,
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CreateClientResp {
    #[serde(with = "base64")]
    pub sig: Vec<u8>,
    #[serde(with = "base64")]
    pub image: Vec<u8>,
}

pub async fn create<P: Processor>(
    Json(req): Json<CreateClientReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<CreateClientResp>, StatusCode> {
    let rand_seed = req.device_seed.unwrap_or_else(rand::random);
    let device = Device::random_with_rng(&mut StdRng::seed_from_u64(rand_seed));
    let protocol = match Protocol::from_u8(req.protocol) {
        Protocol::MacOS => Protocol::MacOS,
        Protocol::AndroidWatch => Protocol::AndroidWatch,
        _ => return Err(StatusCode::BAD_REQUEST),
    };
    let (sender, receiver) = tokio::sync::broadcast::channel(10);
    let cli = Arc::new(Client::new(
        device,
        get_version(protocol),
        Arc::new(
            QSignClient::new(
                String::from("http://localhost:8080"),
                String::from("114514"),
                Duration::from_secs(60),
            )
            .unwrap(),
        ),
        sender,
    ));
    let connector = DefaultConnector;
    let stream = connector
        .connect(&cli)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    let c = cli.clone();
    let network_join_handle = tokio::spawn(async move { c.start(stream).await });
    tokio::task::yield_now().await;
    let resp = cli
        .fetch_qrcode()
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    if let QRCodeState::ImageFetch(image_fetch) = resp {
        ricq_axum_api.qrcode_clients.insert(
            image_fetch.sig.clone(),
            QRCodeClient {
                sig: image_fetch.sig.to_vec(),
                image: image_fetch.image_data.to_vec(),
                state: QRCodeState::ImageFetch(image_fetch.clone()),
                client: cli,
                event_receiver: receiver,
                network_join_handle,
            },
        );
        Ok(Json(CreateClientResp {
            sig: image_fetch.sig.to_vec(),
            image: image_fetch.image_data.to_vec(),
        }))
    } else {
        Err(StatusCode::INTERNAL_SERVER_ERROR)
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct QueryQRCodeReq {
    #[serde(with = "base64")]
    pub sig: Vec<u8>,
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct QueryQRCodeResp {
    pub state: String,
}

pub async fn query<P: Processor>(
    Json(req): Json<QueryQRCodeReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<QueryQRCodeResp>, StatusCode> {
    let sig = Bytes::from(req.sig);

    let resp = ricq_axum_api
        .qrcode_clients
        .get(&sig)
        .ok_or(StatusCode::BAD_REQUEST)?
        .client
        .query_qrcode_result(&sig)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    let state = match resp {
        QRCodeState::ImageFetch(_) => "image_fetch",
        QRCodeState::WaitingForScan => "waiting_for_scan",
        QRCodeState::WaitingForConfirm => "waiting_for_confirm",
        QRCodeState::Timeout => "timeout",
        QRCodeState::Confirmed(_) => "confirmed",
        QRCodeState::Canceled => "canceled",
    }
    .to_string();
    ricq_axum_api
        .qrcode_clients
        .get_mut(&sig)
        .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
        .state = resp.clone();
    if let QRCodeState::Confirmed(confirmed) = resp {
        let (_, cli) = ricq_axum_api.qrcode_clients.remove(&sig).unwrap();
        let mut resp = cli
            .client
            .qrcode_login(
                &confirmed.tmp_pwd,
                &confirmed.tmp_no_pic_sig,
                &confirmed.tgt_qr,
            )
            .await
            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

        if let LoginResponse::DeviceLockLogin(_) = resp {
            resp = cli
                .client
                .device_lock_login()
                .await
                .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
        }
        if let LoginResponse::Success(_) = resp {
            let uin = cli.client.uin().await;
            let credential = Credential::Token(cli.client.gen_token().await);
            tracing::info!("login success: {}", uin);
            ricq_axum_api
                .processor
                .on_login_success(
                    cli.client,
                    cli.event_receiver,
                    credential,
                    cli.network_join_handle,
                )
                .await;
        }
    }
    Ok(Json(QueryQRCodeResp { state }))
}

#[derive(Default, Serialize)]
pub struct ListClientResp {
    pub clients: Vec<ListClientRespClient>,
}

#[derive(Default, Serialize)]
pub struct ListClientRespClient {
    #[serde(with = "base64")]
    pub sig: Vec<u8>,
    #[serde(with = "base64")]
    pub image: Vec<u8>,
    pub protocol: u8,
    pub state: String,
}

pub async fn list<P: Processor>(
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<ListClientResp>, StatusCode> {
    let mut clients = Vec::new();
    for c in ricq_axum_api.qrcode_clients.iter() {
        clients.push(ListClientRespClient {
            sig: c.sig.to_vec(),
            image: c.image.clone(),
            protocol: c.client.version().await.protocol.to_u8(),
            state: match c.state {
                QRCodeState::ImageFetch(_) => "image_fetch",
                QRCodeState::WaitingForScan => "waiting_for_scan",
                QRCodeState::WaitingForConfirm => "waiting_for_confirm",
                QRCodeState::Timeout => "timeout",
                QRCodeState::Confirmed(_) => "confirmed",
                QRCodeState::Canceled => "canceled",
            }
            .into(),
        })
    }
    Ok(Json(ListClientResp { clients }))
}

#[derive(Default, Serialize, Deserialize)]
pub struct DeleteClientReq {
    #[serde(with = "base64")]
    pub sig: Vec<u8>,
}

#[derive(Default, Serialize, Deserialize)]
pub struct DeleteClientResp {}

pub async fn delete<P: Processor>(
    Json(req): Json<DeleteClientReq>,
    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,
) -> Result<Json<DeleteClientResp>, StatusCode> {
    if let Some((_, cli)) = ricq_axum_api.qrcode_clients.remove(&Bytes::from(req.sig)) {
        cli.client.stop(NetworkStatus::Stop);
    }
    Ok(Json(DeleteClientResp {}))
}


================================================
FILE: examples/ricq-axum-api/src/lib.rs
================================================
use bytes::Bytes;
use dashmap::DashMap;
use ricq::ext::reconnect::Credential;
use ricq::handler::QEvent;
use ricq::{Client, LoginResponse, QRCodeState};
use std::sync::Arc;
use tokio::sync::broadcast;
use tokio::task::JoinHandle;

pub mod handler;
pub mod processor;
pub mod u8_protocol;
use serde::{Deserialize, Serialize};

use crate::processor::Processor;

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ClientInfo {
    pub uin: i64,
    pub nick: String,
    pub status: u8,
    pub protocol: u8,
}

pub struct PasswordClient {
    pub client: Arc<Client>,
    pub login_response: LoginResponse,
    pub event_receiver: broadcast::Receiver<QEvent>,
    pub network_join_handle: JoinHandle<()>,
    pub credential: Credential,
}

pub struct QRCodeClient {
    pub sig: Vec<u8>,
    pub image: Vec<u8>,
    pub state: QRCodeState,
    pub client: Arc<Client>,
    pub event_receiver: broadcast::Receiver<QEvent>,
    pub network_join_handle: JoinHandle<()>,
}

pub struct RicqAxumApi<P: Processor> {
    // key: uin+protocol
    password_clients: DashMap<(i64, u8), PasswordClient>,

    // key: sig
    qrcode_clients: DashMap<Bytes, QRCodeClient>,

    // 仅负责登录后的逻辑
    processor: P,
}

impl<P: Processor> RicqAxumApi<P> {
    pub fn new(processor: P) -> Self {
        Self {
            password_clients: Default::default(),
            qrcode_clients: Default::default(),
            processor,
        }
    }
}


================================================
FILE: examples/ricq-axum-api/src/processor.rs
================================================
use std::sync::Arc;

use tokio::sync::broadcast;
use tokio::task::JoinHandle;

use ricq::{ext::reconnect::Credential, handler::QEvent, Client};

use crate::ClientInfo;

#[async_trait::async_trait]
pub trait Processor {
    async fn on_login_success(
        &self,
        client: Arc<Client>,
        event_receiver: broadcast::Receiver<QEvent>,
        credential: Credential,
        network_join_handle: JoinHandle<()>,
    );
    async fn list_client(&self) -> Vec<ClientInfo>;
    async fn delete_client(&self, uin: i64, protocol: u8);
}


================================================
FILE: examples/ricq-axum-api/src/u8_protocol.rs
================================================
use ricq::version::Protocol;

pub trait U8Protocol {
    fn to_u8(&self) -> u8;
    fn from_u8(n: u8) -> Self;
}

impl U8Protocol for Protocol {
    fn to_u8(&self) -> u8 {
        match self {
            Protocol::AndroidPhone => 1,
            Protocol::AndroidWatch => 2,
            Protocol::MacOS => 3,
            Protocol::QiDian => 4,
            Protocol::IPad => 5,
            Protocol::AndroidPad => 6,
        }
    }

    fn from_u8(n: u8) -> Self {
        match n {
            1 => Protocol::AndroidPhone,
            2 => Protocol::AndroidWatch,
            3 => Protocol::MacOS,
            4 => Protocol::QiDian,
            5 => Protocol::IPad,
            _ => Protocol::IPad,
        }
    }
}


================================================
FILE: examples/token_login/Cargo.toml
================================================
[package]
name = "token_login"
version = "0.1.0"
edition = "2021"

[dependencies]
ricq = { path = "../../ricq" }
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
serde_json = "1"
tracing-subscriber = { version = "0.3", features = ["fmt", "local-time"] }
time = { version = "0.3", features = ["macros", "local-offset"] }
rand = "0.8"


================================================
FILE: examples/token_login/src/main.rs
================================================
use std::sync::Arc;
use std::time::Duration;

use rand::prelude::StdRng;
use rand::SeedableRng;
use tracing::Level;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

use ricq::client::{Connector as _, DefaultConnector, Token};
use ricq::ext::common::after_login;
use ricq::handler::DefaultHandler;
use ricq::qsign::QSignClient;
use ricq::{Client, Device, Protocol};

#[tokio::main(flavor = "current_thread")]
async fn main() {
    tracing_subscriber::registry()
        .with(
            tracing_subscriber::fmt::layer()
                .with_target(true)
                .with_timer(tracing_subscriber::fmt::time::OffsetTime::new(
                    time::UtcOffset::__from_hms_unchecked(8, 0, 0),
                    time::macros::format_description!(
                        "[year repr:last_two]-[month]-[day] [hour]:[minute]:[second]"
                    ),
                )),
        )
        .with(
            tracing_subscriber::filter::Targets::new()
                .with_target("ricq", Level::DEBUG)
                .with_target("token_login", Level::DEBUG),
        )
        .init();

    let token = tokio::fs::read_to_string("session.token")
        .await
        .expect("failed to read token");
    let token: Token = serde_json::from_str(&token).expect("failed to parse token");
    let device = Device::random_with_rng(&mut StdRng::seed_from_u64(token.uin as u64));

    let client = Arc::new(Client::new(
        device,
        Protocol::IPad.into(),
        Arc::new(
            QSignClient::new(
                String::from("http://localhost:8080"),
                String::from("114514"),
                Duration::from_secs(60),
            )
            .unwrap(),
        ),
        DefaultHandler,
    ));

    let handle = tokio::spawn({
        let client = client.clone();
        let stream = DefaultConnector.connect(&client).await.unwrap();
        async move { client.start(stream).await }
    });
    // 直接使用 TcpStream,目前不推荐
    // let stream = TcpStream::connect(client.get_address())
    //     .await
    //     .expect("failed to connect");
    // let c = client.clone();
    // let handle = tokio::spawn(async move { c.start(stream).await });

    tokio::task::yield_now().await; // 等一下,确保连上了
    let resp = client
        .token_login(token)
        .await
        .expect("failed to login with token");

    tracing::info!("{:?}", resp);
    after_login(&client).await;
    {
        tracing::info!("{:?}", client.get_friend_list().await);
        tracing::info!("{:?}", client.get_group_list().await);
    }
    let d = client.get_allowed_clients().await;
    tracing::info!("{:?}", d);

    handle.await.unwrap();
}


================================================
FILE: ricq/Cargo.toml
================================================
[package]
name = "ricq"
version = "0.1.20"
edition = "2021"
description = "Android IM protocol"
license = "MPL-2.0"
homepage = "https://github.com/lz1998/ricq"
repository = "https://github.com/lz1998/ricq"
readme = "README.md"
keywords = ["qq", "protocol", "android", "mirai"]

[features]
default = []
image-detail = ["image"]

[dependencies]
ricq-core = { path = "../ricq-core" }
async-trait.workspace = true
bytes.workspace = true
cached = { workspace = true, default-features = false }
derivative.workspace = true
flate2 = { workspace = true, features = ["rust_backend"], default-features = false }
futures-util = { workspace = true, features = ["sink"] }
image = { workspace = true, optional = true }
jcers.workspace = true
md5.workspace = true
prost = { workspace = true, features = ["std"], default-features = false }
rand.workspace = true
serde = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["rt", "macros", "net", "time"] }
tokio-util = { workspace = true, features = ["codec"] }
tracing.workspace = true
reqwest = { workspace = true, features = ["json"] }
async-recursion = "1.0"

================================================
FILE: ricq/README.md
================================================
# RICQ

![](https://socialify.git.ci/lz1998/ricq/image?forks=1&issues=1&language=1&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Dark)

QQ Android 协议的 Rust 实现,移植于 [OICQ](https://github.com/takayama-lily/oicq)。

- [ricq](https://crates.io/crates/ricq):提供异步 API
- [ricq-core](https://crates.io/crates/ricq-core):不带 IO 的数据包构造器、解析器(通常用于 FFI)
- [ricq-axum-api](https://github.com/lz1998/ricq/tree/master/examples/ricq-axum-api):提供 HTTP API 形式的登录接口,配合 [ricq-react-ui](https://github.com/lz1998/ricq-react-ui),只需要开发登录后的逻辑。

## 如何使用

本项目是协议 Lib,如果需要直接使用,可以参考 [examples](https://github.com/lz1998/ricq/tree/master/examples) 中的例子进行开发。

可以配合前端界面 [ricq-react-ui](https://github.com/lz1998/ricq-react-ui/releases) 解压 static.zip 后,运行 [ricq-axum-api](https://github.com/lz1998/ricq/blob/master/examples/ricq-axum-api/src/bin/main.rs#L125),使用浏览器 F12-Network 查看调用方式。

普通开发者推荐使用 SDK、框架进行开发:

| 框架 / SDK | 语言 | 说明 |
| ---- | ---- | ---- |
| [rust_proc_qq](https://github.com/niuhuan/rust_proc_qq) | Rust | 模仿rocket |
| [Walle-Q](https://github.com/abrahum/walle-q) | - | onebot协议 |
| [pbrq](https://github.com/ProtobufBot/pbrq) | - | websocket+protobuf协议(附带[Web-UI](https://github.com/ProtobufBot/pbrq-react-ui)) |
| [atri_qq](https://github.com/LaoLittle/atri_qq) | - | 加载原生动态库插件,高性能低占用 |
| [awr](https://github.com/Wybxc/awr) | Python | 基于 ricq 包装,供 Python 使用的 QQ 无头客户端。 |

> 本项目是一个年轻的项目,请使用 Nightly 工具链构建本项目哦(正经人谁用 Stable 啊)

## 相关项目

| 项目 | 描述 |
| ---- | ---- |
| [lomirus/gtk-qq](https://github.com/lomirus/gtk-qq) | Unofficial Linux QQ client, based on GTK4 and libadwaita, developed with Rust and Relm4. |
| [a1967629423/esp32c3-rs-qq](https://github.com/a1967629423/esp32c3-rs-qq) | 在单片机上运行QQ |
| [ricq-react-ui](https://github.com/lz1998/ricq-react-ui) + [ricq-axum-api](https://github.com/lz1998/ricq/blob/master/examples/ricq-axum-api/src/bin/main.rs#L125) | 登录 demo,附带前端 UI |

## 已完成功能 / 开发计划

### 登录

- [x] 账号密码登录
- [x] 二维码登录
- [x] 验证码提交
- [x] 设备锁验证
- [x] 错误信息解析

### 消息类型

- [x] 文本
- [x] 表情
- [x] At
- [x] 回复
- [x] 匿名
- [x] 骰子
- [x] 石头剪刀布
- [x] 图片
- [x] 语音
- [x] 长消息(仅支持群聊发送)
- [x] 合并转发(仅支持群聊发送)
- [x] 链接分享
- [ ] 小程序(暂只支持 RAW)
- [ ] 短视频
- [ ] 群文件(上传与接收信息)

### 事件

- [x] 群消息
- [x] 好友消息
- [x] 新好友请求
- [x] 收到其他用户进群请求
- [x] 新好友
- [x] 群禁言
- [x] 好友消息撤回
- [x] 群消息撤回
- [x] 收到邀请进群请求
- [x] 群名称变更
- [x] 好友删除
- [x] 群成员权限变更
- [x] 新成员进群 / 退群
- [x] 登录号加群
- [x] 临时会话消息
- [x] 群解散
- [x] 登录号退群(包含踢出)
- [x] 客户端离线
- [ ] 群提示(戳一戳 / 运气王等)

### 主动操作

> 为防止滥用,将不支持主动邀请新成员进群

- [x] 修改昵称
- [x] 发送群消息
- [x] 获取群列表
- [x] 获取群成员列表
- [x] 获取好友列表 / 分组
- [x] 获取好友个性签名
- [x] 添加 / 删除 / 重命名好友分组
- [x] 群成员禁言 / 解除禁言
- [x] 踢出群成员
- [x] 戳一戳群友
- [x] 戳一戳好友
- [x] 设置群管理员
- [x] 设置群公告
- [x] 设置群名称
- [x] 全员禁言
- [x] 获取群@全体剩余次数
- [x] 翻译
- [x] 修改群成员头衔
- [x] 设置群精华消息
- [x] 发送好友消息
- [x] 发送临时会话消息
- [x] 修改群成员 Card
- [x] 撤回群消息
- [x] 撤回好友消息
- [x] 处理被邀请加群请求
- [x] 处理加群请求
- [x] 处理好友请求
- [x] 删除好友
- [x] 获取陌生人信息
- [x] 设置在线状态
- [x] 修改个人资料
- [x] 修改个性签名
- [x] 获取群文件下载链接
- [ ] 获取群荣誉(龙王 / 群聊火焰等)
- [ ] ~~群成员邀请~~

### 敏感操作

> 由于 [QQ 钱包支付用户服务协议](https://www.tenpay.com/v2/html5/basic/public/agreement/protocol_mqq_pay.shtml), 将不提供一切有关 QQ 钱包的功能。

> 4.13 您不得利用本服务实施下列任一的行为:
> \
> (9) **侵害 QQ 钱包支付服务系統;**

- [ ] ~~QQ 钱包协议(收款 / 付款等)~~


================================================
FILE: ricq/src/client/api/friend.rs
================================================
use std::time::Duration;

use bytes::BufMut;

use ricq_core::command::long_conn::OffPicUpResp;
use ricq_core::command::oidb_svc::{LinkShare, MusicShare, MusicVersion, ShareTarget};
use ricq_core::command::{friendlist::*, profile_service::*};
use ricq_core::hex::encode_hex;
use ricq_core::highway::BdhInput;
use ricq_core::msg::elem::FriendImage;
use ricq_core::msg::MessageChain;
use ricq_core::pb;
use ricq_core::pb::msg::routing_head::RoutingHead;
use ricq_core::structs::FriendAudio;
use ricq_core::structs::MessageReceipt;

use crate::structs::ImageInfo;
use crate::{RQError, RQResult};

impl super::super::Client {
    /// 获取好友请求
    pub async fn get_friend_system_messages(&self) -> RQResult<FriendSystemMessages> {
        let req = self
            .engine
            .read()
            .await
            .build_system_msg_new_friend_packet();
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_system_msg_friend_packet(resp.body)
    }

    /// 处理好友申请
    pub async fn solve_friend_system_message(
        &self,
        msg_seq: i64,
        req_uin: i64,
        accept: bool,
    ) -> RQResult<()> {
        let pkt = self
            .engine
            .read()
            .await
            .build_system_msg_friend_action_packet(msg_seq, req_uin, accept);
        self.send_and_wait(pkt).await?;
        Ok(())
    }

    /// 获取好友列表
    /// 第一个参数offset,从0开始;第二个参数count,150,另外两个都是0
    pub async fn _get_friend_list(
        &self,
        friend_start_index: i16,
        friend_list_count: i16,
        group_start_index: i16,
        group_list_count: i16,
    ) -> RQResult<FriendListResponse> {
        let req = self
            .engine
            .read()
            .await
            .build_friend_group_list_request_packet(
                friend_start_index,
                friend_list_count,
                group_start_index,
                group_list_count,
            );
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_friend_group_list_response(resp.body)
    }

    /// 删除好友
    /// ## Args
    /// - `del_uin` 为要删除的好友QQid
    ///
    /// ## Return
    /// - 如果删除好友成功 返回 Ok(())
    /// - 如果删除好友失败 返回 Err(RQError::Other)
    /// - 其他异常 返回 Err(..)
    pub async fn delete_friend(&self, del_uin: i64) -> RQResult<()> {
        let req = self.engine.read().await.build_delete_friend_packet(del_uin);

        let resp = self.send_and_wait(req).await?;

        let resp = self.engine.read().await.decode_remove_friend(resp.body)?;
        if resp.error_code != 0 {
            Err(RQError::Other(format!(
                "Delete Friend Failure : code = {}",
                resp.error_code
            )))
        } else {
            Ok(())
        }
    }

    /// 刷新好友列表
    pub async fn get_friend_list(&self) -> RQResult<FriendListResponse> {
        let mut output = FriendListResponse::default();
        loop {
            let resp = self
                ._get_friend_list(output.friends.len() as i16, 150, 0, 0)
                .await?;
            output.friend_groups.extend(resp.friend_groups);
            output.friends.extend(resp.friends);
            output.total_count = resp.total_count;
            if output.friends.len() as i16 >= resp.total_count {
                break;
            }
        }
        Ok(output)
    }

    /// 好友列表-添加好友分组
    pub async fn friend_list_add_group(&self, sort_id: u8, group_name: String) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_friend_list_add_group_req_packet(sort_id, &group_name);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 好友列表-重命名好友分组
    pub async fn friend_list_rename_group(&self, group_id: u8, group_name: String) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_friend_list_rename_group_req_packet(group_id, &group_name);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 好友列表-删除好友分组
    pub async fn friend_list_del_group(&self, group_id: u8) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_friend_list_del_group_req_packet(group_id);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 好友戳一戳
    pub async fn friend_poke(&self, target: i64) -> RQResult<()> {
        let req = self.engine.read().await.build_friend_poke_packet(target);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 发送好友消息
    pub async fn send_friend_message(
        &self,
        target: i64,
        message_chain: MessageChain,
    ) -> RQResult<MessageReceipt> {
        self._send_friend_message(target, message_chain, None).await
    }

    /// 发送好友语音
    pub async fn send_friend_audio(
        &self,
        target: i64,
        audio: FriendAudio,
    ) -> RQResult<MessageReceipt> {
        self._send_friend_message(target, MessageChain::default(), Some(audio.0))
            .await
    }

    async fn _send_friend_message(
        &self,
        target: i64,
        message_chain: MessageChain,
        ptt: Option<pb::msg::Ptt>,
    ) -> RQResult<MessageReceipt> {
        self.send_message(
            RoutingHead::C2c(pb::msg::C2c {
                to_uin: Some(target),
            }),
            message_chain,
            ptt,
        )
        .await
    }

    pub async fn upload_friend_image(&self, target: i64, data: &[u8]) -> RQResult<FriendImage> {
        let image_info = ImageInfo::try_new(data)?;
        let image_store = self.get_off_pic_store(target, &image_info).await?;

        let friend_image = match image_store {
            OffPicUpResp::Exist { res_id, uuid } => image_info.into_friend_image(res_id, uuid),
            OffPicUpResp::UploadRequired {
                res_id,
                uuid,
                upload_key,
                mut upload_addrs,
            } => {
                let addr = match self.highway_addrs.read().await.first() {
                    Some(addr) => *addr,
                    None => upload_addrs
                        .pop()
                        .ok_or(RQError::EmptyField("upload_addrs"))?,
                };
                self.highway_upload_bdh(
                    addr.into(),
                    BdhInput {
                        command_id: 1,
                        ticket: upload_key,
                        ext: vec![],
                        encrypt: false,
                        chunk_size: 256 * 1024,
                        send_echo: true,
                    },
                    data,
                )
                .await?;
                image_info.into_friend_image(res_id, uuid)
            }
        };
        Ok(friend_image)
    }

    pub async fn get_off_pic_store(
        &self,
        target: i64,
        image_info: &ImageInfo,
    ) -> RQResult<OffPicUpResp> {
        let req = self.engine.read().await.build_off_pic_up_packet(
            target,
            image_info.filename.clone(),
            image_info.md5.clone(),
            image_info.size as u64,
            image_info.width,
            image_info.height,
            image_info.image_type as u32,
        );
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_off_pic_up_response(resp.body)
    }

    /// 分享好友音乐
    pub async fn send_friend_music_share(
        &self,
        uin: i64,
        music_share: MusicShare,
        music_version: MusicVersion,
    ) -> RQResult<()> {
        let req = self.engine.read().await.build_share_music_request_packet(
            ShareTarget::Friend(uin),
            music_share,
            music_version,
        );
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 分享链接
    pub async fn send_friend_link_share(&self, uin: i64, link_share: LinkShare) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_share_link_request_packet(ShareTarget::Friend(uin), link_share);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    // 撤回好友消息
    pub async fn recall_friend_message(
        &self,
        uin: i64,
        msg_time: i64,
        seqs: Vec<i32>,
        rands: Vec<i32>,
    ) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_friend_recall_packet(uin, msg_time, seqs, rands);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    pub async fn upload_friend_audio(
        &self,
        target: i64,
        data: &[u8],
        audio_duration: Duration,
    ) -> RQResult<FriendAudio> {
        let md5 = md5::compute(data).to_vec();
        let size = data.len();
        let ext = self.engine.read().await.build_friend_try_up_ptt_req(
            target,
            md5.clone(),
            size as i64,
            size as i32,
        );
        let addr = self
            .highway_addrs
            .read()
            .await
            .first()
            .copied()
            .ok_or(RQError::EmptyField("highway_addrs"))?;
        let ticket = self
            .highway_session
            .read()
            .await
            .sig_session
            .clone()
            .to_vec();
        let resp = self
            .highway_upload_bdh(
                addr.into(),
                BdhInput {
                    command_id: 26,
                    ticket,
                    ext: ext.to_vec(),
                    encrypt: false,
                    chunk_size: 256 * 1024,
                    send_echo: true,
                },
                data,
            )
            .await?;
        let uuid = self
            .engine
            .read()
            .await
            .decode_friend_try_up_ptt_resp(resp)?;
        Ok(FriendAudio(pb::msg::Ptt {
            file_type: Some(4),
            src_uin: Some(self.uin().await),
            file_uuid: Some(uuid),
            file_name: Some(format!("{}.amr", encode_hex(&md5))),
            file_md5: Some(md5),
            file_size: Some(size as i32),
            reserve: Some({
                let mut w = Vec::new();
                w.put_u8(3); // tlv count
                {
                    w.put_u8(8);
                    w.put_u16(4);
                    w.put_u32(1); // codec
                }
                {
                    w.put_u8(9);
                    w.put_u16(4);
                    w.put_u32(audio_duration.as_secs() as u32); // voiceLength
                }
                {
                    w.put_u8(10);
                    w.put_u16(6);
                    w.put_slice(&[0x08, 0x00, 0x28, 0x00, 0x38, 0x00]); // change_voice+redpack_type+autototext_voice
                }
                w
            }),
            bool_valid: Some(true),
            ..Default::default()
        }))
    }

    pub async fn get_friend_audio_url(
        &self,
        sender_uin: i64,
        audio: FriendAudio,
    ) -> RQResult<String> {
        let req = self.engine.read().await.build_c2c_ptt_down_req(
            sender_uin,
            audio.0.file_uuid.ok_or(RQError::EmptyField("file_uuid"))?,
        );
        let resp = self.send_and_wait(req).await?;
        self.engine.read().await.decode_c2c_ptt_down(resp.body)
    }

    /// 标记私聊消息已读 TODO 待测试
    pub async fn mark_friend_message_readed(&self, uin: i64, time: i64) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_friend_msg_readed_packet(uin, time);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 获取好友个性签名
    pub async fn get_friend_rich_sig(&self, user_ids: Vec<i64>) -> RQResult<Vec<RichSigInfo>> {
        let req = self
            .engine
            .read()
            .await
            .build_get_rich_sig_request_packet(user_ids);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_get_rich_sig_response_packet(resp.body)
    }
}


================================================
FILE: ricq/src/client/api/group.rs
================================================
use std::collections::HashMap;
use std::time::{Duration, UNIX_EPOCH};

use bytes::Bytes;
use cached::Cached;
use prost::Message;

use ricq_core::command::common::PbToBytes;
use ricq_core::command::img_store::GroupImageStoreResp;
use ricq_core::command::multi_msg::gen_forward_preview;
use ricq_core::command::{friendlist::*, oidb_svc::*, profile_service::*};
use ricq_core::common::group_code2uin;
use ricq_core::hex::encode_hex;
use ricq_core::highway::BdhInput;
use ricq_core::msg::elem::{Anonymous, GroupImage, RichMsg, VideoFile};
use ricq_core::msg::MessageChain;
use ricq_core::pb;
use ricq_core::pb::short_video::ShortVideoUploadRsp;
use ricq_core::structs::{ForwardMessage, GroupFileCount, GroupFileList, MessageNode};
use ricq_core::structs::{GroupAudio, GroupMemberPermission};
use ricq_core::structs::{GroupInfo, GroupMemberInfo, MessageReceipt};

use crate::structs::ImageInfo;
use crate::{RQError, RQResult};

impl super::super::Client {
    /// 获取进群申请信息
    async fn get_group_system_messages(&self, suspicious: bool) -> RQResult<GroupSystemMessages> {
        let req = self
            .engine
            .read()
            .await
            .build_system_msg_new_group_packet(suspicious);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_system_msg_group_packet(resp.body)
    }

    /// 获取所有进群请求
    pub async fn get_all_group_system_messages(&self) -> RQResult<GroupSystemMessages> {
        let mut resp = self.get_group_system_messages(false).await?;
        let risk_resp = self.get_group_system_messages(true).await?;
        resp.join_group_requests
            .extend(risk_resp.join_group_requests);
        resp.self_invited.extend(risk_resp.self_invited);
        Ok(resp)
    }

    /// 处理加群申请
    #[allow(clippy::too_many_arguments)]
    pub async fn solve_group_system_message(
        &self,
        msg_seq: i64,
        req_uin: i64,
        group_code: i64,
        suspicious: bool,
        is_invite: bool,
        accept: bool,
        block: bool,
        reason: String,
    ) -> RQResult<()> {
        let pkt = self
            .engine
            .read()
            .await
            .build_system_msg_group_action_packet(
                msg_seq,
                req_uin,
                group_code,
                if suspicious { 2 } else { 1 },
                is_invite,
                accept,
                block,
                reason,
            );
        self.send_and_wait(pkt).await?;

        Ok(())
    }

    /// 获取群列表
    /// 第一个参数offset,从0开始;第二个参数count,150,另外两个都是0
    pub async fn _get_group_list(&self, vec_cookie: &[u8]) -> RQResult<GroupListResponse> {
        let req = self
            .engine
            .read()
            .await
            .build_group_list_request_packet(vec_cookie);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_list_response(resp.body)
    }

    /// 发送群消息
    pub async fn send_group_message(
        &self,
        group_code: i64,
        message_chain: MessageChain,
    ) -> RQResult<MessageReceipt> {
        self._send_group_message(group_code, message_chain.into(), None)
            .await
    }

    /// 发送群语音
    pub async fn send_group_audio(
        &self,
        group_code: i64,
        group_audio: GroupAudio,
    ) -> RQResult<MessageReceipt> {
        self._send_group_message(group_code, vec![], Some(group_audio.0))
            .await
    }

    async fn _send_group_message(
        &self,
        group_code: i64,
        elems: Vec<pb::msg::Elem>,
        ptt: Option<pb::msg::Ptt>,
    ) -> RQResult<MessageReceipt> {
        let ran = (rand::random::<u32>() >> 1) as i32;
        let (tx, rx) = tokio::sync::oneshot::channel();
        {
            self.receipt_waiters.lock().await.cache_set(ran, tx);
        }
        let req = self
            .engine
            .read()
            .await
            .build_group_sending_packet(group_code, elems, ptt, ran, 1, 0, 0, false);
        let _ = self.send_and_wait(req).await?;
        let mut receipt = MessageReceipt {
            seqs: vec![0],
            rands: vec![ran],
            time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i64,
        };
        match tokio::time::timeout(Duration::from_secs(5), rx).await {
            Ok(Ok(seq)) => {
                if let Some(s) = receipt.seqs.first_mut() {
                    *s = seq;
                }
            }
            Ok(Err(_)) => {} //todo
            Err(_) => {}
        }
        Ok(receipt)
    }

    /// 发送群成员临时消息
    pub async fn send_group_temp_message(
        &self,
        group_code: i64,
        user_uin: i64,
        message_chain: MessageChain,
    ) -> RQResult<MessageReceipt> {
        self.send_message(
            pb::msg::routing_head::RoutingHead::GrpTmp(pb::msg::GrpTmp {
                group_uin: Some(group_code2uin(group_code)),
                to_uin: Some(user_uin),
            }),
            message_chain,
            None,
        )
        .await
    }

    /// 获取群成员信息
    pub async fn get_group_member_info(
        &self,
        group_code: i64,
        uin: i64,
    ) -> RQResult<GroupMemberInfo> {
        let req = self
            .engine
            .read()
            .await
            .build_group_member_info_request_packet(group_code, uin);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_member_info_response(resp.body)
    }

    /// 批量获取群信息
    pub async fn get_group_infos(&self, group_codes: Vec<i64>) -> RQResult<Vec<GroupInfo>> {
        let req = self
            .engine
            .read()
            .await
            .build_group_info_request_packet(group_codes);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_info_response(resp.body)
    }

    /// 获取群信息
    pub async fn get_group_info(&self, group_code: i64) -> RQResult<Option<GroupInfo>> {
        Ok(self.get_group_infos(vec![group_code]).await?.pop())
    }

    /// 刷新群列表
    pub async fn get_group_list(&self) -> RQResult<Vec<GroupInfo>> {
        // 获取群列表
        let mut vec_cookie = Bytes::new();
        let mut groups = Vec::new();
        loop {
            let resp = self._get_group_list(&vec_cookie).await?;
            vec_cookie = resp.vec_cookie;
            for g in resp.groups {
                groups.push(g);
            }
            if vec_cookie.is_empty() {
                break;
            }
        }
        Ok(groups)
    }

    /// 获取群成员列表 (low level api)
    async fn _get_group_member_list(
        &self,
        group_code: i64,
        next_uin: i64,
        group_owner_uin: i64,
    ) -> RQResult<GroupMemberListResponse> {
        let req = self
            .engine
            .read()
            .await
            .build_group_member_list_request_packet(group_code, next_uin);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_member_list_response(resp.body, group_owner_uin)
    }

    /// 获取群成员列表
    pub async fn get_group_member_list(
        &self,
        group_code: i64,
        group_owner_uin: i64,
    ) -> RQResult<Vec<GroupMemberInfo>> {
        let mut next_uin = 0;
        let mut list = Vec::new();
        loop {
            let mut resp = self
                ._get_group_member_list(group_code, next_uin, group_owner_uin)
                .await?;
            if resp.list.is_empty() {
                return Err(RQError::EmptyField("GroupMemberListResponse.list"));
            }
            for m in resp.list.iter_mut() {
                m.group_code = group_code;
            }
            list.append(&mut resp.list);
            next_uin = resp.next_uin;
            if next_uin == 0 {
                break;
            }
        }
        Ok(list)
    }

    /// 标记群消息已读
    pub async fn mark_group_message_readed(&self, group_code: i64, seq: i32) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_msg_readed_packet(group_code, seq);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 群禁言 (解除禁言 duration=0)
    pub async fn group_mute(
        &self,
        group_code: i64,
        member_uin: i64,
        duration: std::time::Duration,
    ) -> RQResult<()> {
        let req = self.engine.read().await.build_group_mute_packet(
            group_code,
            member_uin,
            duration.as_secs() as u32,
        );
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 全员禁言
    pub async fn group_mute_all(&self, group_code: i64, mute: bool) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_mute_all_packet(group_code, mute);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 修改群名称
    pub async fn update_group_name(&self, group_code: i64, name: String) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_name_update_packet(group_code, name);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 设置群公告
    pub async fn update_group_memo(&self, group_code: i64, memo: String) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_memo_update_packet(group_code, memo);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 设置群管理员
    ///
    /// flag: true 设置管理员 false 取消管理员
    pub async fn group_set_admin(&self, group_code: i64, member: i64, flag: bool) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_admin_set_packet(group_code, member, flag);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 群戳一戳
    pub async fn group_poke(&self, group_code: i64, target: i64) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_poke_packet(group_code, target);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 群踢人
    pub async fn group_kick(
        &self,
        group_code: i64,
        member_uins: Vec<i64>,
        kick_msg: &str,
        block: bool,
    ) -> RQResult<()> {
        let req = self.engine.read().await.build_group_kick_packet(
            group_code,
            member_uins,
            kick_msg,
            block,
        );
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    pub async fn group_invite(&self, group_code: i64, uin: i64) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_invite_packet(group_code, uin);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    pub async fn group_quit(&self, group_code: i64) -> RQResult<()> {
        let req = self.engine.read().await.build_quit_group_packet(group_code);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 获取群 @全体成员 剩余次数
    pub async fn group_at_all_remain(&self, group_code: i64) -> RQResult<GroupAtAllRemainInfo> {
        let req = self
            .engine
            .read()
            .await
            .build_group_at_all_remain_request_packet(group_code);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_at_all_remain_response(resp.body)
    }

    /// 设置群头衔
    pub async fn group_edit_special_title(
        &self,
        group_code: i64,
        member_uin: i64,
        new_title: String,
    ) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_edit_special_title_packet(group_code, member_uin, new_title);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 获取自己的匿名信息(用于发送群消息)
    pub async fn get_anony_info(&self, group_code: i64) -> RQResult<Option<Anonymous>> {
        let req = self
            .engine
            .read()
            .await
            .build_get_anony_info_request(group_code);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_get_anony_info_response(resp.body)
    }

    /// 分享群音乐
    pub async fn send_group_music_share(
        &self,
        group_code: i64,
        music_share: MusicShare,
        music_version: MusicVersion,
    ) -> RQResult<()> {
        let req = self.engine.read().await.build_share_music_request_packet(
            ShareTarget::Group(group_code),
            music_share,
            music_version,
        );
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 分享链接
    pub async fn send_group_link_share(
        &self,
        group_code: i64,
        link_share: LinkShare,
    ) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_share_link_request_packet(ShareTarget::Group(group_code), link_share);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 修改群名片
    pub async fn edit_group_member_card(
        &self,
        group_code: i64,
        member_uin: i64,
        card: String,
    ) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_edit_group_tag_packet(group_code, member_uin, card);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 撤回群消息
    pub async fn recall_group_message(
        &self,
        group_code: i64,
        seqs: Vec<i32>,
        rands: Vec<i32>,
    ) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_recall_packet(group_code, seqs, rands);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    // 用 highway 上传群图片之前调用,获取 upload_key
    pub async fn get_group_image_store(
        &self,
        group_code: i64,
        image_info: &ImageInfo,
    ) -> RQResult<GroupImageStoreResp> {
        let req = self.engine.read().await.build_group_image_store_packet(
            group_code,
            image_info.filename.clone(),
            image_info.md5.clone(),
            image_info.size as u64,
            image_info.width,
            image_info.height,
            image_info.image_type as u32,
        );
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_image_store_response(resp.body)
    }

    /// 上传群图片
    pub async fn upload_group_image(&self, group_code: i64, data: &[u8]) -> RQResult<GroupImage> {
        let image_info = ImageInfo::try_new(data)?;

        let image_store = self.get_group_image_store(group_code, &image_info).await?;
        let signature = self.highway_session.read().await.session_key.to_vec();
        let group_image = match image_store {
            GroupImageStoreResp::Exist { file_id, addrs } => image_info.into_group_image(
                file_id,
                addrs.first().copied().unwrap_or_default(),
                signature,
            ),
            GroupImageStoreResp::NotExist {
                file_id,
                upload_key,
                mut upload_addrs,
            } => {
                let addr = match self.highway_addrs.read().await.first() {
                    Some(addr) => *addr,
                    None => upload_addrs
                        .pop()
                        .ok_or(RQError::EmptyField("upload_addrs"))?,
                };
                self.highway_upload_bdh(
                    addr.into(),
                    BdhInput {
                        command_id: 2,
                        ticket: upload_key,
                        ext: vec![],
                        encrypt: false,
                        chunk_size: 256 * 1024,
                        send_echo: true,
                    },
                    data,
                )
                .await?;
                image_info.into_group_image(file_id, addr, signature)
            }
        };
        Ok(group_image)
    }

    /// 上传群音频 codec: 0-amr, 1-silk
    pub async fn upload_group_audio(
        &self,
        group_code: i64,
        data: &[u8],
        codec: u32,
    ) -> RQResult<GroupAudio> {
        let md5 = md5::compute(data).to_vec();
        let size = data.len();
        let ext = self.engine.read().await.build_group_try_up_ptt_req(
            group_code,
            md5.clone(),
            size as u64,
            codec,
            size as u32,
        );
        let addr = self
            .highway_addrs
            .read()
            .await
            .first()
            .copied()
            .ok_or(RQError::EmptyField("highway_addrs"))?;
        let ticket = self
            .highway_session
            .read()
            .await
            .sig_session
            .clone()
            .to_vec();
        let resp = self
            .highway_upload_bdh(
                addr.into(),
                BdhInput {
                    command_id: 29,
                    ticket,
                    ext: ext.to_vec(),
                    encrypt: false,
                    chunk_size: 256 * 1024,
                    send_echo: true,
                },
                data,
            )
            .await?;
        let file_key = self
            .engine
            .read()
            .await
            .decode_group_try_up_ptt_resp(resp)?;
        Ok(GroupAudio(pb::msg::Ptt {
            file_type: Some(4),
            src_uin: Some(self.uin().await),
            file_name: Some(format!("{}.amr", encode_hex(&md5))),
            file_md5: Some(md5),
            file_size: Some(size as i32),
            bool_valid: Some(true),
            pb_reserve: Some(vec![8, 0, 40, 0, 56, 0]),
            group_file_key: Some(file_key),
            ..Default::default()
        }))
    }

    pub async fn get_group_audio_url(
        &self,
        group_code: i64,
        audio: GroupAudio,
    ) -> RQResult<String> {
        let req = self.engine.read().await.build_group_ptt_down_req(
            group_code,
            audio.0.file_md5.ok_or(RQError::EmptyField("file_md5"))?,
        );
        let resp = self.send_and_wait(req).await?;
        self.engine.read().await.decode_group_ptt_down(resp.body)
    }

    // 用 highway 上传群视频之前调用,获取 upload_key
    pub async fn get_group_short_video_store(
        &self,
        short_video_upload_req: pb::short_video::ShortVideoUploadReq,
    ) -> RQResult<ShortVideoUploadRsp> {
        let req = self
            .engine
            .read()
            .await
            .build_group_video_store_packet(short_video_upload_req);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_video_store_response(resp.body)
    }

    /// 上传群短视频 参数:群号,视频数据,封面数据
    /// TODO 未来可能会改成输入 std::io::Read
    pub async fn upload_group_short_video(
        &self,
        group_code: i64,
        video_data: &[u8],
        thumb_data: &[u8],
    ) -> RQResult<VideoFile> {
        let video_md5 = md5::compute(video_data).to_vec();
        let thumb_md5 = md5::compute(thumb_data).to_vec();
        let video_size = video_data.len();
        let thumb_size = thumb_data.len();
        let short_video_up_req = self.engine.read().await.build_short_video_up_req(
            group_code,
            video_md5.clone(),
            thumb_md5.clone(),
            video_size as i64,
            thumb_size as i64,
        );
        let ext = short_video_up_req.to_bytes().to_vec();

        let video_store = self.get_group_short_video_store(short_video_up_req).await?;

        if video_store.file_exists == 1 {
            return Ok(VideoFile {
                name: format!("{}.mp4", encode_hex(&video_md5)),
                uuid: video_store.file_id,
                size: video_size as i32,
                thumb_size: thumb_size as i32,
                md5: video_md5,
                thumb_md5,
            });
        }

        let addr = self
            .highway_addrs
            .read()
            .await
            .first()
            .copied()
            .ok_or(RQError::EmptyField("highway_addrs"))?;

        if self.highway_session.read().await.session_key.is_empty() {
            return Err(RQError::EmptyField("highway_session_key"));
        }
        let ticket = self.highway_session.read().await.sig_session.to_vec();
        let mut data = Vec::with_capacity(thumb_size + video_size);
        data.copy_from_slice(thumb_data);
        data[thumb_size..].copy_from_slice(video_data);

        let rsp = self
            .highway_upload_bdh(
                addr.into(),
                BdhInput {
                    command_id: 25,
                    ticket,
                    ext,
                    encrypt: true,
                    chunk_size: 256 * 1024,
                    send_echo: true,
                },
                &data,
            )
            .await?;
        let rsp = pb::short_video::ShortVideoUploadRsp::decode(&*rsp)
            .map_err(|_| RQError::Decode("ShortVideoUploadRsp".into()))?;
        Ok(VideoFile {
            name: format!("{}.mp4", encode_hex(&video_md5)),
            uuid: rsp.file_id,
            size: video_size as i32,
            thumb_size: thumb_size as i32,
            md5: video_md5,
            thumb_md5,
        })
    }

    /// 设置群精华消息
    pub async fn operate_group_essence(
        &self,
        group_code: i64,
        msg_seq: i32,
        msg_rand: i32,
        flag: bool,
    ) -> RQResult<pb::oidb::EacRspBody> {
        let req = self
            .engine
            .read()
            .await
            .build_essence_msg_operate_packet(group_code, msg_seq, msg_rand, flag);
        let resp = self.send_and_wait(req).await?;
        let decode = self
            .engine
            .read()
            .await
            .decode_essence_msg_response(resp.body)?;
        Ok(decode)
    }

    /// 发送群消息
    /// 仅在多张图片时需要,发送文字不需要
    pub async fn send_group_long_message(
        &self,
        group_code: i64,
        message_chain: MessageChain,
    ) -> RQResult<MessageReceipt> {
        let brief = "[图片][图片][图片]"; // TODO brief
        let res_id = self
            .upload_msgs(
                group_code,
                vec![MessageNode {
                    sender_id: self.uin().await,
                    time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i32,
                    sender_name: self.account_info.read().await.nickname.clone(),
                    elements: message_chain,
                }
                .into()],
                true,
            )
            .await?;
        let template=format!(
            "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID=\"35\" templateID=\"1\" action=\"viewMultiMsg\" brief=\"{}\" m_resid=\"{}\" m_fileName=\"{}\" sourceMsgId=\"0\" url=\"\" flag=\"3\" adverSign=\"0\" multiMsgFlag=\"1\"><item layout=\"1\"><title>{}</title><hr hidden=\"false\" style=\"0\" /><summary>点击查看完整消息</summary></item><source name=\"聊天记录\" icon=\"\" action=\"\" appid=\"-1\" /></msg>",
            brief,
            res_id,
            UNIX_EPOCH.elapsed().unwrap().as_millis(),
            brief);
        let mut chain = MessageChain::default();
        chain.push(RichMsg {
            service_id: 35,
            template1: template,
        });
        chain.0.extend(vec![
            pb::msg::elem::Elem::Text(pb::msg::Text {
                str: Some("你的QQ暂不支持查看[转发多条消息],请期待后续版本。".into()),
                ..Default::default()
            }),
            pb::msg::elem::Elem::GeneralFlags(pb::msg::GeneralFlags {
                long_text_flag: Some(1),
                long_text_resid: Some(res_id),
                pendant_id: Some(0),
                pb_reserve: Some(vec![0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00]), // TODO 15=73255?
                ..Default::default()
            }),
        ]);
        self._send_group_message(group_code, chain.into(), None)
            .await
    }

    /// 发送转发消息
    pub async fn send_group_forward_message(
        &self,
        group_code: i64,
        msgs: Vec<ForwardMessage>,
    ) -> RQResult<MessageReceipt> {
        let t_sum = msgs.len();
        let preview = gen_forward_preview(&msgs);
        let res_id = self.upload_msgs(group_code, msgs, false).await?;
        // TODO friend template?
        let template = format!(
            r##"<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="35" templateID="1" action="viewMultiMsg" brief="[聊天记录]" m_resid="{}" m_fileName="{}" tSum="{}" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0"><item layout="1" advertiser_id="0" aid="0"><title size="34" maxLines="2" lineSpace="12">群聊的聊天记录</title>{}<hr hidden="false" style="0" /><summary size="26" color="#777777">查看{}条转发消息</summary></item><source name="聊天记录" icon="" action="" appid="-1" /></msg>"##,
            res_id,
            UNIX_EPOCH.elapsed().unwrap().as_millis(), // TODO m_filename?
            t_sum,
            preview,
            t_sum
        );
        let mut chain = MessageChain::default();
        chain.push(RichMsg {
            service_id: 35,
            template1: template,
        });
        chain
            .0
            .push(pb::msg::elem::Elem::GeneralFlags(pb::msg::GeneralFlags {
                pendant_id: Some(0),
                pb_reserve: Some(vec![0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00]),
                ..Default::default()
            }));
        self._send_group_message(group_code, chain.into(), None)
            .await
    }

    /// 获取群主/管理员列表
    pub async fn get_group_admin_list(
        &self,
        group_code: i64,
    ) -> RQResult<HashMap<i64, GroupMemberPermission>> {
        let req = self
            .engine
            .read()
            .await
            .build_get_group_admin_list_request_packet(group_code as u64);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_get_group_admin_list_response(resp.body)
    }

    /// 群聊打卡
    pub async fn group_sign_in(&self, group_code: i64) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_group_sign_in_packet(group_code);
        self.send_and_wait(req).await?;
        Ok(())
    }
    // 获取群文件列表
    pub async fn get_group_file_list(
        &self,
        group_code: u64,
        folder_id: &str,
        start_index: u32,
    ) -> RQResult<GroupFileList> {
        let req = self
            .engine
            .read()
            .await
            .build_group_file_list_request_packet(group_code, folder_id.into(), start_index);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_file_list_response(resp.body)
    }

    /// 获取群文件总数
    pub async fn get_group_files_count(&self, group_code: u64) -> RQResult<GroupFileCount> {
        let req = self
            .engine
            .read()
            .await
            .build_group_file_count_request_packet(group_code);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_file_count_response(resp.body)
    }
    /// 获取文件下载链接
    /// # Examples
    /// ```
    /// # async fn test(client: &ricq::Client) {
    /// let group_code: i64 = 123456789;
    /// let file_list = client.get_group_file_list(group_code.try_into().unwrap(), "/", 0).await.unwrap();
    /// for item_info in file_list.items {
    ///     let url = client
    ///         .get_group_file_download(
    ///             group_code,
    ///             &item_info.file_info.file_id,
    ///             item_info.file_info.bus_id,
    ///             &item_info.file_info.file_name,
    ///         )
    ///         .await;
    ///     println!("{:?}", url);
    /// }
    /// # }
    ///```
    pub async fn get_group_file_download(
        &self,
        group_code: i64,
        file_id: &str,
        bus_id: u32,
        file_name: &str,
    ) -> RQResult<String> {
        let req = self
            .engine
            .read()
            .await
            .build_group_file_download_request_packet(group_code, file_id.into(), bus_id as i32);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_group_file_download_response(resp.body, file_name)
    }
}


================================================
FILE: ricq/src/client/api/login.rs
================================================
use std::sync::atomic::Ordering;

use crate::jce::SvcRespRegister;
use crate::qsign::QSignClient;
use crate::{RQError, RQResult};
use ricq_core::command::wtlogin::*;
use ricq_core::hex::decode_hex;
use ricq_core::token::Token;

/// 登录相关
impl super::super::Client {
    /// 二维码登录 - 获取二维码
    pub async fn fetch_qrcode(&self) -> RQResult<QRCodeState> {
        let req = self.engine.read().await.build_qrcode_fetch_request_packet();
        let resp = self.send_and_wait(req).await?;
        let resp = self
            .engine
            .read()
            .await
            .decode_trans_emp_response(resp.body)?;
        self.process_trans_emp_response(&resp).await;
        Ok(resp)
    }

    /// 二维码登录 - 查询二维码状态
    pub async fn query_qrcode_result(&self, sig: &[u8]) -> RQResult<QRCodeState> {
        let req = self
            .engine
            .read()
            .await
            .build_qrcode_result_query_request_packet(sig);
        let resp = self.send_and_wait(req).await?;
        let resp = self
            .engine
            .read()
            .await
            .decode_trans_emp_response(resp.body)?;
        self.process_trans_emp_response(&resp).await;
        Ok(resp)
    }

    /// 二维码登录 - 登录 ( 可能还需要 device_lock_login )
    pub async fn qrcode_login(
        &self,
        tmp_pwd: &[u8],
        tmp_no_pic_sig: &[u8],
        tgt_qr: &[u8],
    ) -> RQResult<LoginResponse> {
        let req =
            self.engine
                .read()
                .await
                .build_qrcode_login_packet(tmp_pwd, tmp_no_pic_sig, tgt_qr);
        let resp = self.send_and_wait(req).await?;
        let resp = self.engine.read().await.decode_login_response(resp.body)?;
        self.process_login_response(&resp).await;
        Ok(resp)
    }

    pub async fn sign(&self, data: &str) -> RQResult<Vec<u8>> {
        let uin = self.uin().await;
        let engine = self.engine.read().await;
        let sub_cmd = u8::from_str_radix(&data[4..], 16).unwrap();
        let salt = QSignClient::calc_salt(
            uin as u64,
            &engine.transport.sig.guid,
            &engine.transport.version.sdk_version,
            sub_cmd as u32,
        );
        let resp = self
            .qsign_client
            .custom_energy(
                uin,
                data,
                &salt,
                &engine.transport.sig.guid,
                &engine.transport.device.android_id,
            )
            .await
            .map_err(|e| RQError::Other(e.to_string()))?;
        if resp.code != 0 {
            return Err(RQError::Other(format!("failed to energy {}", resp.msg)));
        }
        decode_hex(&resp.data)
            .map_err(|err| RQError::Other(format!("failed to decode hex: {}", err)))
    }

    /// 密码登录 - 提交密码md5
    pub async fn password_md5_login(
        &self,
        uin: i64,
        password_md5: &[u8],
    ) -> RQResult<LoginResponse> {
        self.engine.read().await.uin.store(uin, Ordering::Relaxed);
        let sign = self.sign("810_9").await?;
        let req = self
            .engine
            .read()
            .await
            .build_login_packet(password_md5, &sign, true);
        let resp = self.send_and_wait(req).await?;
        let resp = self.engine.read().await.decode_login_response(resp.body)?;
        self.process_login_response(&resp).await;
        Ok(resp)
    }

    pub async fn password_login(&self, uin: i64, password: &str) -> RQResult<LoginResponse> {
        self.password_md5_login(uin, &md5::compute(password).to_vec())
            .await
    }

    /// 密码登录 - 请求短信验证码
    pub async fn request_sms(&self) -> RQResult<LoginResponse> {
        let req = self.engine.read().await.build_sms_request_packet();
        let resp = self.send_and_wait(req).await?;
        let resp = self.engine.read().await.decode_login_response(resp.body)?;
        self.process_login_response(&resp).await;
        Ok(resp)
    }

    /// 密码登录 - 提交短信验证码
    pub async fn submit_sms_code(&self, code: &str) -> RQResult<LoginResponse> {
        let sign = self.sign("810_7").await?;
        let req = self
            .engine
            .read()
            .await
            .build_sms_code_submit_packet(code.trim(), &sign);
        let resp = self.send_and_wait(req).await?;
        let resp = self.engine.read().await.decode_login_response(resp.body)?;
        self.process_login_response(&resp).await;
        Ok(resp)
    }

    /// 密码登录 - 提交滑块ticket
    pub async fn submit_ticket(&self, ticket: &str) -> RQResult<LoginResponse> {
        let sign = self.sign("810_2").await?;
        let req = self
            .engine
            .read()
            .await
            .build_ticket_submit_packet(ticket, &sign);
        let resp = self.send_and_wait(req).await?;
        let resp = self.engine.read().await.decode_login_response(resp.body)?;
        self.process_login_response(&resp).await;
        Ok(resp)
    }

    /// 设备锁登录 - 二维码、密码登录都需要
    pub async fn device_lock_login(&self) -> RQResult<LoginResponse> {
        let req = self.engine.read().await.build_device_lock_login_packet();
        let resp = self.send_and_wait(req).await?;
        let resp = self.engine.read().await.decode_login_response(resp.body)?;
        self.process_login_response(&resp).await;
        Ok(resp)
    }

    /// token 登录
    pub async fn token_login(&self, token: Token) -> RQResult<LoginResponse> {
        self.load_token(token).await;
        self.request_change_sig(None).await
    }

    /// 换 token,使用后需要重新 register
    pub async fn request_change_sig(&self, main_sig_map: Option<u32>) -> RQResult<LoginResponse> {
        let req = self
            .engine
            .read()
            .await
            .build_request_change_sig_packet(main_sig_map);
        let resp = self.send_and_wait(req).await?;
        let resp = self
            .engine
            .read()
            .await
            .decode_exchange_emp_response(resp.body)?;
        self.process_login_response(&resp).await;
        Ok(resp)
    }

    /// 注册客户端,登录后必须注册
    pub async fn register_client(&self) -> RQResult<SvcRespRegister> {
        let req = self.engine.read().await.build_client_register_packet();
        let resp = self.send_and_wait(req).await?;
        let resp = self
            .engine
            .read()
            .await
            .decode_client_register_response(resp.body)?;
        if !resp.result.is_empty() || resp.reply_code != 0 {
            return Err(RQError::Other(resp.result + &resp.reply_code.to_string()));
        }
        self.online.store(true, Ordering::SeqCst);
        Ok(resp)
    }

    pub async fn heartbeat(&self) -> RQResult<()> {
        let req = self.engine.read().await.build_heartbeat_packet();
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    // 系统强制下线 response
    pub(crate) async fn send_msg_offline_rsp(&self, uin: i64, seq_no: i64) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_msf_force_offline_rsp(uin, seq_no);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    pub(crate) async fn send_sid_ticket_expired_response(&self, seq: i32) -> RQResult<()> {
        let rsp = self
            .engine
            .read()
            .await
            .build_sid_ticket_expired_response(seq);
        self.send(rsp).await?;
        Ok(())
    }
}


================================================
FILE: ricq/src/client/api/mod.rs
================================================
use std::net::SocketAddr;
use std::sync::atomic::Ordering;
use std::time::UNIX_EPOCH;

use bytes::Bytes;
use cached::Cached;

use ricq_core::command::message_svc::MessageSyncResponse;
use ricq_core::command::oidb_svc::*;
use ricq_core::common::{group_code2uin, RQAddr};
use ricq_core::highway::BdhInput;
use ricq_core::msg::MessageChain;
use ricq_core::pb;
use ricq_core::structs::Status;
use ricq_core::structs::SummaryCardInfo;
use ricq_core::structs::{ForwardMessage, MessageReceipt};

use crate::jce::SvcDevLoginInfo;
use crate::{RQError, RQResult};

mod friend;
mod group;
mod login;

/// API
impl super::Client {
    /// 设置在线状态 TODO net_type
    pub async fn update_online_status<T>(&self, status: T) -> RQResult<()>
    where
        T: Into<Status>,
    {
        let status = status.into();
        if let Some(ref custom_status) = status.custom_status {
            if custom_status.wording.is_empty() || custom_status.wording.chars().count() > 4 {
                return Err(RQError::Other("invalid wording length".into()));
            }
        }
        let req = self.engine.read().await.build_set_online_status_packet(
            status.online_status,
            status.ext_online_status,
            status.custom_status,
        );
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 修改签名
    pub async fn update_signature(&self, signature: String) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_update_signature_packet(signature);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 修改个人资料
    pub async fn update_profile_detail(&self, profile: ProfileDetailUpdate) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_update_profile_detail_packet(profile);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 刷新客户端状态
    pub async fn refresh_status(&self) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_get_offline_msg_request_packet(self.last_message_time.load(Ordering::SeqCst));
        let _resp = self.send_and_wait(req).await?;
        Ok(())
    }

    /// 获取通过安全验证的设备
    pub async fn get_allowed_clients(&self) -> RQResult<Vec<SvcDevLoginInfo>> {
        let req = self.engine.read().await.build_device_list_request_packet();
        let resp = self.send_and_wait(req).await?;
        self.engine.read().await.decode_dev_list_response(resp.body)
    }

    /// 文本翻译
    pub async fn translate(
        &self,
        src_language: String,
        dst_language: String,
        src_text_list: Vec<String>,
    ) -> RQResult<Vec<String>> {
        let list_len = src_text_list.len();
        let req = self.engine.read().await.build_translate_request_packet(
            src_language,
            dst_language,
            src_text_list,
        );
        let resp = self.send_and_wait(req).await?;
        let translations = self
            .engine
            .read()
            .await
            .decode_translate_response(resp.body)?;
        if translations.len() != list_len {
            return Err(RQError::Other("translate length error".into()));
        }
        Ok(translations)
    }

    // source 0-自己 1-好友 2-群成员
    // cookie source=1时 在 summary info 获取
    pub async fn send_like(
        &self,
        uin: i64,
        count: i32,
        source: i32,
        cookies: Bytes,
    ) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_send_like_packet(uin, count, source, cookies);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    // TODO 待完善
    // 图片 OCR
    pub async fn image_ocr(
        &self,
        img_url: String,
        md5: String,
        size: i32,
        wight: i32,
        height: i32,
    ) -> RQResult<OcrResponse> {
        let req = self
            .engine
            .read()
            .await
            .build_image_ocr_request_packet(img_url, md5, size, wight, height);
        let resp = self.send_and_wait(req).await?;

        let decode = self
            .engine
            .read()
            .await
            .decode_image_ocr_response(resp.body)?;
        Ok(decode)
    }

    // 标记消息已收到,server 不再重复推送
    pub async fn delete_message(&self, items: Vec<pb::MessageItem>) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_delete_message_request_packet(items);
        let _ = self.send_and_wait(req).await?;
        Ok(())
    }

    // 标记 online_push 已收到,server 不再重复推送
    pub async fn delete_online_push(
        &self,
        uin: i64,
        svrip: i32,
        push_token: Bytes,
        seq: u16,
        del_msg: Vec<ricq_core::jce::PushMessageInfo>,
    ) -> RQResult<()> {
        let req = self
            .engine
            .read()
            .await
            .build_delete_online_push_packet(uin, svrip, push_token, seq, del_msg);
        self.send(req).await?;
        Ok(())
    }

    // sync message
    async fn sync_message(&self, sync_flag: i32) -> RQResult<MessageSyncResponse> {
        let time = UNIX_EPOCH.elapsed().unwrap().as_secs() as i64;
        let req = self
            .engine
            .read()
            .await
            .build_get_message_request_packet(sync_flag, time);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_message_svc_packet(resp.body)
    }

    // 从服务端拉取通知
    pub(crate) async fn sync_all_message(&self) -> RQResult<Vec<pb::msg::Message>> {
        const SYNC_START: i32 = 0;
        const _SYNC_CONTINUE: i32 = 1;
        const SYNC_STOP: i32 = 2;

        let mut sync_flag = SYNC_START;
        let mut msgs = Vec::new();
        loop {
            let resp = match self.sync_message(sync_flag).await {
                Ok(resp) => resp,
                Err(_) => {
                    tracing::warn!("failed to sync_message");
                    break;
                }
            };
            if let Err(err) = self
                .delete_message(
                    resp.msgs
                        .iter()
                        .map(|m| {
                            let head = m.head.as_ref().unwrap();
                            pb::MessageItem {
                                from_uin: head.from_uin(),
                                to_uin: head.to_uin(),
                                msg_type: head.msg_type(),
                                msg_seq: head.msg_seq(),
                                msg_uid: head.msg_uid(),
                                ..Default::default()
                            }
                        })
                        .collect(),
                )
                .await
            {
                tracing::warn!("failed to delete_message: {}", err);
                break;
            }
            match resp.msg_rsp_type {
                0 => {
                    let mut engine = self.engine.write().await;
                    if let Some(sync_cookie) = resp.sync_cookie {
                        engine.transport.sig.sync_cookie = Bytes::from(sync_cookie)
                    }
                    if let Some(pub_account_cookie) = resp.pub_account_cookie {
                        engine.transport.sig.pub_account_cookie = Bytes::from(pub_account_cookie)
                    }
                }
                1 => {
                    let mut engine = self.engine.write().await;
                    if let Some(sync_cookie) = resp.sync_cookie {
                        engine.transport.sig.sync_cookie = Bytes::from(sync_cookie)
                    }
                }
                2 => {
                    let mut engine = self.engine.write().await;
                    if let Some(pub_account_cookie) = resp.pub_account_cookie {
                        engine.transport.sig.pub_account_cookie = Bytes::from(pub_account_cookie)
                    }
                }
                _ => {}
            }
            msgs.extend(resp.msgs);
            sync_flag = resp.sync_flag;
            if sync_flag == SYNC_STOP {
                break;
            }
        }
        Ok(msgs)
    }

    // 获取名片信息
    pub async fn get_summary_info(&self, uin: i64) -> RQResult<SummaryCardInfo> {
        let req = self
            .engine
            .read()
            .await
            .build_summary_card_request_packet(uin);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_summary_card_response(resp.body)
    }

    // 准备上传消息,获取 ukey, resid, ip, port
    async fn multi_msg_apply_up(
        &self,
        dst_uin: i64,
        data: &[u8],
        is_long: bool,
    ) -> RQResult<pb::multimsg::MultiMsgApplyUpRsp> {
        let req = self.engine.read().await.build_multi_msg_apply_up_req(
            data.len() as i64,
            md5::compute(data).to_vec(),
            if is_long { 1 } else { 2 },
            dst_uin,
        );
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_multi_msg_apply_up_resp(resp.body)
    }

    // 上传长消息、转发消息 私聊未测试
    pub async fn upload_msgs(
        &self,
        group_code: i64,
        msgs: Vec<ForwardMessage>,
        is_long: bool,
    ) -> RQResult<String> {
        let data = self
            .engine
            .read()
            .await
            .calculate_validation_data(msgs, group_code);
        let rsp = self
            .multi_msg_apply_up(group_code2uin(group_code), &data, is_long)
            .await?;
        let resid = rsp.msg_resid;
        if self.highway_session.read().await.session_key.is_empty() {
            return Err(RQError::EmptyField("highway_session_key is empty"));
        }
        let addrs: Vec<RQAddr> = rsp
            .uint32_up_ip
            .into_iter()
            .zip(rsp.uint32_up_port)
            .map(|(ip, port)| RQAddr(ip as u32, port as u16))
            .collect();
        let body =
            self.engine
                .read()
                .await
                .build_long_req(group_code2uin(group_code), data, rsp.msg_ukey);
        for addr in addrs {
            match self
                .highway_upload_bdh(
                    addr.into(),
                    BdhInput {
                        command_id: 27,
                        ticket: rsp.msg_sig.clone(),
                        chunk_size: 8192 * 8,
                        ..Default::default()
                    },
                    &body,
                )
                .await
            {
                Ok(_) => return Ok(resid),
                Err(_) => continue,
            }
        }
        Err(RQError::Other("failed to upload long message".into()))
    }

    // 获取转发消息下载地址和 key
    async fn multi_msg_apply_down(
        &self,
        res_id: String,
    ) -> RQResult<pb::multimsg::MultiMsgApplyDownRsp> {
        let req = self
            .engine
            .read()
            .await
            .build_multi_msg_apply_down_req(res_id);
        let resp = self.send_and_wait(req).await?;
        self.engine
            .read()
            .await
            .decode_multi_msg_apply_down_resp(resp.body)
    }

    pub async fn download_msgs(&self, res_id: String) -> RQResult<Vec<ForwardMessage>> {
        let mut resp = self.multi_msg_apply_down(res_id).await?;
        if resp.result != 0 {
            return Err(RQError::Other(format!(
                "multi_msg_apply_down result {}",
                resp.result
            )));
        }
        let prefix=if let Some(pb::multimsg::ExternMsg { channel_type }) = resp.msg_extern_info && channel_type == 2 {
            "https://ssl.htdata.qq.com".into()
        } else {
            let addr = SocketAddr::from(RQAddr(resp.down_ip.pop().ok_or(RQError::EmptyField("down_ip"))?,resp.down_port.pop().ok_or(RQError::EmptyField("down_port"))? as u16));
            format!("http://{addr}")
        };
        let _url = format!(
            "{}{}",
            prefix,
            String::from_utf8_lossy(&resp.thumb_down_para)
        );
        let _encrypt_key = resp.msg_key;
        // TODO get data and decrypt
        // TODO decoder -> LongRspBody
        // TODO uncompress
        // TODO link message, convert to Vec<ForwardMessage>
        todo!()
    }

    /// 发送消息
    pub async fn send_message(
        &self,
        routing_head: pb::msg::routing_head::RoutingHead,
        message_chain: MessageChain,
        ptt: Option<pb::msg::Ptt>,
    ) -> RQResult<MessageReceipt> {
        let time = UNIX_EPOCH.elapsed().unwrap().as_secs() as i64;
        let seq = self.engine.read().await.next_friend_seq();
        let ran = (rand::random::<u32>() >> 1) as i32;
        let (tx, _) = tokio::sync::oneshot::channel();
        {
            self.receipt_waiters.lock().await.cache_set(ran, tx);
        }
        let req = self.engine.read().await.build_send_message_packet(
            routing_head,
            message_chain.into(),
            ptt,
            seq,
            ran,
            time,
        );
        self.send_and_wait(req).await?;
        let receipt = MessageReceipt {
            seqs: vec![seq],
            rands: vec![ran],
            time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i64,
        };
        // 除了群聊,都不需要等 receipt 的 seq
        Ok(receipt)
    }
}


================================================
FILE: ricq/src/client/event.rs
================================================
use std::sync::Arc;

use ricq_core::command::profile_service::{JoinGroupRequest, NewFriendRequest, SelfInvited};
use ricq_core::structs::{
    DeleteFriend, FriendAudioMessage, FriendInfo, FriendMessageRecall, FriendPoke,
    GroupAudioMessage, GroupDisband, GroupLeave, GroupMessageRecall, GroupMute, GroupNameUpdate,
    GroupPoke, GroupTempMessage, MemberPermissionChange, NewMember,
};
use ricq_core::{jce, RQResult};

use crate::client::NetworkStatus;
use crate::structs::{FriendMessage, GroupMessage};
use crate::Client;

#[derive(Clone, derivative::Derivative)]
#[derivative(Debug)]
pub struct EventWithClient<T> {
    #[derivative(Debug = "ignore")]
    pub client: Arc<Client>,
    pub inner: T,
}

pub type GroupMessageEvent = EventWithClient<GroupMessage>;

impl GroupMessageEvent {
    pub async fn recall(&self) -> RQResult<()> {
        // TODO check permission
        self.client
            .recall_group_message(
                self.inner.group_code,
                self.inner.seqs.clone(),
                self.inner.rands.clone(),
            )
            .await
    }
}

pub type FriendMessageEvent = EventWithClient<FriendMessage>;
pub type GroupTempMessageEvent = EventWithClient<GroupTempMessage>;
pub type JoinGroupRequestEvent = EventWithClient<JoinGroupRequest>;

impl JoinGroupRequestEvent {
    pub async fn accept(&self) -> RQResult<()> {
        self.client
            .solve_group_system_message(
                self.inner.msg_seq,
                self.inner.req_uin,
                self.inner.group_code,
                self.inner.suspicious,
                self.inner.invitor_uin.is_some(),
                true,
                false,
                "".into(),
            )
            .await
    }

    pub async fn reject(&self, reason: String, block: bool) -> RQResult<()> {
        self.client
            .solve_group_system_message(
                self.inner.msg_seq,
                self.inner.req_uin,
                self.inner.group_code,
                self.inner.suspicious,
                self.inner.invitor_uin.is_some(),
                false,
                block,
                reason,
            )
            .await
    }
}

pub type NewFriendRequestEvent = EventWithClient<NewFriendRequest>;

impl NewFriendRequestEvent {
    pub async fn accept(&self) -> RQResult<()> {
        self.client
            .solve_friend_system_message(self.inner.msg_seq, self.inner.req_uin, true)
            .await
    }

    pub async fn reject(&self) -> RQResult<()> {
        self.client
            .solve_friend_system_message(self.inner.msg_seq, self.inner.req_uin, false)
            .await
    }
}

pub type NewMemberEvent = EventWithClient<NewMember>;
pub type GroupMuteEvent = EventWithClient<GroupMute>;
pub type FriendMessageRecallEvent = EventWithClient<FriendMessageRecall>;
pub type GroupMessageRecallEvent = EventWithClient<GroupMessageRecall>;
pub type NewFriendEvent = EventWithClient<FriendInfo>;
pub type GroupLeaveEvent = EventWithClient<GroupLeave>;
pub type GroupDisbandEvent = EventWithClient<GroupDisband>;
pub type FriendPokeEvent = EventWithClient<FriendPoke>;
pub type GroupPokeEvent = EventWithClient<GroupPoke>;
pub type GroupNameUpdateEvent = EventWithClient<GroupNameUpdate>;
pub type DeleteFriendEvent = EventWithClient<DeleteFriend>;
pub type MemberPermissionChangeEvent = EventWithClient<MemberPermissionChange>;
pub type SelfInvitedEvent = EventWithClient<SelfInvited>;
pub type GroupAudioMessageEvent = EventWithClient<GroupAudioMessage>;

impl GroupAudioMessageEvent {
    pub async fn url(&self) -> RQResult<String> {
        self.client
            .get_group_audio_url(self.inner.group_code, self.inner.audio.clone())
            .await
    }
}

pub type FriendAudioMessageEvent = EventWithClient<FriendAudioMessage>;

impl FriendAudioMessageEvent {
    pub async fn url(&self) -> RQResult<String> {
        self.client
            .get_friend_audio_url(self.inner.from_uin, self.inner.audio.clone())
            .await
    }
}

pub type KickedOfflineEvent = EventWithClient<jce::RequestPushForceOffline>;
pub type MSFOfflineEvent = EventWithClient<jce::RequestMSFForceOffline>;

#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum DisconnectReason {
    /// 主动断开
    Actively(NetworkStatus),
    /// 网络原因
    Network,
}

impl DisconnectReason {
    /// 客户端网络状态
    pub fn status(&self) -> NetworkStatus {
        match self {
            Self::Actively(s) => *s,
            Self::Network => NetworkStatus::NetworkOffline,
        }
    }
}

pub type ClientDisconnect = EventWithClient<DisconnectReason>;

impl ClientDisconnect {
    pub fn reason(&self) -> DisconnectReason {
        self.inner
    }
}


================================================
FILE: ricq/src/client/handler/mod.rs
================================================
use std::future::Future;
use std::pin::Pin;

use async_trait::async_trait;
use tokio::sync::{
    broadcast::Sender as BroadcastSender,
    mpsc::{Sender as MpscSender, UnboundedSender},
    watch::Sender as WatchSender,
};

use crate::client::event::*;

/// 所有需要外发的数据的枚举打包
#[derive(Clone, derivative::Derivative)]
#[derivative(Debug)]
pub enum QEvent {
    /// 登录成功事件
    Login(i64),
    /// 群消息
    GroupMessage(GroupMessageEvent),
    /// 群语音
    GroupAudioMessage(GroupAudioMessageEvent),
    /// 好友消息
    FriendMessage(FriendMessageEvent),
    /// 群语音
    FriendAudioMessage(FriendAudioMessageEvent),
    /// 群临时消息
    GroupTempMessage(GroupTempMessageEvent),
    /// 加群申请
    GroupRequest(JoinGroupRequestEvent),
    /// 加群申请
    SelfInvited(SelfInvitedEvent),
    /// 加好友申请
    NewFriendRequest(NewFriendRequestEvent),
    /// 新成员入群
    NewMember(NewMemberEvent),
    /// 成员被禁言
    GroupMute(GroupMuteEvent),
    /// 好友消息撤回
    FriendMessageRecall(FriendMessageRecallEvent),
    /// 群消息撤回
    GroupMessageRecall(GroupMessageRecallEvent),
    /// 新好友
    NewFriend(NewFriendEvent),
    /// 退群/被踢
    GroupLeave(GroupLeaveEvent),
    /// 群解散
    GroupDisband(GroupDisbandEvent),
    /// 好友戳一戳
    FriendPoke(FriendPokeEvent),
    /// 群成员戳一戳
    GroupPoke(GroupPokeEvent),
    /// 群名称修改
    GroupNameUpdate(GroupNameUpdateEvent),
    /// 好友删除
    DeleteFriend(DeleteFriendEvent),
    /// 群成员权限变更
    MemberPermissionChange(MemberPermissionChangeEvent),
    /// 被其他客户端踢下线
    /// 不能用于掉线重连,掉线重连以 start 返回为准
    KickedOffline(KickedOfflineEvent),
    /// 服务端强制下线
    /// 不能用于掉线重连,掉线重连以 start 返回为准
    MSFOffline(MSFOfflineEvent),
    /// 网络原因/客户端主动掉线
    /// 可用于掉线重连
    ClientDisconnect(ClientDisconnect),
}

/// 处理外发数据的接口
///
/// 同时,所有 `async fn(QEvent)` 都已自动实现 `Handler`。
///
/// # Examples
///
/// 你可以为自己的 struct 实现 Handler:
///
/// ```ignore
/// struct MyHandler;
/// impl Handler for MyHandler { ... }
/// ```
///
/// 或者只定义单个事件处理函数,更简洁:
///
/// ```
/// # use ricq::{Client, Device, Protocol, handler::QEvent};
/// # fn test(device: Device) {
/// async fn on_event(e: QEvent) {
///     dbg!(e);
/// }
/// let client = Client::new(
///     device,
///     Protocol::MacOS.into(),
///     on_event as fn(_) -> _,
/// );
/// # }
/// ```
#[async_trait]
pub trait Handler: Sync {
    async fn handle(&self, event: QEvent);
}

// 这里还有一种 Fn(QEvent) -> Fut 的写法,但是会与 PartlyHandler 冲突
impl<Fut> Handler for fn(QEvent) -> Fut
where
    Fut: Future<Output = ()> + Send,
{
    fn handle<'a: 'b, 'b>(&'a self, e: QEvent) -> Pin<Box<dyn Future<Output = ()> + Send + 'b>> {
        Box::pin(async move { self(e).await })
    }
}

/// 一个默认 Handler,只是把信息打印出来
pub struct DefaultHandler;

#[async_trait]
impl Handler for DefaultHandler {
    async fn handle(&self, e: QEvent) {
        match e {
            QEvent::GroupMessage(m) => {
                tracing::info!(
                    "MESSAGE (GROUP={}): {}",
                    m.inner.group_code,
                    m.inner.elements
                )
            }
            QEvent::FriendMessage(m) => {
                tracing::info!(
                    "MESSAGE (FRIEND={}): {}",
                    m.inner.from_uin,
                    m.inner.elements
                )
            }
            QEvent::GroupTempMessage(m) => {
                tracing::info!("MESSAGE (TEMP={}): {}", m.inner.from_uin, m.inner.elements)
            }
            QEvent::GroupRequest(m) => {
                tracing::info!(
                    "REQUEST (GROUP={}, UIN={}): {}",
                    m.inner.group_code,
                    m.inner.req_uin,
                    m.inner.message
                )
            }
            QEvent::NewFriendRequest(m) => {
                tracing::info!("REQUEST (UIN={}): {}", m.inner.req_uin, m.inner.message)
            }
            _ => tracing::info!("{:?}", e),
        }
    }
}

#[async_trait]
impl Handler for BroadcastSender<QEvent> {
    async fn handle(&self, msg: QEvent) {
        self.send(msg).ok();
    }
}

#[async_trait]
impl Handler for MpscSender<QEvent> {
    async fn handle(&self, msg: QEvent) {
        self.send(msg).await.ok();
    }
}

#[async_trait]
impl Handler for UnboundedSender<QEvent> {
    async fn handle(&self, msg: QEvent) {
        self.send(msg).ok();
    }
}

#[async_trait]
impl Handler for WatchSender<QEvent> {
    async fn handle(&self, msg: QEvent) {
        self.send(msg).ok();
    }
}

#[async_trait]
pub trait PartlyHandler: Sync {
    async fn handle_login(&self, _: i64) {}
    async fn handle_group_message(&self, _event: GroupMessageEvent) {}
    async fn handle_group_audio(&self, _event: GroupAudioMessageEvent) {}
    async fn handle_friend_message(&self, _event: FriendMessageEvent) {}
    async fn handle_friend_audio(&self, _event: FriendAudioMessageEvent) {}
    async fn handle_group_temp_message(&self, _event: GroupTempMessageEvent) {}
    async fn handle_group_request(&self, _event: JoinGroupRequestEvent) {}
    async fn handle_self_invited(&self, _event: SelfInvitedEvent) {}
    async fn handle_friend_request(&self, _event: NewFriendRequestEvent) {}
    async fn handle_new_member(&self, _event: NewMemberEvent) {}
    async fn handle_group_mute(&self, _event: GroupMuteEvent) {}
    async fn handle_friend_message_recall(&self, _event: FriendMessageRecallEvent) {}
    async fn handle_group_message_recall(&self, _event: GroupMessageRecallEvent) {}
    async fn handle_new_friend(&self, _event: NewFriendEvent) {}
    async fn handle_group_leave(&self, _event: GroupLeaveEvent) {}
    async fn handle_group_disband(&self, _event: GroupDisbandEvent) {}
    async fn handle_friend_poke(&self, _event: FriendPokeEvent) {}
    async fn handle_group_poke(&self, _event: GroupPokeEvent) {}
    async fn handle_group_name_update(&self, _event: GroupNameUpdateEvent) {}
    async fn handle_delete_friend(&self, _event: DeleteFriendEvent) {}
    async fn handle_member_permission_change(&self, _event: MemberPermissionChangeEvent) {}
    async fn handle_kicked_offline(&self, _event: KickedOfflineEvent) {}
    async fn handle_msf_offline(&self, _event: MSFOfflineEvent) {}
    async fn handle_client_disconnect(&self, _event: ClientDisconnect) {}
}

#[async_trait]
impl<PH> Handler for PH
where
    PH: PartlyHandler,
{
    async fn handle(&self, event: QEvent) {
        match event {
            QEvent::Login(uin) => self.handle_login(uin).await,
            QEvent::GroupMessage(m) => self.handle_group_message(m).await,
            QEvent::GroupAudioMessage(m) => self.handle_group_audio(m).await,
            QEvent::FriendMessage(m) => self.handle_friend_message(m).await,
            QEvent::FriendAudioMessage(m) => self.handle_friend_audio(m).await,
            QEvent::GroupTempMessage(m) => self.handle_group_temp_message(m).await,
            QEvent::GroupRequest(m) => self.handle_group_request(m).await,
            QEvent::SelfInvited(m) => self.handle_self_invited(m).await,
            QEvent::NewFriendRequest(m) => self.handle_friend_request(m).await,
            QEvent::NewMember(m) => self.handle_new_member(m).await,
            QEvent::GroupMute(m) => self.handle_group_mute(m).await,
            QEvent::FriendMessageRecall(m) => self.handle_friend_message_recall(m).await,
            QEvent::GroupMessageRecall(m) => self.handle_group_message_recall(m).await,
            QEvent::NewFriend(m) => self.handle_new_friend(m).await,
            QEvent::GroupLeave(m) => self.handle_group_leave(m).await,
            QEvent::GroupDisband(m) => self.handle_group_disband(m).await,
            QEvent::FriendPoke(m) => self.handle_friend_poke(m).await,
            QEvent::GroupPoke(m) => self.handle_group_poke(m).await,
            QEvent::GroupNameUpdate(m) => self.handle_group_name_update(m).await,
            QEvent::DeleteFriend(m) => self.handle_delete_friend(m).await,
            QEvent::MemberPermissionChange(m) => self.handle_member_permission_change(m).await,
            QEvent::KickedOffline(m) => self.handle_kicked_offline(m).await,
            QEvent::MSFOffline(m) => self.handle_msf_offline(m).await,
            QEvent::ClientDisconnect(m) => self.handle_client_disconnect(m).await,
        }
    }
}


================================================
FILE: ricq/src/client/highway/codec.rs
================================================
use bytes::{Buf, BufMut, BytesMut};
use tokio_util::codec::{Decoder, Encoder};

use ricq_core::RQError;

use crate::client::highway::HighwayFrame;

pub struct HighwayCodec;

impl Encoder<HighwayFrame> for HighwayCodec {
    type Error = RQError;

    fn encode(&mut self, item: HighwayFrame, dst: &mut BytesMut) -> Result<(), Self::Error> {
        dst.put_u8(40);
        dst.put_u32(item.head.len() as u32);
        dst.put_u32(item.body.len() as u32);
        dst.put_slice(&item.head);
        dst.put_slice(&item.body);
        dst.put_u8(41);
        Ok(())
    }
}

impl Decoder for HighwayCodec {
    type Item = HighwayFrame;
    type Error = RQError;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        if src.len() < 10 {
            return Ok(None);
        }
        src.get_u8();
        let head_length = src.get_u32() as usize;
        let body_length = src.get_u32() as usize;
        if head_length + body_length + 1 > src.remaining() {
            return Ok(None);
        }
        let head = src.copy_to_bytes(head_length);
        let body = src.copy_to_bytes(body_length);
        src.get_u8();
        Ok(Some(Self::Item { head, body }))
    }
}


================================================
FILE: ricq/src/client/highway/mod.rs
================================================
use bytes::Bytes;

mod codec;
mod net;

pub struct HighwayFrame {
    pub head: Bytes,
    pub body: Bytes,
}


================================================
FILE: ricq/src/client/highway/net.rs
================================================
use std::net::SocketAddr;
use std::time::Duration;

use bytes::Bytes;
use futures_util::{SinkExt, StreamExt};
use tokio::net::TcpStream;
use tokio_util::codec::Framed;

use ricq_core::command::common::PbToBytes;
use ricq_core::crypto::qqtea_encrypt;
use ricq_core::highway::BdhInput;
use ricq_core::{pb, RQError, RQResult};

use crate::client::highway::codec::HighwayCodec;
use crate::client::highway::HighwayFrame;
use crate::client::tcp::tcp_connect_timeout;
use crate::Client;

impl Client {
    pub async fn highway_upload_bdh(
        &self,
        addr: SocketAddr,
        mut input: BdhInput,
        data: &[u8],
    ) -> RQResult<Bytes> {
        if input.encrypt {
            let session_key = self.highway_session.read().await.session_key.clone();
            input.ext = qqtea_encrypt(&input.ext, &session_key)
        }
        let stream = tcp_connect_timeout(addr, Duration::from_secs(5))
            .await
            .map_err(RQError::IO)?;
        let mut stream = Framed::new(stream, HighwayCodec);
        // send heartbeat
        let sum = md5::compute(data).to_vec();
        let length = data.len();

        if input.send_echo {
            stream
                .send(HighwayFrame {
                    head: self.highway_session.read().await.build_heartbreak(),
                    body: Bytes::new(),
                })
                .await?;
            let _ = read_response(&mut stream).await?;
        }
        let mut ticket = input.ticket;
        let mut rsp_ext = Bytes::new();
        let data = Bytes::copy_from_slice(data);
        let len = data.len();
        let chunk_size = input.chunk_size;

        for i in (0..len).step_by(chunk_size) {
            let min = std::cmp::min(i + chunk_size, len);
            let chunk = data.slice(i..min);
            let head = pb::ReqDataHighwayHead {
                msg_basehead: Some(self.highway_session.read().await.build_basehead(
                    "PicUp.DataUp".into(),
                    4096,
                    input.command_id,
                    2052,
                )),
                msg_seghead: Some(self.highway_session.read().await.build_seghead(
                    length as i64,
                    i as i64,
                    &chunk,
                    ticket.clone(),
                    sum.clone(),
                )),
                req_extendinfo: input.ext.clone(),
                ..Default::default()
            };
            stream
                .send(HighwayFrame {
                    head: head.to_bytes(),
                    body: chunk,
                })
                .await?;
            let resp = read_response(&mut stream).await?;
            let rsp_head = self
                .highway_session
                .read()
                .await
                .decode_rsp_head(resp.head)?;
            if rsp_head.error_code != 0 {
                return Err(RQError::Other(format!(
                    "error_code = {}",
                    rsp_head.error_code
                )));
            }
            if !rsp_head.rsp_extendinfo.is_empty() {
                rsp_ext = Bytes::from(rsp_head.rsp_extendinfo)
            }
            if let Some(h) = rsp_head.msg_seghead {
                if !h.serviceticket.is_empty() {
                    ticket = h.serviceticket
                }
            }
        }

        Ok(rsp_ext)
    }
}

async fn read_response(stream: &mut Framed<TcpStream, HighwayCodec>) -> RQResult<HighwayFrame> {
    loop {
        if let Some(resp) = stream.next().await {
            return resp;
        }
    }
}


================================================
FILE: ricq/src/client/mod.rs
================================================
use bytes::Bytes;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU8, Ordering};
use std::sync::Arc;
use std::time::UNIX_EPOCH;

use cached::Cached;
use futures_util::StreamExt;
use tokio::sync::{broadcast, RwLock};
use tokio::sync::{oneshot, Mutex};
use tokio::time::{sleep, Duration};

pub use net::{Connector, DefaultConnector};
use ricq_core::command::common::PbToBytes;
use ricq_core::command::online_push::GroupMessagePart;
use ricq_core::command::profile_service::GroupSystemMessages;
use ricq_core::common::RQAddr;
use ricq_core::hex::decode_hex;
use ricq_core::protocol::version::Version;
use ricq_core::protocol::{device::Device, packet::Packet};
use ricq_core::structs::{AccountInfo, AddressInfo, OtherClientInfo};
use ricq_core::Engine;
pub use ricq_core::Token;

use crate::qsign::{QSignClient, QSignResponse, RequestCallback, SignData};
use crate::{RQError, RQResult};

mod api;
pub mod event;
pub mod handler;
mod highway;
pub(crate) mod net;
mod processor;
pub mod qimei;
mod tcp;

const SIGN_COMMANDS: &str = r#"ConnAuthSvr.fast_qq_login
ConnAuthSvr.sdk_auth_api
ConnAuthSvr.sdk_auth_api_emp
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoBarrage
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoComment
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoFollow
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoLike
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoPush
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoReply
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.PublishFeed
FeedCloudSvr.trpc.videocircle.circleprofile.CircleProfile.SetProfile
friendlist.addFriend
friendlist.AddFriendReq
friendlist.ModifyGroupInfoReq
MessageSvc.PbSendMsg
MsgProxy.SendMsg
OidbSvc.0x4ff_9
OidbSvc.0x4ff_9_IMCore
OidbSvc.0x56c_6
OidbSvc.0x6d9_4
OidbSvc.0x758
OidbSvc.0x758_0
OidbSvc.0x758_1
OidbSvc.0x88d_0
OidbSvc.0x89a_0
OidbSvc.0x89b_1
OidbSvc.0x8a1_0
OidbSvc.0x8a1_7
OidbSvc.0x8ba
OidbSvc.0x9fa
OidbSvc.oidb_0x758
OidbSvcTrpcTcp.0x101e_1
OidbSvcTrpcTcp.0x101e_2
OidbSvcTrpcTcp.0x1100_1
OidbSvcTrpcTcp.0x1105_1
OidbSvcTrpcTcp.0x1107_1
OidbSvcTrpcTcp.0x55f_0
OidbSvcTrpcTcp.0x6d9_4
OidbSvcTrpcTcp.0xf55_1
OidbSvcTrpcTcp.0xf57_1
OidbSvcTrpcTcp.0xf57_106
OidbSvcTrpcTcp.0xf57_9
OidbSvcTrpcTcp.0xf65_1
OidbSvcTrpcTcp.0xf65_10 
OidbSvcTrpcTcp.0xf67_1
OidbSvcTrpcTcp.0xf67_5
OidbSvcTrpcTcp.0xf6e_1
OidbSvcTrpcTcp.0xf88_1
OidbSvcTrpcTcp.0xf89_1
OidbSvcTrpcTcp.0xfa5_1
ProfileService.getGroupInfoReq
ProfileService.GroupMngReq
QChannelSvr.trpc.qchannel.commwriter.ComWriter.DoComment
QChannelSvr.trpc.qchannel.commwriter.ComWriter.DoReply
QChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed
qidianservice.135
qidianservice.207
qidianservice.269
qidianservice.290
SQQzoneSvc.addComment
SQQzoneSvc.addReply
SQQzoneSvc.forward
SQQzoneSvc.like
SQQzoneSvc.publishmood
SQQzoneSvc.shuoshuo
trpc.group_pro.msgproxy.sendmsg
trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLoginUnusualDevice
trpc.o3.ecdh_access.EcdhAccess.SsoEstablishShareKey
trpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Access
trpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Establish
trpc.o3.ecdh_access.EcdhAccess.SsoSecureAccess
trpc.o3.report.Report.SsoReport
trpc.passwd.manager.PasswdManager.SetPasswd
trpc.passwd.manager.PasswdManager.VerifyPasswd
trpc.qlive.relationchain_svr.RelationchainSvr.Follow
trpc.qlive.word_svr.WordSvr.NewPublicChat
trpc.qqhb.qqhb_proxy.Handler.sso_handle
trpc.springfestival.redpacket.LuckyBag.SsoSubmitGrade
wtlogin.device_lock
wtlogin.exchange_emp
wtlogin.login
wtlogin.name2uin
wtlogin.qrlogin
wtlogin.register
wtlogin.trans_emp
wtlogin_device.login
wtlogin_device.tran_sim_emp"#;

pub struct Client {
    /// QEvent Handler 调用 handle 方法外发 QEvent
    handler: Box<dyn handler::Handler + Sync + Send + 'static>,
    pub engine: RwLock<Engine>,

    // 状态相关
    /// 网络状态
    status: AtomicU8,
    /// 停止网络信号 Sender
    disconnect_signal: broadcast::Sender<()>,
    /// 是否在线
    pub online: AtomicBool,
    /// 心跳包是否已启用
    pub heartbeat_enabled: AtomicBool,

    // 包相关
    /// 外发包 Sender
    out_pkt_sender: net::OutPktSender,
    /// send_and_wait WaitMap
    packet_promises: RwLock<HashMap<i32, oneshot::Sender<Packet>>>,
    /// 当前客户端发送消息后使用 cache 避免上报自身消息事件
    receipt_waiters: Mutex<cached::TimedCache<i32, oneshot::Sender<i32>>>,

    // account info
    pub account_info: RwLock<AccountInfo>,

    // address
    pub address: RwLock<AddressInfo>,
    /// 其他同时在线客户端
    pub online_clients: RwLock<Vec<OtherClientInfo>>,

    // statics
    pub last_message_time: AtomicI64,
    /// 调用 new 方法时的时间戳
    pub start_time: i32,

    /// 群消息 builder 寄存 <div_seq, parts> : parts is sorted by pkg_index
    group_message_builder: RwLock<cached::TimedCache<i32, Vec<GroupMessagePart>>>,
    /// 每个 28 Byte
    c2c_cache: RwLock<cached::TimedCache<(i64, i64, i32, i64), ()>>,
    push_req_cache: RwLock<cached::TimedCache<(i16, i64), ()>>,
    push_trans_cache: RwLock<cached::TimedCache<(i32, i64), ()>>,
    group_sys_message_cache: RwLock<GroupSystemMessages>,

    pub highway_session: RwLock<ricq_core::highway::Session>,
    pub highway_addrs: RwLock<Vec<RQAddr>>,

    packet_handler: RwLock<HashMap<String, broadcast::Sender<Packet>>>,
    pub qsign_client: Arc<QSignClient>,
}

impl super::Client {
    /// 新建 Clinet
    ///
    /// **Notice: 该方法仅新建 Client 需要调用 start 方法连接到服务器**
    pub fn new<H>(
        device: Device,
        version: Version,
        qsign_client: Arc<QSignClient>,
        handler: H,
    ) -> Client
    where
        H: crate::client::handler::Handler + 'static + Sync + Send,
    {
        let (out_pkt_sender, _) = tokio::sync::broadcast::channel(1024);
        let (disconnect_signal, _) = tokio::sync::broadcast::channel(8);

        Client {
            handler: Box::new(handler),
            engine: RwLock::new(Engine::new(device, version)),
            status: AtomicU8::new(NetworkStatus::Unknown as u8),
            heartbeat_enabled: AtomicBool::new(false),
            online: AtomicBool::new(false),
            out_pkt_sender,
            disconnect_signal,
            // out_going_packet_session_id: RwLock::new(Bytes::from_static(&[0x02, 0xb0, 0x5b, 0x8b])),
            packet_promises: Default::default(),
            receipt_waiters: Mutex::new(cached::TimedCache::with_lifespan(60)),
            account_info: Default::default(),
            address: Default::default(),
            online_clients: Default::default(),
            last_message_time: Default::default(),
            start_time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i32,
            group_message_builder: RwLock::new(cached::TimedCache::with_lifespan(600)),
            c2c_cache: RwLock::new(cached::TimedCache::with_lifespan(3600)),
            push_req_cache: RwLock::new(cached::TimedCache::with_lifespan(30)),
            push_trans_cache: RwLock::new(cached::TimedCache::with_lifespan(15)),
            group_sys_message_cache: RwLock::new(Default::default()),
            highway_session: RwLock::new(Default::default()),
            highway_addrs: RwLock::new(Default::default()),
            packet_handler: Default::default(),
            qsign_client,
        }
    }

    /// 新建 Clinet
    ///
    /// **Notice: 该方法仅新建 Client 需要调用 start 方法连接到服务器**
    pub fn new_with_config<H>(
        config: crate::Config,
        qsign_client: Arc<QSignClient>,
        handler: H,
    ) -> Self
    where
        H: crate::client::handler::Handler + 'static + Sync + Send,
    {
        Self::new(config.device, config.version, qsign_client, handler)
    }

    /// 获取当前 Client uin
    pub async fn uin(&self) -> i64 {
        self.engine.read().await.uin.load(Ordering::Relaxed)
    }

    pub async fn sign_packet(&self, pkt: &mut Packet) -> RQResult<QSignResponse<SignData>> {
        if !SIGN_COMMANDS.contains(&pkt.command_name) {
            return Ok(Default::default());
        }
        let engine = self.engine.read().await;
        let resp = self
            .qsign_client
            .sign(
                pkt.uin,
                engine.transport.version.qua,
                &pkt.command_name,
                pkt.seq_id,
                &pkt.body,
                &engine
                    .transport
                    .device
                    .qimei
                    .as_ref()
                    .map(|qimei| qimei.q36.as_str())
                    .unwrap_or_default(),
                &engine.transport.device.android_id,
                &engine.transport.sig.guid,
            )
            .await
            .map_err(|err| RQError::Other(format!("failed to sign packet: {err}")))?;
        if resp.code != 0 {
            return Err(RQError::Other(format!(
                "failed to sign packet, msg: {}",
                resp.msg
            )));
        }
        let sign = ricq_core::pb::SsoReserveField {
            flag: 0,
            qimei: engine
                .transport
                .device
                .qimei
                .clone()
                .unwrap_or_default()
                .q16,
            newconn_flag: 0,
            uid: pkt.uin.to_string(),
            imsi: 0,
            network_type: 1,
            ip_stack_type: 1,
            message_type: 0,
            sec_info: Some(ricq_core::pb::SsoSecureInfo {
                sec_sig: decode_hex(&resp.data.sign).unwrap_or_default(),
                sec_device_token: decode_hex(&resp.data.token).unwrap_or_default(),
                sec_extra: decode_hex(&resp.data.extra).unwrap_or_default(),
            }),
            sso_ip_origin: 0,
        }
        .to_bytes();
        pkt.sign = Some(sign);
        Ok(resp)
    }

    pub async fn process_sign_callback(&self, callbacks: Vec<RequestCallback>) {
        let callbacks: Vec<(i64, Packet)> = {
            let engine = self.engine.read().await;
            callbacks
                .into_iter()
                .map(|cb| {
                    (
                        cb.callback_id,
                        engine.uni_packet(
                            &cb.cmd,
                            Bytes::from(decode_hex(&cb.body).unwrap_or_default()),
                        ),
                    )
                })
                .collect()
        };
        let _: Vec<_> = futures_util::stream::iter(callbacks)
            .map(|(id, pkt)| async move {
                let uin = pkt.uin;
                let cmd = pkt.command_name.clone();
                let resp = self.send_and_wait(pkt).await;
                if let Err(ref err) = resp {
                    tracing::error!(
                        "failed to process sign callback, id: {id}, cmd: {cmd}, err: {err}"
                    )
                }
                let resp = resp.unwrap_or_default();
                if let Err(err) = self.qsign_client.submit(uin, &cmd, id, &resp.body).await {
                    tracing::error!("failed to submit sign callback, err: {err}")
                }
            })
            .buffered(10)
            .collect()
            .await;
    }

    /// 向服务器发包
    pub async fn send(&self, pkt: Packet) -> RQResult<usize> {
        tracing::trace!("sending pkt {}-{},", pkt.command_name, pkt.seq_id);
        let data = self.engine.read().await.transport.encode_packet(pkt);
        self.out_pkt_sender
            .send(data)
            .map_err(|_| RQError::Other("failed to send out_pkt".into()))
    }

    /// 向服务器发包并等待接收返回的包,15 秒后超时返回 `Err(RQError::Timeout)`
    #[async_recursion::async_recursion]
    pub async fn send_and_wait(&self, mut pkt: Packet) -> RQResult<Packet> {
        let callbacks = self.sign_packet(&mut pkt).await;
        if let Err(ref err) = callbacks {
            tracing::error!("failed to sign packet, err: {err}");
        }
        let callbacks = callbacks.unwrap_or_default().data.request_callback;
        let callback_future = self.process_sign_callback(callbacks);

        tracing::trace!("send_and_waitting pkt {}-{},", pkt.command_name, pkt.seq_id);
        let seq = pkt.seq_id;
        let expect = pkt.command_name.clone();
        let data = self.engine.read().await.transport.encode_packet(pkt);
        let (sender, receiver) = oneshot::channel();
        {
            let mut packet_promises = self.packet_promises.write().await;
            packet_promises.insert(seq, sender);
        }
        if self.out_pkt_sender.send(data).is_err() {
            let mut packet_promises = self.packet_promises.write().await;
            packet_promises.remove(&seq);
            return Err(RQError::Network);
        }
        let packet_future = tokio::time::timeout(std::time::Duration::from_secs(15), receiver);

        let (resp, _) = tokio::join!(packet_future, callback_future);
        match resp {
            Ok(p) => p.unwrap().check_command_name(&expect),
            Err(_) => {
                tracing::trace!("waiting pkt {}-{} timeout", expect, seq);
                self.packet_promises.write().await.remove(&seq);
                Err(RQError::Timeout)
            }
        }
    }

    /// 向服务器发送心跳包,并自动注册客户端
    ///
    /// 该方法会阻塞当前协程,通常 spawn 使用
    pub async fn do_heartbeat(&self) {
        self.heartbeat_enabled.store(true, Ordering::SeqCst);
        let mut times = 0;
        while self.online.load(Ordering::SeqCst) {
            sleep(Duration::from_secs(30)).await;
            if self.heartbeat().await.is_ok() {
                times += 1;
                if times >= 7 {
                    if self.register_client().await.is_err() {
                        break;
                    }
                    times = 0;
                }
            }
        }
        self.heartbeat_enabled.store(false, Ordering::SeqCst);
    }

    /// 生成 token
    pub async fn gen_token(&self) -> Token {
        self.engine.read().await.gen_token()
    }

    /// 从 token 恢复
    pub async fn load_token(&self, token: Token) {
        self.engine.write().await.load_token(token)
    }

    pub async fn device(&self) -> Device {
        self.engine.read().await.transport.device.clone()
    }

    pub async fn version(&self) -> Version {
        self.engine.read().await.transport.version.clone()
    }

    pub async fn get_highway_session_key(&self) -> Vec<u8> {
        self.highway_session.read().await.session_key.to_vec()
    }

    /// 监听指定 command 数据包
    pub async fn listen_command<S: ToString>(&self, command: S) -> broadcast::Receiver<Packet> {
        self.packet_handler
            .write()
            .await
            .cache_get_or_set_with(command.to_string(), || broadcast::channel(10).0)
            .subscribe()
    }
}

impl Drop for Client {
    fn drop(&mut self) {
        self.stop(NetworkStatus::Drop);
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum NetworkStatus {
    // 未启动
    Unknown = 0,
    // 运行中
    Running = 1,
    // 用户手动停止
    Stop = 2,
    // 内存释放
    Drop = 3,
    // 网络原因掉线
    NetworkOffline = 4,
    // 其他客户端踢下线
    KickedOffline = 5,
    // 服务端强制下线
    MsfOffline = 6,
}


================================================
FILE: ricq/src/client/net.rs
================================================
use std::net::SocketAddr;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;

use crate::client::event::{ClientDisconnect, DisconnectReason};
use async_trait::async_trait;
use bytes::Bytes;
use futures_util::{SinkExt, StreamExt};
use tokio::io::{self, AsyncRead, AsyncWrite};
use tokio::net::TcpStream;
use tokio::sync::broadcast;
use tokio_util::codec::LengthDelimitedCodec;

use crate::client::tcp::tcp_connect_fastest;
use crate::client::NetworkStatus;
use crate::handler::QEvent;

use super::Client;

pub type OutPktSender = broadcast::Sender<Bytes>;

#[async_trait]
pub trait Connector<T: AsyncRead + AsyncWrite> {
    async fn connect(&self, client: &Client) -> io::Result<T>;
}

pub struct DefaultConnector;

#[async_trait]
impl Connector<TcpStream> for DefaultConnector {
    async fn connect(&self, client: &Client) -> io::Result<TcpStream> {
        tcp_connect_fastest(client.get_address_list().await, Duration::from_secs(5)).await
    }
}

impl crate::Client {
    /// 获取服务器地址
    pub async fn get_address_list(&self) -> Vec<SocketAddr> {
        const BUILD_IN: [([u8; 4], u16); 6] = [
            ([42, 81, 172, 81], 80),
            ([114, 221, 148, 59], 14000),
            ([42, 81, 172, 147], 443),
            ([125, 94, 60, 146], 80),
            ([114, 221, 144, 215], 80),
            ([42, 81, 172, 22], 80),
        ];
        let mut addrs: Vec<_> = BUILD_IN.into_iter().map(SocketAddr::from).collect();
        if let Ok(res) = tokio::net::lookup_host(("msfwifi.3g.qq.com", 8080)).await {
            addrs.extend(res);
        }
        // TODO: src/client/processor/config_push_svc.rs
        addrs
    }

    /// 获取网络状态
    pub fn get_status(&self) -> u8 {
        self.status.load(Ordering::Relaxed)
    }

    /// 开始处理流数据,阻塞当前 Task。该方法返回即为断线。
    ///
    /// **Notice: 该方法仅开始处理包,需要手动登录并开始心跳包**
    pub async fn start(self: &Arc<Self>, stream: impl AsyncRead + AsyncWrite) {
        self.status
            .store(NetworkStatus::Running as u8, Ordering::Relaxed);
        self.net_loop(stream).await; // 阻塞到断开
        self.disconnect();
        self.online.store(false, Ordering::Relaxed);

        match self.status.compare_exchange(
            NetworkStatus::Running as u8,
            NetworkStatus::NetworkOffline as u8,
            Ordering::Relaxed,
            Ordering::Relaxed,
        ) {
            Ok(_) => {
                self.handler
                    .handle(QEvent::ClientDisconnect(ClientDisconnect {
                        client: Arc::clone(self),
                        inner: DisconnectReason::Network,
                    }))
                    .await;
            }
            Err(status) => {
                self.handler
                    .handle(QEvent::ClientDisconnect(ClientDisconnect {
                        client: Arc::clone(self),
                        inner: {
                            let network = match status {
                                0 => NetworkStatus::Unknown,
                                1 => NetworkStatus::Running,
                                2 => NetworkStatus::Stop,
                                3 => NetworkStatus::Drop,
                                5 => NetworkStatus::KickedOffline,
                                6 => NetworkStatus::MsfOffline,
                                _ => NetworkStatus::Unknown,
                            };

                            DisconnectReason::Actively(network)
                        },
                    }))
                    .await;
            }
        }
    }

    pub fn stop(&self, status: NetworkStatus) {
        self.disconnect();
        self.status.store(status as u8, Ordering::Relaxed);
        self.online.store(false, Ordering::Relaxed);
    }

    fn disconnect(&self) {
        // TODO dispatch disconnect event
        // don't unwrap (Err means there is no receiver.)
        self.disconnect_signal.send(()).ok();
    }

    async fn net_loop(self: &Arc<Client>, stream: impl AsyncRead + AsyncWrite) {
        let (mut write_half, mut read_half) = LengthDelimitedCodec::builder()
            .length_field_length(4)
            .length_adjustment(-4)
            .new_framed(stream)
            .split();
        // 外发包 Channel Receiver
        let mut rx = self.out_pkt_sender.subscribe();
        let mut disconnect_signal = self.disconnect_signal.subscribe();
        loop {
            tokio::select! {
                input = read_half.next() => {
                    if let Some(Ok(mut input)) = input {
                        if let Ok(pkt) = self.engine.read().await.transport.decode_packet(&mut input) {
                            self.process_income_packet(pkt).await;
                        } else {
                            self.status.store(NetworkStatus::MsfOffline as u8, Ordering::Relaxed);
                            break;
                        }
                    } else {
                        break;
                    }
                }
                output = rx.recv() => {
                    if let Ok(output) = output && write_half.send(output).await.is_err() {
                        break;
                    }
                }
                _ = disconnect_signal.recv() => {
                    break;
                }
            }
        }
    }
}


================================================
FILE: ricq/src/client/processor/c2c/friend_msg.rs
================================================
use cached::Cached;
use std::sync::Arc;

use ricq_core::msg::MessageChain;
use ricq_core::structs::{FriendAudio, FriendAudioMessage, FriendMessage};
use ricq_core::{pb, RQResult};

use crate::client::event::{FriendAudioMessageEvent, FriendMessageEvent};
use crate::handler::QEvent;
use crate::Client;

impl Client {
    pub(crate) async fn process_friend_message(
        self: &Arc<Self>,
        mut msg: pb::msg::Message,
    ) -> RQResult<()> {
        fn take_ptt(msg: &mut pb::msg::Message) -> Option<pb::msg::Ptt> {
            msg.body.as_mut()?.rich_text.as_mut()?.ptt.take()
        }
        if let Some(ptt) = take_ptt(&mut msg) {
            // TODO self friend audio
            self.handler
                .handle(QEvent::FriendAudioMessage(FriendAudioMessageEvent {
                    client: self.clone(),
                    inner: parse_friend_audio_message(msg, ptt)?,
                }))
                .await;
            return Ok(());
        }

        let message = parse_friend_message(msg)?;
        if message.from_uin == self.uin().await {
            if let Some(tx) = self
                .receipt_waiters
                .lock()
                .await
                .cache_remove(&message.rands.first().cloned().unwrap_or_default())
            {
                let _ = tx.send(message.seqs.first().cloned().unwrap_or_default());
                return Ok(());
            }
        }
        self.handler
            .handle(QEvent::FriendMessage(FriendMessageEvent {
                client: self.clone(),
                inner: message,
            }))
            .await;
        Ok(())
    }
}

pub fn parse_friend_message(msg: pb::msg::Message) -> RQResult<FriendMessage> {
    let head = msg.head.unwrap();
    Ok(FriendMessage {
        seqs: vec![head.msg_seq()],
        target: head.to_uin.unwrap(),
        time: head.msg_time.unwrap(),
        from_uin: head.from_uin.unwrap_or_default(),
        from_nick: head.from_nick.unwrap_or_default(),
        rands: vec![
            if let Some(attr) = &msg.body.as_ref().unwrap().rich_text.as_ref().unwrap().attr {
                attr.random()
            } else {
                0
            },
        ],
        elements: MessageChain::from(msg.body.unwrap().rich_text.unwrap().elems), // todo ptt_store
    })
}

pub fn parse_friend_audio_message(
    msg: pb::msg::Message,
    ptt: pb::msg::Ptt,
) -> RQResult<FriendAudioMessage> {
    let head = msg.head.unwrap();
    Ok(FriendAudioMessage {
        seqs: vec![head.msg_seq()],
        target: head.to_uin.unwrap(),
        time: head.msg_time.unwrap(),
        from_uin: head.from_uin.unwrap_or_default(),
        from_nick: head.from_nick.unwrap_or_default(),
        rands: vec![
            if let Some(attr) = &msg.body.as_ref().unwrap().rich_text.as_ref().unwrap().attr {
                attr.random()
            } else {
                0
            },
        ],
        audio: FriendAudio(ptt),
    })
}


================================================
FILE: ricq/src/client/processor/c2c/friend_system_msg.rs
================================================
use crate::client::event::NewFriendRequestEvent;
use crate::handler::QEvent;
use crate::Client;
use ricq_core::command::profile_service::FriendSystemMessages;
use std::sync::Arc;

impl Client {
    pub(crate) async fn process_friend_system_messages(
        self: &Arc<Self>,
        msgs: FriendSystemMessages,
    ) {
        for request in msgs.requests {
            self.handler
                .handle(QEvent::NewFriendRequest(NewFriendRequestEvent {
                    client: self.clone(),
                    inner: request,
                }))
                .await;
        }
    }
}


================================================
FILE: ricq/src/client/processor/c2c/group_system_msg.rs
================================================
use std::sync::Arc;

use ricq_core::command::profile_service::GroupSystemMessages;

use crate::client::event::{JoinGroupRequestEvent, SelfInvitedEvent};
use crate::handler::QEvent;
use crate::Client;

impl Client {
    pub(crate) async fn process_group_system_messages(self: &Arc<Self>, msgs: GroupSystemMessages) {
        for request in msgs.self_invited.clone() {
            if self
                .self_invited_exists(request.msg_seq, request.msg_time)
                .await
            {
                continue;
            }
            self.handler
                .handle(QEvent::SelfInvited(SelfInvitedEvent {
                    client: self.clone(),
                    inner: request,
                }))
                .await;
        }
        for request in msgs.join_group_requests.clone() {
            if self
                .join_group_request_exists(request.msg_seq, request.msg_time)
                .await
            {
                continue;
            }
            self.handler
                .handle(QEvent::GroupRequest(JoinGroupRequestEvent {
                    client: self.clone(),
                    inner: request,
                }))
                .await;
        }
        let mut cache = self.group_sys_message_cache.write().await;
        *cache = msgs
    }

    async fn self_invited_exists(&self, msg_seq: i64, msg_time: i64) -> bool {
        if self.start_time > msg_time as i32 {
            return true;
        }
        self.group_sys_message_cache
            .read()
            .await
            .self_invited
            .iter()
            .any(|m| m.msg_seq == msg_seq)
    }

    async fn join_group_request_exists(&self, msg_seq: i64, msg_time: i64) -> bool {
        if self.start_time > msg_time as i32 {
            return true;
        }
        self.group_sys_message_cache
            .read()
            .await
            .join_group_requests
            .iter()
            .any(|m| m.msg_seq == msg_seq)
    }
}


================================================
FILE: ricq/src/client/processor/c2c/mod.rs
================================================
pub mod friend_msg;
pub mod friend_system_msg;
pub mod group_system_msg;
pub mod new_member;
pub mod temp_session;


================================================
FILE: ricq/src/client/processor/c2c/new_member.rs
================================================
use std::sync::Arc;

use ricq_core::common::group_uin2code;
use ricq_core::structs::NewMember;
use ricq_core::{pb, RQError, RQResult};

use crate::client::event::NewMemberEvent;
use crate::handler::QEvent;
use crate::Client;

impl Client {
    pub(crate) async fn process_join_group(
        self: &Arc<Self>,
        msg: pb::msg::Message,
    ) -> RQResult<()> {
        let head = msg.head.ok_or(RQError::EmptyField("msg.head"))?;
        let group_code = group_uin2code(head.from_uin());
        let member_uin = head.auth_uin();

        self.handler
            .handle(QEvent::NewMember(NewMemberEvent {
                client: self.clone(),
                inner: NewMember {
                    group_code,
                    member_uin,
                },
            }))
            .await;

        Ok(())
    }
}


================================================
FILE: ricq/src/client/processor/c2c/temp_session.rs
================================================
use std::sync::Arc;

use ricq_core::msg::MessageChain;
use ricq_core::structs::GroupTempMessage;
use ricq_core::{pb, RQError, RQResult};

use crate::client::event::GroupTempMessageEvent;
use crate::handler::QEvent;
use crate::Client;

impl Client {
    pub(crate) async fn process_temp_message(
        self: &Arc<Self>,
        msg: pb::msg::Message,
    ) -> RQResult<()> {
        let message = parse_temp_message(msg)?;
        self.handler
            .handle(QEvent::GroupTempMessage(GroupTempMessageEvent {
                client: self.clone(),
                inner: message,
            }))
            .await;
        Ok(())
    }
}

pub fn parse_temp_message(msg: pb::msg::Message) -> RQResult<GroupTempMessage> {
    let head = msg.head.unwrap();
    let tmp_head = head
        .c2c_tmp_msg_head
        .ok_or(RQError::EmptyField("c2c_tmp_msg_head"))?;

    Ok(GroupTempMessage {
        seqs: vec![head.msg_seq.unwrap_or_default()],
        rands: vec![
            if let Some(attr) = &msg.body.as_ref().unwrap().rich_text.as_ref().unwrap().attr {
                attr.random()
            } else {
                0
            },
        ],
        time: head.msg_time.unwrap(),
        from_uin: head.from_uin.unwrap_or_default(),
        from_nick: head.from_nick.unwrap_or_default(),
        elements: MessageChain::from(msg.body.unwrap().rich_text.unwrap().elems), // todo ptt_store
        group_code: tmp_head.group_code.unwrap_or_default(),
    })
}


================================================
FILE: ricq/src/client/processor/config_push_svc.rs
================================================
use std::time::Duration;

use bytes::Bytes;

use ricq_core::command::config_push_svc::ConfigPushBody;
use ricq_core::command::config_push_svc::ConfigPushReq;
use ricq_core::common::RQAddr;

use crate::client::tcp::sort_addrs;
use crate::client::Client;
use crate::RQError;

impl Client {
    pub(crate) async fn process_config_push_req(
        &self,
        config_push_req: ConfigPushReq,
    ) -> Result<(), RQError> {
        // send response to server
        let resp = config_push_req.resp;
        let response = self.engine.read().await.build_conf_push_resp_packet(
            resp.t,
            resp.pkt_seq,
            resp.jce_buf,
        );
        self.send(response).await?;
        match config_push_req.body {
            ConfigPushBody::Unknown => {}
            ConfigPushBody::SsoServers { .. } => {}
            ConfigPushBody::FileStorageInfo { info: _, rsp_body } => {
                let mut session = self.highway_session.write().await;
                if let Some(rsp_body) = rsp_body {
                    session.sig_session = Bytes::from(rsp_body.sig_session.unwrap_or_default());
                    session.session_key = Bytes::from(rsp_body.session_key.unwrap_or_default());
                    session.uin = self.uin().await;
                    session.app_id = self.engine.read().await.transport.version.app_id as i32;
                    for addr in rsp_body.addrs.into_iter() {
                        let service_type = addr.service_type.unwrap_or_default();
                        if service_type == 10 {
                            let addrs: Vec<RQAddr> = addr
                                .addrs
                                .into_iter()
                                .map(|addr| {
                                    RQAddr(
                                        addr.ip.unwrap_or_default(),
                                        addr.port.unwrap_or_default() as u16,
                                    )
                                })
                                .collect();
                            // 先写入,确保启动后可以快速使用
                            self.highway_addrs.write().await.extend(addrs);
                            // 去重,排序
                            {
                                let mut addrs = self.highway_addrs.read().await.clone();
                                addrs.dedup_by(|a, b| (a.0 == b.0 && a.1 == b.1));
                                let sorted_addrs = sort_addrs(addrs, Duration::from_secs(5)).await;
                                let mut highway_addrs = self.highway_addrs.write().await;
                                highway_addrs.clear();
                                highway_addrs.extend(sorted_addrs);
                            }
                        } else if service_type == 11 {
                            // TODO
                        }
                    }
                }
            }
        }
        // TODO process
        Ok(())
    }
}


================================================
FILE: ricq/src/client/processor/message_svc.rs
================================================
use std::sync::Arc;
use std::time::UNIX_EPOCH;

use cached::Cached;

use ricq_core::{jce, pb};

use crate::client::event::KickedOfflineEvent;
use crate::client::{Client, NetworkStatus};
use crate::handler::QEvent;

impl Client {
    pub(crate) async fn process_push_notify(self: &Arc<Self>, notify: jce::RequestPushNotify) {
        match notify.msg_type {
            35 | 36 | 37 | 45 | 46 | 84 | 85 | 86 | 87 => {
                // pull group system msg(group request), then process
                match self.get_all_group_system_messages().await {
                    Ok(msgs) => {
                        self.process_group_system_messages(msgs).await;
                    }
                    Err(err) => {
                        tracing::warn!("failed to get group system message {}", err);
                    }
                }
            }
            187..=191 => {
                // pull friend system msg(friend request), then process
                match self.get_friend_system_messages().await {
                    Ok(msgs) => {
                        self.process_friend_system_messages(msgs).await;
                    }
                    Err(err) => {
                        tracing::warn!("failed to get friend system message {}", err);
                    }
                }
            }
            _ => {
                // TODO tracing.warn!()
            }
        }
        // pull friend msg and other, then process
        let all_message = self.sync_all_message().await;
        match all_message {
            Ok(msgs) => {
                self.process_message_sync(msgs).await;
            }
            Err(err) => {
                tracing::warn!("failed to sync message {}", err);
            }
        }
    }

    pub(crate) async fn process_push_force_offline(
        self: &Arc<Self>,
        offline: jce::RequestPushForceOffline,
    ) {
        self.stop(NetworkStatus::KickedOffline);
        self.handler
            .handle(QEvent::KickedOffline(KickedOfflineEvent {
                client: self.clone(),
                inner: offline,
            }))
            .await;
    }

    pub(crate) async fn process_message_sync(self: &Arc<Self>, msgs: Vec<pb::msg::Message>) {
        for msg in msgs {
            let head = msg.head.clone().unwrap();
            if self.msg_exists(&head).await {
                continue;
            }
            match msg.head.as_ref().unwrap().msg_type() {
                9 | 10 | 31 | 79 | 97 | 120 | 132 | 133 | 166 | 167 => {
                    if let Err(err) = self.process_friend_message(msg).await {
                        tracing::error!("failed to process friend message {err}");
                    }
                }
                33 => {
                    if let Err(err) = self.process_join_group(msg).await {
                        tracing::error!("failed to process join group {err}");
                    }
                }
                140 | 141 => {
                    if let Err(err) = self.process_temp_message(msg).await {
                        tracing::error!("failed to process temp message {err}");
                    }
                }
                208 => {
                    // friend ptt_store
                }
                _ => tracing::warn!("unhandled sync message type"),
            }
        }
    }

    async fn msg_exists(&self, head: &pb::msg::MessageHead) -> bool {
        let now = UNIX_EPOCH.elapsed().unwrap().as_secs() as i32;
        let msg_time = head.msg_time.unwrap_or_default();
        if now - msg_time > 60 || self.start_time > msg_time {
            return true;
        }
        let mut c2c_cache = self.c2c_cache.write().await;
        let key = (
            head.from_uin(),
            head.to_uin(),
            head.msg_seq(),
            head.msg_uid(),
        );
        if c2c_cache.cache_get(&key).is_some() {
            return true;
        }
        c2c_cache.cache_set(key, ());
        if c2c_cache.cache_misses().unwrap_or_default() > 100 {
            c2c_cache.flush();
            c2c_cache.cache_reset_metrics();
        }
        false
    }
}


================================================
FILE: ricq/src/client/processor/mod.rs
================================================
use std::sync::Arc;

use bytes::Bytes;

use ricq_core::protocol::packet::Packet;

pub mod c2c;
pub mod config_push_svc;
pub mod message_svc;
pub mod online_push;
pub mod reg_prxy_svc;
pub mod stat_svc;
pub mod wtlogin;

macro_rules! log_error {
    ($process: expr, $info: expr) => {
        if let Err(e) = $process {
            tracing::error!($info, e);
        }
    };
}

impl super::Client {
    /// 接收到的 Packet 统一分发
    pub async fn process_income_packet(self: &Arc<Self>, pkt: Packet) {
        tracing::trace!("received pkt: {}", &pkt.command_name);
        // response, send_and_wait 的包将会在此被截流
        {
            if let Some(sender) = self.packet_promises.write().await.remove(&pkt.seq_id) {
                sender.send(pkt).unwrap();
                return;
            }
        }

        tracing::trace!("pkt: {} passed packet_promises", &pkt.command_name);

        {
            if let Some(handler) = self.packet_handler.read().await.get(&pkt.command_name) {
                let _ = handler.send(pkt.clone());
            }
        }

        let cli = self.clone();
        tokio::spawn(async move {
            match pkt.command_name.as_ref() {
                "OnlinePush.PbPushGroupMsg" => {
                    let p = cli
                        .engine
                        .read()
                        .await
                        .decode_group_message_packet(pkt.body);
                    match p {
                        Ok(part) => {
                            log_error!(
                                cli.process_group_message_part(part).await,
                                "process_group_message_part error: {:?}"
                            )
                        }
                        Err(err) => {
                            tracing::warn!("failed to decode [OnlinePush.PbPushGroupMsg]: {}", err);
                        }
                    }
                }
                "ConfigPushSvc.PushReq" => {
                    let req = cli.engine.read().await.decode_push_req_packet(pkt.body);
                    match req {
                        Ok(req) => {
                            log_error!(
                                cli.process_config_push_req(req).await,
                                "process_config_push_req error: {:?}"
                            )
                        }
                        Err(err) => {
                            tracing::warn!("failed to decode [ConfigPushSvc.PushReq]: {}", err);
                        }
                    }
                }
                "RegPrxySvc.PushParam" => {
                    let other_clients = cli.engine.read().await.decode_push_param_packet(&pkt.body);
                    match other_clients {
                        Ok(other_clients) => {
                            log_error!(
                                cli.process_push_param(other_clients).await,
                                "process_push_param error: {:?}"
                            )
                        }
                        Err(err) => {
                            tracing::warn!("failed to decode [RegPrxySvc.PushParam]: {}", err);
                        }
                    }
                }
                "MessageSvc.PushNotify" => {
                    // c2c流程:
                    // 1. Server 发送 PushNotify 到 Client, 表示有通知需要 Client 拉取 (不带具体内容)
                    // 2. Client 根据 msg_type 发送请求拉取具体通知内容
                    // 类型:好友申请、群申请、私聊消息、其他?
                    let resp = cli.engine.read().await.decode_svc_notify(pkt.body);
                    match resp {
                        Ok(notify) => {
                            cli.process_push_notify(notify).await;
                        }
                        Err(err) => {
                            tracing::warn!("failed to decode [MessageSvc.PushNotify]: {}", err);
                        }
                    }
                }
                "OnlinePush.ReqPush" => {
                    let resp = cli
                        .engine
                        .read()
                        .await
                        .decode_online_push_req_packet(pkt.body);
                    match resp {
                        Ok(resp) => {
                            log_error!(
                                cli.delete_online_push(
                                    resp.uin,
                                    0,
                                    Bytes::new(),
                                    pkt.seq_id as u16,
                                    resp.msg_infos.clone(),
                                )
                                .await,
                                "delete_online_push error: {:?}"
                            );
                            cli.process_push_req(resp.msg_infos).await;
                        }
                        Err(err) => {
                            tracing::warn!("failed to decode [OnlinePush.ReqPush]: {}", err);
                        }
                    }
                }
                "OnlinePush.PbPushTransMsg" => {
                    let online_push_trans = cli
                        .engine
                        .read()
                        .await
                        .decode_online_push_trans_packet(pkt.body);
                    match online_push_trans {
                        Ok(online_push_trans) => {
                            cli.process_push_trans(online_push_trans).await;
                        }
                        Err(err) => {
                            tracing::warn!("failed to decode [OnlinePush.PbPushTransMsg]: {}", err);
                        }
                    }
                }
                "MessageSvc.PushForceOffline" => {
                    let offline = cli.engine.read().await.decode_force_offline(pkt.body);
                    match offline {
                        Ok(offline) => {
                            cli.process_push_force_offline(offline).await;
                        }
                        Err(err) => {
                            tracing::warn!(
                                "failed to decode [MessageSvc.PushForceOffline]: {}",
                                err
                            );
                        }
                    }
                }
                "StatSvc.ReqMSFOffline" => {
                    let offline = cli.engine.read().await.decode_msf_force_offline(pkt.body);
                    match offline {
                        Ok(offline) => {
                            cli.process_msf_force_offline(offline).await;
                        }
                        Err(err) => {
                            tracing::warn!("failed to decode [StatSvc.ReqMSFOffline]: {}", err);
                        }
                    }
                }
                "OnlinePush.PbC2CMsgSync" => {
                    // 其他设备发送消息,同步
                    let push = cli.engine.read().await.decode_c2c_sync_packet(pkt.body);
                    match push {
                        Ok(push) => {
                            log_error!(
                                cli.process_c2c_sync(pkt.seq_id, push).await,
                                "process_c2c_sync error: {:?}"
                            )
                        }
                        Err(err) => {
                            tracing::warn!("failed to decode [OnlinePush.PbC2CMsgSync]: {}", err);
                        }
      
Download .txt
gitextract_sa4m0udb/

├── .gitignore
├── Cargo.toml
├── LICENSE
├── examples/
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── password_login/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       └── main.rs
│   ├── qrcode_login/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       └── main.rs
│   ├── ricq-axum-api/
│   │   ├── Cargo.toml
│   │   ├── README.md
│   │   └── src/
│   │       ├── bin/
│   │       │   └── main.rs
│   │       ├── handler/
│   │       │   ├── bot.rs
│   │       │   ├── mod.rs
│   │       │   ├── password.rs
│   │       │   └── qrcode.rs
│   │       ├── lib.rs
│   │       ├── processor.rs
│   │       └── u8_protocol.rs
│   └── token_login/
│       ├── Cargo.toml
│       └── src/
│           └── main.rs
├── ricq/
│   ├── Cargo.toml
│   ├── README.md
│   └── src/
│       ├── client/
│       │   ├── api/
│       │   │   ├── friend.rs
│       │   │   ├── group.rs
│       │   │   ├── login.rs
│       │   │   └── mod.rs
│       │   ├── event.rs
│       │   ├── handler/
│       │   │   └── mod.rs
│       │   ├── highway/
│       │   │   ├── codec.rs
│       │   │   ├── mod.rs
│       │   │   └── net.rs
│       │   ├── mod.rs
│       │   ├── net.rs
│       │   ├── processor/
│       │   │   ├── c2c/
│       │   │   │   ├── friend_msg.rs
│       │   │   │   ├── friend_system_msg.rs
│       │   │   │   ├── group_system_msg.rs
│       │   │   │   ├── mod.rs
│       │   │   │   ├── new_member.rs
│       │   │   │   └── temp_session.rs
│       │   │   ├── config_push_svc.rs
│       │   │   ├── message_svc.rs
│       │   │   ├── mod.rs
│       │   │   ├── online_push.rs
│       │   │   ├── reg_prxy_svc.rs
│       │   │   ├── stat_svc.rs
│       │   │   └── wtlogin.rs
│       │   ├── qimei.rs
│       │   └── tcp.rs
│       ├── config.rs
│       ├── ext/
│       │   ├── common.rs
│       │   ├── image.rs
│       │   ├── login.rs
│       │   ├── mod.rs
│       │   └── reconnect.rs
│       ├── lib.rs
│       ├── qsign.rs
│       └── structs/
│           ├── image_info.rs
│           └── mod.rs
├── ricq-core/
│   ├── Cargo.toml
│   ├── build.rs
│   └── src/
│       ├── binary/
│       │   ├── binary_reader.rs
│       │   ├── binary_writer.rs
│       │   ├── mod.rs
│       │   └── packet_writer.rs
│       ├── command/
│       │   ├── common.rs
│       │   ├── config_push_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── friendlist/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── group_anonymous_generate_nick/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── group_member_card/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── heartbeat/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   ├── img_store/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── long_conn/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── longmsg/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   ├── message_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── mod.rs
│       │   ├── multi_msg/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── oidb_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── online_push/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── pb_message_svc/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   ├── profile_service/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── ptt_center_svr/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── ptt_store/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── reg_prxy_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── signature/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   ├── stat_svc/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── summary_card/
│       │   │   ├── builder.rs
│       │   │   ├── decoder.rs
│       │   │   └── mod.rs
│       │   ├── visitor_svc/
│       │   │   ├── builder.rs
│       │   │   └── mod.rs
│       │   └── wtlogin/
│       │       ├── builder.rs
│       │       ├── decoder.rs
│       │       ├── mod.rs
│       │       ├── tlv_reader.rs
│       │       └── tlv_writer.rs
│       ├── common.rs
│       ├── crypto/
│       │   ├── encrypt.rs
│       │   ├── mod.rs
│       │   └── qqtea.rs
│       ├── error.rs
│       ├── hex.rs
│       ├── highway/
│       │   └── mod.rs
│       ├── jce/
│       │   └── mod.rs
│       ├── lib.rs
│       ├── msg/
│       │   ├── elem/
│       │   │   ├── anonymous.rs
│       │   │   ├── at.rs
│       │   │   ├── face.rs
│       │   │   ├── flash_image.rs
│       │   │   ├── friend_image.rs
│       │   │   ├── group_image.rs
│       │   │   ├── light_app.rs
│       │   │   ├── market_face.rs
│       │   │   ├── mod.rs
│       │   │   ├── reply.rs
│       │   │   ├── rich_msg.rs
│       │   │   ├── text.rs
│       │   │   └── video_file.rs
│       │   ├── fragment.rs
│       │   ├── macros.rs
│       │   └── mod.rs
│       ├── pb/
│       │   ├── cmd0x346/
│       │   │   └── cmd0x346.proto
│       │   ├── cmd0x352/
│       │   │   └── cmd0x352.proto
│       │   ├── cmd0x388/
│       │   │   └── cmd0x388.proto
│       │   ├── cmd0x3bb/
│       │   │   └── cmd0x3bb.proto
│       │   ├── cmd0x6ff/
│       │   │   ├── smbcmd0x519.proto
│       │   │   └── subcmd0x501.proto
│       │   ├── cmd0x899/
│       │   │   └── cmd0x899.proto
│       │   ├── data.proto
│       │   ├── longmsg/
│       │   │   └── longmsg.proto
│       │   ├── mod.rs
│       │   ├── msf/
│       │   │   └── register_proxy.proto
│       │   ├── msg/
│       │   │   ├── TextMsgExt.proto
│       │   │   ├── head.proto
│       │   │   ├── msg.proto
│       │   │   ├── objmsg.proto
│       │   │   └── report.proto
│       │   ├── msgtype0x210/
│       │   │   └── subMsgType0x27.proto
│       │   ├── multimsg/
│       │   │   └── multimsg.proto
│       │   ├── notify/
│       │   │   └── group0x857.proto
│       │   ├── oidb/
│       │   │   ├── oidb.proto
│       │   │   ├── oidb0x6d6.proto
│       │   │   ├── oidb0x6d8.proto
│       │   │   ├── oidb0x758.proto
│       │   │   ├── oidb0x769.proto
│       │   │   ├── oidb0x88d.proto
│       │   │   ├── oidb0x8a7.proto
│       │   │   ├── oidb0x8fc.proto
│       │   │   ├── oidb0x990.proto
│       │   │   ├── oidb0xb77.proto
│       │   │   ├── oidb0xe07.proto
│       │   │   ├── oidb0xeac.proto
│       │   │   └── oidb0xeb7.proto
│       │   ├── online_status/
│       │   │   └── OnlineStatusExtInfo.java.proto
│       │   ├── profilecard/
│       │   │   ├── busi.proto
│       │   │   └── gate.proto
│       │   ├── short_video/
│       │   │   └── short_video.proto
│       │   ├── sig_act/
│       │   │   └── sig_act.proto
│       │   └── structmsg/
│       │       └── structmsg.proto
│       ├── protocol/
│       │   ├── device.rs
│       │   ├── mod.rs
│       │   ├── oicq.rs
│       │   ├── packet.rs
│       │   ├── qimei.rs
│       │   ├── sig.rs
│       │   ├── transport.rs
│       │   └── version.rs
│       ├── structs.rs
│       ├── token.rs
│       ├── utils/
│       │   ├── mod.rs
│       │   └── option_set.rs
│       └── wtlogin.rs
├── ricq-guild/
│   ├── Cargo.toml
│   ├── README.md
│   ├── build.rs
│   └── src/
│       ├── client/
│       │   ├── builder.rs
│       │   ├── decoder.rs
│       │   ├── mod.rs
│       │   └── processor.rs
│       ├── lib.rs
│       └── protocol/
│           ├── core/
│           │   ├── cmd0x346/
│           │   │   └── cmd0x346.proto
│           │   ├── cmd0x352/
│           │   │   └── cmd0x352.proto
│           │   ├── cmd0x388/
│           │   │   └── cmd0x388.proto
│           │   ├── cmd0x3bb/
│           │   │   └── cmd0x3bb.proto
│           │   ├── cmd0x6ff/
│           │   │   ├── smbcmd0x519.proto
│           │   │   └── subcmd0x501.proto
│           │   ├── cmd0x899/
│           │   │   └── cmd0x899.proto
│           │   ├── data.proto
│           │   ├── longmsg/
│           │   │   └── longmsg.proto
│           │   ├── msf/
│           │   │   └── register_proxy.proto
│           │   ├── msg/
│           │   │   ├── TextMsgExt.proto
│           │   │   ├── head.proto
│           │   │   ├── msg.proto
│           │   │   ├── objmsg.proto
│           │   │   └── report.proto
│           │   ├── msgtype0x210/
│           │   │   └── subMsgType0x27.proto
│           │   ├── multimsg/
│           │   │   └── multimsg.proto
│           │   ├── notify/
│           │   │   └── group0x857.proto
│           │   ├── oidb/
│           │   │   ├── oidb.proto
│           │   │   ├── oidb0x6d6.proto
│           │   │   ├── oidb0x758.proto
│           │   │   ├── oidb0x769.proto
│           │   │   ├── oidb0x88d.proto
│           │   │   ├── oidb0x8a7.proto
│           │   │   ├── oidb0x8fc.proto
│           │   │   ├── oidb0x990.proto
│           │   │   ├── oidb0xb77.proto
│           │   │   ├── oidb0xe07.proto
│           │   │   ├── oidb0xeac.proto
│           │   │   └── oidb0xeb7.proto
│           │   ├── online_status/
│           │   │   └── OnlineStatusExtInfo.java.proto
│           │   ├── profilecard/
│           │   │   ├── busi.proto
│           │   │   └── gate.proto
│           │   ├── short_video/
│           │   │   └── short_video.proto
│           │   ├── sig_act/
│           │   │   └── sig_act.proto
│           │   └── structmsg/
│           │       └── structmsg.proto
│           ├── mod.rs
│           └── protobuf/
│               ├── GuildChannelBase.proto
│               ├── GuildFeedCloudMeta.proto
│               ├── GuildFeedCloudRead.proto
│               ├── GuildWriter.proto
│               ├── MsgResponsesSvr.proto
│               ├── common.proto
│               ├── msgpush.proto
│               ├── oidb0xf62.proto
│               ├── servtype.proto
│               ├── synclogic.proto
│               └── unknown.proto
└── rust-toolchain.toml
Download .txt
SYMBOL INDEX (1085 symbols across 138 files)

FILE: examples/password_login/src/main.rs
  function main (line 21) | async fn main() {

FILE: examples/qrcode_login/src/main.rs
  function main (line 17) | async fn main() {

FILE: examples/ricq-axum-api/src/bin/main.rs
  type ClientProcessor (line 32) | struct ClientProcessor(DashMap<(i64, u8), Arc<Client>>);
  method on_login_success (line 36) | async fn on_login_success(
  method list_client (line 102) | async fn list_client(&self) -> Vec<ClientInfo> {
  method delete_client (line 117) | async fn delete_client(&self, uin: i64, protocol: u8) {
  function main (line 125) | async fn main() {
  function handle_error (line 199) | async fn handle_error(_: std::io::Error) -> impl axum::response::IntoRes...

FILE: examples/ricq-axum-api/src/handler/bot.rs
  type ListBotResp (line 11) | pub struct ListBotResp {
  function list (line 15) | pub async fn list<P: Processor>(
  type DeleteBotReq (line 24) | pub struct DeleteBotReq {
  type DeleteBotResp (line 30) | pub struct DeleteBotResp {}
  function delete (line 32) | pub async fn delete<P: Processor>(

FILE: examples/ricq-axum-api/src/handler/password.rs
  type CreateClientReq (line 20) | pub struct CreateClientReq {
  type SubmitTicketReq (line 28) | pub struct SubmitTicketReq {
  type RequestSmsReq (line 35) | pub struct RequestSmsReq {
  type SubmitSmsReq (line 41) | pub struct SubmitSmsReq {
  type PasswordLoginResp (line 48) | pub struct PasswordLoginResp {
    method from (line 57) | fn from(login_response: LoginResponse) -> Self {
  function login (line 99) | pub async fn login<P: Processor>(
  function submit_ticket (line 166) | pub async fn submit_ticket<P: Processor>(
  function request_sms (line 216) | pub async fn request_sms<P: Processor>(
  function submit_sms (line 236) | pub async fn submit_sms<P: Processor>(
  type ListClientResp (line 287) | pub struct ListClientResp {
  type ListClientRespClient (line 292) | pub struct ListClientRespClient {
  function list (line 298) | pub async fn list<P: Processor>(
  type DeleteClientReq (line 313) | pub struct DeleteClientReq {
  type DeleteClientResp (line 319) | pub struct DeleteClientResp {}
  function delete (line 321) | pub async fn delete<P: Processor>(

FILE: examples/ricq-axum-api/src/handler/qrcode.rs
  function serialize (line 28) | pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
  function deserialize (line 35) | pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
  type CreateClientReq (line 45) | pub struct CreateClientReq {
  type CreateClientResp (line 51) | pub struct CreateClientResp {
  function create (line 58) | pub async fn create<P: Processor>(
  type QueryQRCodeReq (line 118) | pub struct QueryQRCodeReq {
  type QueryQRCodeResp (line 124) | pub struct QueryQRCodeResp {
  function query (line 128) | pub async fn query<P: Processor>(
  type ListClientResp (line 194) | pub struct ListClientResp {
  type ListClientRespClient (line 199) | pub struct ListClientRespClient {
  function list (line 208) | pub async fn list<P: Processor>(
  type DeleteClientReq (line 232) | pub struct DeleteClientReq {
  type DeleteClientResp (line 238) | pub struct DeleteClientResp {}
  function delete (line 240) | pub async fn delete<P: Processor>(

FILE: examples/ricq-axum-api/src/lib.rs
  type ClientInfo (line 18) | pub struct ClientInfo {
  type PasswordClient (line 25) | pub struct PasswordClient {
  type QRCodeClient (line 33) | pub struct QRCodeClient {
  type RicqAxumApi (line 42) | pub struct RicqAxumApi<P: Processor> {
  function new (line 54) | pub fn new(processor: P) -> Self {

FILE: examples/ricq-axum-api/src/processor.rs
  type Processor (line 11) | pub trait Processor {
    method on_login_success (line 12) | async fn on_login_success(
    method list_client (line 19) | async fn list_client(&self) -> Vec<ClientInfo>;
    method delete_client (line 20) | async fn delete_client(&self, uin: i64, protocol: u8);

FILE: examples/ricq-axum-api/src/u8_protocol.rs
  type U8Protocol (line 3) | pub trait U8Protocol {
    method to_u8 (line 4) | fn to_u8(&self) -> u8;
    method from_u8 (line 5) | fn from_u8(n: u8) -> Self;
    method to_u8 (line 9) | fn to_u8(&self) -> u8 {
    method from_u8 (line 20) | fn from_u8(n: u8) -> Self {

FILE: examples/token_login/src/main.rs
  function main (line 16) | async fn main() {

FILE: ricq-core/build.rs
  function recurse_dir (line 2) | fn recurse_dir(v: &mut Vec<PathBuf>, dir: impl AsRef<Path>) {
  function main (line 14) | fn main() {

FILE: ricq-core/src/binary/binary_reader.rs
  type BinaryReader (line 5) | pub trait BinaryReader {
    method read_string (line 6) | fn read_string(&mut self) -> String;
    method read_string_short (line 7) | fn read_string_short(&mut self) -> String;
    method read_bytes_short (line 8) | fn read_bytes_short(&mut self) -> Bytes;
    method read_tlv_map (line 9) | fn read_tlv_map(&mut self, tag_size: usize) -> HashMap<u16, Bytes>;
    method read_string_limit (line 10) | fn read_string_limit(&mut self, limit: usize) -> String;
    method read_string (line 17) | fn read_string(&mut self) -> String {
    method read_string_short (line 22) | fn read_string_short(&mut self) -> String {
    method read_bytes_short (line 27) | fn read_bytes_short(&mut self) -> Bytes {
    method read_tlv_map (line 32) | fn read_tlv_map(&mut self, tag_size: usize) -> HashMap<u16, Bytes> {
    method read_string_limit (line 60) | fn read_string_limit(&mut self, limit: usize) -> String {

FILE: ricq-core/src/binary/binary_writer.rs
  type BinaryWriter (line 5) | pub trait BinaryWriter {
    method write_bytes_short (line 6) | fn write_bytes_short(&mut self, data: &[u8]);
    method encrypt_and_write (line 7) | fn encrypt_and_write(&mut self, key: &[u8], data: &[u8]);
    method write_hex (line 8) | fn write_hex(&mut self, h: &str);
    method write_int_lv_packet (line 9) | fn write_int_lv_packet(&mut self, offset: usize, data: &[u8]);
    method write_string (line 10) | fn write_string(&mut self, v: &str);
    method write_uni_packet (line 11) | fn write_uni_packet(
    method write_tlv_limited_size (line 18) | fn write_tlv_limited_size(&mut self, data: &[u8], limit: isize);
    method write_bytes_short (line 25) | fn write_bytes_short(&mut self, data: &[u8]) {
    method encrypt_and_write (line 30) | fn encrypt_and_write(&mut self, key: &[u8], data: &[u8]) {
    method write_hex (line 35) | fn write_hex(&mut self, h: &str) {
    method write_int_lv_packet (line 40) | fn write_int_lv_packet(&mut self, offset: usize, data: &[u8]) {
    method write_string (line 45) | fn write_string(&mut self, v: &str) {
    method write_uni_packet (line 51) | fn write_uni_packet(
    method write_tlv_limited_size (line 77) | fn write_tlv_limited_size(&mut self, data: &[u8], limit: isize) {

FILE: ricq-core/src/binary/packet_writer.rs
  type PacketWriter (line 4) | pub trait PacketWriter<B: BufMut> {
    method write (line 5) | fn write(self, buf: &mut B);
  type PacketAppender (line 8) | pub trait PacketAppender<B: BufMut>: PacketWriter<B> + Sized {
    method append (line 9) | fn append<W: PacketWriter<B>>(self, w: W) -> impl PacketWriter<B> {
  method write (line 22) | fn write(self, buf: &mut B) {
  function write (line 31) | fn write(self, buf: &mut B) {
  type Either (line 36) | pub enum Either<L, R> {
  function write (line 47) | fn write(self, buf: &mut B) {
  type CounterWriter (line 55) | pub struct CounterWriter<B, W>
  function write (line 70) | fn write(self, buf: &mut B) {
  function append (line 80) | fn append<W>(self, w: W) -> CounterWriter<B, impl PacketWriter<B>>
  method default (line 96) | fn default() -> Self {
  function append_option (line 110) | pub fn append_option<W>(self, w: Option<W>) -> CounterWriter<B, impl Pac...
  type WriteLV (line 127) | pub trait WriteLV: BufMut {
    method write_short_lv (line 128) | fn write_short_lv<W>(&mut self, w: W)

FILE: ricq-core/src/command/common.rs
  method build_oicq_request_packet (line 9) | pub fn build_oicq_request_packet(&self, uin: i64, command_id: u16, body:...
  method uni_packet_with_seq (line 19) | pub fn uni_packet_with_seq(&self, seq: i32, command: &str, body: Bytes) ...
  method uni_packet (line 31) | pub fn uni_packet(&self, command: &str, body: Bytes) -> Packet {
  function pack_uni_request_data (line 37) | pub fn pack_uni_request_data(data: &[u8]) -> Bytes {
  type PbToBytes (line 45) | pub trait PbToBytes<B: Message> {
    method to_bytes (line 46) | fn to_bytes(&self) -> Bytes;
  method to_bytes (line 50) | fn to_bytes(&self) -> Bytes {

FILE: ricq-core/src/command/config_push_svc/builder.rs
  function build_conf_push_resp_packet (line 12) | pub fn build_conf_push_resp_packet(&self, t: i32, pkt_seq: i64, jce_buf:...

FILE: ricq-core/src/command/config_push_svc/decoder.rs
  function decode_push_req_packet (line 10) | pub fn decode_push_req_packet(&self, mut payload: Bytes) -> RQResult<Con...

FILE: ricq-core/src/command/config_push_svc/mod.rs
  type ConfigPushReq (line 10) | pub struct ConfigPushReq {
  type ConfigPushBody (line 17) | pub enum ConfigPushBody {
  type ConfigPushResp (line 30) | pub struct ConfigPushResp {

FILE: ricq-core/src/command/friendlist/builder.rs
  function build_friend_group_list_request_packet (line 13) | pub fn build_friend_group_list_request_packet(
  function build_group_list_request_packet (line 73) | pub fn build_group_list_request_packet(&self, vec_cookie: &[u8]) -> Pack...
  function build_group_member_list_request_packet (line 107) | pub fn build_group_member_list_request_packet(&self, group_code: i64, ne...
  function build_edit_group_tag_packet (line 135) | pub fn build_edit_group_tag_packet(
  function build_delete_friend_packet (line 169) | pub fn build_delete_friend_packet(&self, del_uin: i64) -> Packet {
  function build_friend_list_set_group_req_packet (line 194) | fn build_friend_list_set_group_req_packet(&self, req_type: i32, body: By...
  function build_friend_list_add_group_req_packet (line 222) | pub fn build_friend_list_add_group_req_packet(&self, sort_id: u8, group_...
  function build_friend_list_rename_group_req_packet (line 232) | pub fn build_friend_list_rename_group_req_packet(
  function build_friend_list_del_group_req_packet (line 246) | pub fn build_friend_list_del_group_req_packet(&self, group_id: u8) -> Pa...

FILE: ricq-core/src/command/friendlist/decoder.rs
  function decode_friend_group_list_response (line 10) | pub fn decode_friend_group_list_response(
  function decode_group_list_response (line 57) | pub fn decode_group_list_response(&self, mut payload: Bytes) -> RQResult...
  function decode_group_member_list_response (line 91) | pub fn decode_group_member_list_response(
  function decode_remove_friend (line 135) | pub fn decode_remove_friend(&self, mut payload: Bytes) -> RQResult<jce::...

FILE: ricq-core/src/command/friendlist/mod.rs
  type FriendListResponse (line 11) | pub struct FriendListResponse {
  type GroupListResponse (line 23) | pub struct GroupListResponse {
  type GroupMemberListResponse (line 29) | pub struct GroupMemberListResponse {

FILE: ricq-core/src/command/group_anonymous_generate_nick/builder.rs
  function build_get_anony_info_request (line 7) | pub fn build_get_anony_info_request(&self, group_code: i64) -> Packet {

FILE: ricq-core/src/command/group_anonymous_generate_nick/decoder.rs
  function decode_get_anony_info_response (line 9) | pub fn decode_get_anony_info_response(&self, payload: Bytes) -> RQResult...

FILE: ricq-core/src/command/group_member_card/builder.rs
  function build_group_member_info_request_packet (line 7) | pub fn build_group_member_info_request_packet(&self, group_code: i64, ui...

FILE: ricq-core/src/command/group_member_card/decoder.rs
  function decode_group_member_info_response (line 9) | pub fn decode_group_member_info_response(&self, payload: Bytes) -> RQRes...

FILE: ricq-core/src/command/heartbeat/builder.rs
  function build_heartbeat_packet (line 5) | pub fn build_heartbeat_packet(&self) -> Packet {

FILE: ricq-core/src/command/img_store/builder.rs
  function build_group_image_store_packet (line 7) | pub fn build_group_image_store_packet(

FILE: ricq-core/src/command/img_store/decoder.rs
  function decode_group_image_store_response (line 10) | pub fn decode_group_image_store_response(

FILE: ricq-core/src/command/img_store/mod.rs
  type GroupImageStoreResp (line 7) | pub enum GroupImageStoreResp {

FILE: ricq-core/src/command/long_conn/builder.rs
  function build_off_pic_up_packet (line 7) | pub fn build_off_pic_up_packet(

FILE: ricq-core/src/command/long_conn/decoder.rs
  function decode_off_pic_up_response (line 11) | pub fn decode_off_pic_up_response(&self, payload: Bytes) -> RQResult<Off...

FILE: ricq-core/src/command/long_conn/mod.rs
  type OffPicUpResp (line 7) | pub enum OffPicUpResp {

FILE: ricq-core/src/command/longmsg/builder.rs
  function build_long_req (line 5) | pub fn build_long_req(&self, dst_uin: i64, msg_content: Vec<u8>, msg_uke...

FILE: ricq-core/src/command/message_svc/builder.rs
  function build_group_sending_packet (line 10) | pub fn build_group_sending_packet(
  function sync_cookie (line 57) | fn sync_cookie(&self, time: i64) -> Vec<u8> {
  function build_get_message_request_packet (line 76) | pub fn build_get_message_request_packet(&self, flag: i32, time: i64) -> ...
  function build_delete_message_request_packet (line 96) | pub fn build_delete_message_request_packet(&self, items: Vec<pb::Message...
  function build_send_message_packet (line 102) | pub fn build_send_message_packet(
  function build_get_group_msg_request (line 140) | pub fn build_get_group_msg_request(
  function build_friend_recall_packet (line 156) | pub fn build_friend_recall_packet(
  function build_group_recall_packet (line 192) | pub fn build_group_recall_packet(

FILE: ricq-core/src/command/message_svc/decoder.rs
  function decode_svc_notify (line 9) | pub fn decode_svc_notify(&self, mut payload: Bytes) -> RQResult<jce::Req...
  function decode_force_offline (line 25) | pub fn decode_force_offline(
  function decode_message_svc_packet (line 43) | pub fn decode_message_svc_packet(

FILE: ricq-core/src/command/message_svc/mod.rs
  type MessageSyncResponse (line 6) | pub struct MessageSyncResponse {

FILE: ricq-core/src/command/multi_msg/builder.rs
  function build_multi_msg_apply_down_req (line 16) | pub fn build_multi_msg_apply_down_req(&self, res_id: String) -> Packet {
  function build_multi_msg_apply_up_req (line 35) | pub fn build_multi_msg_apply_up_req(
  function calculate_validation_data (line 62) | pub fn calculate_validation_data(
  function pack_forward_msg (line 91) | fn pack_forward_msg(
  function pack_msg (line 136) | fn pack_msg(&self, node: super::MessageNode, group_code: i64) -> pb::msg...

FILE: ricq-core/src/command/multi_msg/decoder.rs
  function decode_multi_msg_apply_down_resp (line 7) | pub fn decode_multi_msg_apply_down_resp(
  function decode_multi_msg_apply_up_resp (line 17) | pub fn decode_multi_msg_apply_up_resp(

FILE: ricq-core/src/command/multi_msg/mod.rs
  type ForwardMessage (line 10) | pub enum ForwardMessage {
    method from (line 37) | fn from(n: MessageNode) -> Self {
    method from (line 50) | fn from(f: ForwardNode) -> Self {
  function gen_forward_preview (line 15) | pub fn gen_forward_preview(messages: &[ForwardMessage]) -> String {
  type MessageNode (line 29) | pub struct MessageNode {
  type ForwardNode (line 42) | pub struct ForwardNode {
  type PackedMessage (line 55) | struct PackedMessage {

FILE: ricq-core/src/command/oidb_svc/builder.rs
  function build_update_profile_detail_packet (line 10) | pub fn build_update_profile_detail_packet(&self, profile: ProfileDetailU...
  function build_group_info_request_packet (line 25) | pub fn build_group_info_request_packet(&self, group_codes: Vec<i64>) -> ...
  function build_group_mute_packet (line 70) | pub fn build_group_mute_packet(
  function build_group_operation_packet (line 87) | fn build_group_operation_packet(&self, body: pb::oidb::D89aReqBody) -> P...
  function build_group_mute_all_packet (line 93) | pub fn build_group_mute_all_packet(&self, group_code: i64, mute: bool) -...
  function build_group_name_update_packet (line 107) | pub fn build_group_name_update_packet(&self, group_code: i64, name: Stri...
  function build_group_memo_update_packet (line 120) | pub fn build_group_memo_update_packet(&self, group_code: i64, memo: Stri...
  function build_group_kick_packet (line 133) | pub fn build_group_kick_packet(
  function build_group_poke_packet (line 160) | pub fn build_group_poke_packet(&self, group_code: i64, target: i64) -> P...
  function build_friend_poke_packet (line 171) | pub fn build_friend_poke_packet(&self, target: i64) -> Packet {
  function build_group_admin_set_packet (line 182) | pub fn build_group_admin_set_packet(&self, group_code: i64, member: i64,...
  function build_group_invite_packet (line 192) | pub fn build_group_invite_packet(&self, group_code: i64, uin: i64) -> Pa...
  function build_group_at_all_remain_request_packet (line 206) | pub fn build_group_at_all_remain_request_packet(&self, group_code: i64) ...
  function build_edit_special_title_packet (line 219) | pub fn build_edit_special_title_packet(
  function build_translate_request_packet (line 241) | pub fn build_translate_request_packet(
  function build_essence_msg_operate_packet (line 259) | pub fn build_essence_msg_operate_packet(
  function build_image_ocr_request_packet (line 278) | pub fn build_image_ocr_request_packet(
  function build_share_music_request_packet (line 304) | pub fn build_share_music_request_packet(
  function build_share_link_request_packet (line 351) | pub fn build_share_link_request_packet(
  function build_get_group_admin_list_request_packet (line 396) | pub fn build_get_group_admin_list_request_packet(&self, group_code: u64)...
  function build_group_sign_in_packet (line 413) | pub fn build_group_sign_in_packet(&self, group_code: i64) -> Packet {
  function build_group_file_list_request_packet (line 426) | pub fn build_group_file_list_request_packet(
  function build_group_file_download_request_packet (line 453) | pub fn build_group_file_download_request_packet(
  function build_group_file_count_request_packet (line 473) | pub fn build_group_file_count_request_packet(&self, group_code: u64) -> ...

FILE: ricq-core/src/command/oidb_svc/decoder.rs
  function decode_group_info_response (line 17) | pub fn decode_group_info_response(&self, payload: Bytes) -> RQResult<Vec...
  function decode_group_at_all_remain_response (line 44) | pub fn decode_group_at_all_remain_response(
  function decode_translate_response (line 58) | pub fn decode_translate_response(&self, payload: Bytes) -> RQResult<Vec<...
  function decode_essence_msg_response (line 65) | pub fn decode_essence_msg_response(&self, payload: Bytes) -> RQResult<pb...
  function decode_image_ocr_response (line 72) | pub fn decode_image_ocr_response(&self, payload: Bytes) -> RQResult<OcrR...
  function decode_get_group_admin_list_response (line 86) | pub fn decode_get_group_admin_list_response(
  function decode_group_file_list_response (line 110) | pub fn decode_group_file_list_response(&self, payload: Bytes) -> RQResul...
  function decode_group_file_download_response (line 165) | pub fn decode_group_file_download_response(
  function decode_group_file_count_response (line 181) | pub fn decode_group_file_count_response(&self, payload: Bytes) -> RQResu...

FILE: ricq-core/src/command/oidb_svc/mod.rs
  type GroupAtAllRemainInfo (line 10) | pub struct GroupAtAllRemainInfo {
  type OcrResponse (line 16) | pub struct OcrResponse {
  type ProfileDetailUpdate (line 23) | pub struct ProfileDetailUpdate(pub HashMap<u16, Vec<u8>>);
    method new (line 26) | pub fn new() -> Self {
    method name (line 29) | pub fn name(&mut self, value: String) {
    method email (line 32) | pub fn email(&mut self, value: String) {
    method personal_note (line 35) | pub fn personal_note(&mut self, value: String) {
    method company (line 38) | pub fn company(&mut self, value: String) {
    method college (line 41) | pub fn college(&mut self, value: String) {
  type ShareTarget (line 46) | pub enum ShareTarget {
    method send_type (line 53) | pub fn send_type(&self) -> u32 {
  type MusicShare (line 63) | pub struct MusicShare {
  type MusicVersion (line 73) | pub struct MusicVersion {
    constant QQ (line 83) | pub const QQ: MusicVersion = MusicVersion {
    constant NETEASE (line 92) | pub const NETEASE: MusicVersion = MusicVersion {
    constant MIGU (line 101) | pub const MIGU: MusicVersion = MusicVersion {
    constant KUGOU (line 110) | pub const KUGOU: MusicVersion = MusicVersion {
    constant KUWO (line 119) | pub const KUWO: MusicVersion = MusicVersion {
  type LinkShare (line 130) | pub struct LinkShare {

FILE: ricq-core/src/command/online_push/builder.rs
  function build_delete_online_push_packet (line 12) | pub fn build_delete_online_push_packet(
  function build_sid_ticket_expired_response (line 51) | pub fn build_sid_ticket_expired_response(&self, seq: i32) -> Packet {

FILE: ricq-core/src/command/online_push/decoder.rs
  function decode_group_message_packet (line 13) | pub fn decode_group_message_packet(&self, payload: Bytes) -> RQResult<Gr...
  function decode_online_push_req_packet (line 47) | pub fn decode_online_push_req_packet(&self, mut payload: Bytes) -> RQRes...
  function decode_online_push_trans_packet (line 65) | pub fn decode_online_push_trans_packet(&self, payload: Bytes) -> RQResul...
  function decode_c2c_sync_packet (line 159) | pub fn decode_c2c_sync_packet(&self, payload: Bytes) -> RQResult<pb::msg...

FILE: ricq-core/src/command/online_push/mod.rs
  type ReqPush (line 8) | pub struct ReqPush {
  type PushTransInfo (line 14) | pub enum PushTransInfo {
  type OnlinePushTrans (line 21) | pub struct OnlinePushTrans {
  type GroupMessagePart (line 29) | pub struct GroupMessagePart {

FILE: ricq-core/src/command/pb_message_svc/builder.rs
  function build_group_msg_readed_packet (line 7) | pub fn build_group_msg_readed_packet(&self, group_code: i64, msg_seq: i3...
  function build_friend_msg_readed_packet (line 19) | pub fn build_friend_msg_readed_packet(&self, uin: i64, time: i64) -> Pac...

FILE: ricq-core/src/command/profile_service/builder.rs
  function build_system_msg_new_group_packet (line 9) | pub fn build_system_msg_new_group_packet(&self, suspicious: bool) -> Pac...
  function build_system_msg_new_friend_packet (line 42) | pub fn build_system_msg_new_friend_packet(&self) -> Packet {
  function build_system_msg_group_action_packet (line 64) | pub fn build_system_msg_group_action_packet(
  function build_system_msg_friend_action_packet (line 98) | pub fn build_system_msg_friend_action_packet(
  function build_quit_group_packet (line 124) | pub fn build_quit_group_packet(&self, group_code: i64) -> Packet {
  function build_get_rich_sig_request_packet (line 155) | pub fn build_get_rich_sig_request_packet(&self, user_ids: Vec<i64>) -> P...

FILE: ricq-core/src/command/profile_service/decoder.rs
  function decode_system_msg_group_packet (line 12) | pub fn decode_system_msg_group_packet(&self, payload: Bytes) -> RQResult...
  function decode_system_msg_friend_packet (line 88) | pub fn decode_system_msg_friend_packet(
  function decode_get_rich_sig_response_packet (line 115) | pub fn decode_get_rich_sig_response_packet(

FILE: ricq-core/src/command/profile_service/mod.rs
  type GroupSystemMessages (line 9) | pub struct GroupSystemMessages {
  type SelfInvited (line 16) | pub struct SelfInvited {
  type JoinGroupRequest (line 29) | pub struct JoinGroupRequest {
  type FriendSystemMessages (line 44) | pub struct FriendSystemMessages {
  type NewFriendRequest (line 49) | pub struct NewFriendRequest {
  type RichSigInfo (line 57) | pub struct RichSigInfo {
    method get_signature (line 72) | pub fn get_signature(&self) -> String {

FILE: ricq-core/src/command/ptt_center_svr/builder.rs
  function build_group_video_store_packet (line 7) | pub fn build_group_video_store_packet(
  function build_short_video_up_req (line 29) | pub fn build_short_video_up_req(
  function build_c2c_ptt_down_req (line 61) | pub fn build_c2c_ptt_down_req(&self, sender_uin: i64, file_uuid: Vec<u8>...

FILE: ricq-core/src/command/ptt_center_svr/decoder.rs
  function decode_group_video_store_response (line 8) | pub fn decode_group_video_store_response(
  function decode_c2c_ptt_down (line 18) | pub fn decode_c2c_ptt_down(&self, payload: Bytes) -> RQResult<String> {

FILE: ricq-core/src/command/ptt_store/builder.rs
  function build_group_try_up_ptt_req (line 9) | pub fn build_group_try_up_ptt_req(
  function build_friend_try_up_ptt_req (line 44) | pub fn build_friend_try_up_ptt_req(
  function build_group_ptt_down_req (line 78) | pub fn build_group_ptt_down_req(&self, group_code: i64, file_md5: Vec<u8...

FILE: ricq-core/src/command/ptt_store/decoder.rs
  function decode_group_try_up_ptt_resp (line 8) | pub fn decode_group_try_up_ptt_resp(&self, payload: Bytes) -> RQResult<V...
  function decode_friend_try_up_ptt_resp (line 17) | pub fn decode_friend_try_up_ptt_resp(&self, payload: Bytes) -> RQResult<...
  function decode_group_ptt_down (line 24) | pub fn decode_group_ptt_down(&self, payload: Bytes) -> RQResult<String> {

FILE: ricq-core/src/command/reg_prxy_svc/builder.rs
  function build_get_offline_msg_request_packet (line 14) | pub fn build_get_offline_msg_request_packet(&self, last_message_time: i6...
  function build_sync_msg_request_packet (line 80) | pub fn build_sync_msg_request_packet(&self, last_message_time: i64) -> P...

FILE: ricq-core/src/command/reg_prxy_svc/decoder.rs
  function decode_push_param_packet (line 8) | pub fn decode_push_param_packet(&self, payload: &[u8]) -> RQResult<Vec<O...

FILE: ricq-core/src/command/signature/builder.rs
  function build_update_signature_packet (line 7) | pub fn build_update_signature_packet(&self, signature: String) -> Packet {

FILE: ricq-core/src/command/stat_svc/builder.rs
  function build_set_online_status_packet (line 15) | pub fn build_set_online_status_packet(
  function build_client_register_packet (line 61) | pub fn build_client_register_packet(&self) -> Packet {
  function svc_req_register_pkt (line 107) | fn svc_req_register_pkt(&self, svc: jce::SvcReqRegister) -> jce::Request...
  function build_device_list_request_packet (line 127) | pub fn build_device_list_request_packet(&self) -> Packet {
  function build_msf_force_offline_rsp (line 154) | pub fn build_msf_force_offline_rsp(&self, uin: i64, seq_no: i64) -> Pack...

FILE: ricq-core/src/command/stat_svc/decoder.rs
  function decode_client_register_response (line 8) | pub fn decode_client_register_response(
  function decode_dev_list_response (line 28) | pub fn decode_dev_list_response(
  function decode_msf_force_offline (line 61) | pub fn decode_msf_force_offline(

FILE: ricq-core/src/command/stat_svc/mod.rs
  type Status (line 5) | pub struct Status {
    method from (line 23) | fn from(s: OnlineStatus) -> Self {
    method from (line 56) | fn from(s: ExtOnlineStatus) -> Self {
    method from (line 72) | fn from(s: CustomOnlineStatus) -> Self {
  type OnlineStatus (line 12) | pub enum OnlineStatus {
  type ExtOnlineStatus (line 33) | pub enum ExtOnlineStatus {
  type CustomOnlineStatus (line 66) | pub struct CustomOnlineStatus {

FILE: ricq-core/src/command/summary_card/builder.rs
  function build_summary_card_request_packet (line 12) | pub fn build_summary_card_request_packet(&self, target: i64) -> Packet {

FILE: ricq-core/src/command/summary_card/decoder.rs
  function decode_summary_card_response (line 9) | pub fn decode_summary_card_response(&self, mut payload: Bytes) -> RQResu...

FILE: ricq-core/src/command/visitor_svc/builder.rs
  function build_send_like_packet (line 12) | pub fn build_send_like_packet(

FILE: ricq-core/src/command/wtlogin/builder.rs
  function build_qrcode_fetch_request_packet (line 15) | pub fn build_qrcode_fetch_request_packet(&self) -> Packet {
  function build_qrcode_result_query_request_packet (line 89) | pub fn build_qrcode_result_query_request_packet(&self, sig: &[u8]) -> Pa...
  function build_qrcode_login_packet (line 126) | pub fn build_qrcode_login_packet(
  function build_device_lock_login_packet (line 242) | pub fn build_device_lock_login_packet(&self) -> Packet {
  function build_captcha_packet (line 272) | pub fn build_captcha_packet(&self, result: String, sign: &[u8]) -> Packet {
  function build_sms_request_packet (line 303) | pub fn build_sms_request_packet(&self) -> Packet {
  function build_sms_code_submit_packet (line 336) | pub fn build_sms_code_submit_packet(&self, code: &str, sign: &[u8]) -> P...
  function build_ticket_submit_packet (line 371) | pub fn build_ticket_submit_packet(&self, ticket: &str, sign: &[u8]) -> P...
  function build_request_tgtgt_no_pic_sig_packet (line 405) | pub fn build_request_tgtgt_no_pic_sig_packet(&self) -> Packet {
  function build_request_change_sig_packet (line 525) | pub fn build_request_change_sig_packet(&self, main_sig_map: Option<u32>)...
  function build_login_packet (line 619) | pub fn build_login_packet(
  function build_code2d_request_packet (line 744) | pub fn build_code2d_request_packet(seq: u32, j: u64, cmd: u16, body: &[u...
  type DeviceToPb (line 761) | pub trait DeviceToPb {
    method gen_pb_data (line 762) | fn gen_pb_data(&self) -> Bytes;
    method gen_pb_data (line 766) | fn gen_pb_data(&self) -> Bytes {

FILE: ricq-core/src/command/wtlogin/decoder.rs
  function decode_trans_emp_response (line 8) | pub fn decode_trans_emp_response(&self, mut payload: Bytes) -> RQResult<...
  function decode_login_response (line 91) | pub fn decode_login_response(&self, mut reader: Bytes) -> RQResult<Login...
  function decode_exchange_emp_response (line 100) | pub fn decode_exchange_emp_response(&self, mut payload: Bytes) -> RQResu...

FILE: ricq-core/src/command/wtlogin/mod.rs
  type QRCodeState (line 18) | pub enum QRCodeState {
  type QRCodeImageFetch (line 28) | pub struct QRCodeImageFetch {
  type QRCodeConfirmed (line 34) | pub struct QRCodeConfirmed {
  type ImageCaptcha (line 43) | pub struct ImageCaptcha {
  type LoginResponse (line 49) | pub enum LoginResponse {
    method decode (line 120) | pub fn decode(
  type LoginSuccess (line 63) | pub struct LoginSuccess {
  type LoginNeedCaptcha (line 87) | pub struct LoginNeedCaptcha {
  type LoginDeviceLocked (line 95) | pub struct LoginDeviceLocked {
  type LoginDeviceLockLogin (line 106) | pub struct LoginDeviceLockLogin {
  type LoginUnknownStatus (line 113) | pub struct LoginUnknownStatus {
  function t546_to_t547 (line 222) | pub fn t546_to_t547(mut data: Bytes) -> Bytes {

FILE: ricq-core/src/command/wtlogin/tlv_reader.rs
  type T161 (line 9) | pub struct T161 {
  type T113 (line 16) | pub struct T113 {
  type T125 (line 20) | pub struct T125 {
  type T11A (line 25) | pub struct T11A {
  type T199 (line 32) | pub struct T199 {
  type T200 (line 37) | pub struct T200 {
  type T512 (line 42) | pub struct T512 {
  type T531 (line 47) | pub struct T531 {
  function decode_t161 (line 52) | pub fn decode_t161(mut data: Bytes) -> T161 {
  function decode_t119 (line 60) | pub fn decode_t119(data: &[u8], ek: &[u8]) -> HashMap<u16, Bytes> {
  function decode_t113 (line 66) | pub fn decode_t113(mut data: Bytes) -> T113 {
  function decode_t186 (line 72) | pub fn decode_t186(_: &[u8]) {}
  function read_t125 (line 75) | pub fn read_t125(data: &[u8]) -> T125 {
  function read_t11a (line 82) | pub fn read_t11a(mut data: Bytes) -> T11A {
  function read_t199 (line 96) | pub fn read_t199(mut data: Bytes) -> T199 {
  function read_t200 (line 102) | pub fn read_t200(mut data: Bytes) -> T200 {
  function read_t512 (line 108) | pub fn read_t512(mut reader: Bytes) -> T512 {
  function read_t531 (line 133) | pub fn read_t531(mut data: Bytes) -> T531 {
  function select (line 148) | pub fn select(a: Option<&Bytes>, b: &[u8]) -> Bytes {

FILE: ricq-core/src/command/wtlogin/tlv_writer.rs
  function tlv (line 9) | pub fn tlv<'a, B: BufMut + WriteLV, W: PacketWriter<B> + 'a>(
  function t1 (line 19) | pub fn t1<B: BufMut + WriteLV>(uin: u32, ip: &[u8]) -> impl PacketWriter...
  function t1b (line 33) | pub fn t1b<B: BufMut + WriteLV>(
  function t1d (line 54) | pub fn t1d<B: BufMut + WriteLV>(misc_bitmap: u32) -> impl PacketWriter<B> {
  function t1f (line 64) | pub fn t1f<'a, B: BufMut + WriteLV>(
  function t2 (line 83) | pub fn t2<B: BufMut + WriteLV>(result: String, sign: &[u8]) -> impl Pack...
  function t8 (line 91) | pub fn t8<B: BufMut + WriteLV>(local_id: u32) -> impl PacketWriter<B> {
  function t10a (line 99) | pub fn t10a<B: BufMut + WriteLV>(arr: &[u8]) -> impl PacketWriter<B> + '_ {
  function t16 (line 103) | pub fn t16<'a, B: BufMut + WriteLV>(
  function t16a (line 123) | pub fn t16a<B: BufMut + WriteLV>(arr: &[u8]) -> impl PacketWriter<B> + '_ {
  function t16e (line 127) | pub fn t16e<B: BufMut + WriteLV>(build_model: &[u8]) -> impl PacketWrite...
  function t17a (line 131) | pub fn t17a<B: BufMut + WriteLV>(value: i32) -> impl PacketWriter<B> {
  function t17c (line 135) | pub fn t17c<B: BufMut + WriteLV>(code: &str) -> impl PacketWriter<B> + '_ {
  function t18 (line 141) | pub fn t18<B: BufMut + WriteLV>(app_id: u32, uin: u32) -> impl PacketWri...
  function t33 (line 153) | pub fn t33<B: BufMut + WriteLV>(guid: &[u8]) -> impl PacketWriter<B> + '_ {
  function t35 (line 157) | pub fn t35<B: BufMut + WriteLV>(product_type: u32) -> impl PacketWriter<...
  function t52d (line 161) | pub fn t52d<B: BufMut + WriteLV>(dev_info: &[u8]) -> impl PacketWriter<B...
  function t100 (line 165) | pub fn t100<B: BufMut + WriteLV>(
  function t104 (line 180) | pub fn t104<B: BufMut + WriteLV>(data: &[u8]) -> impl PacketWriter<B> + ...
  function t106 (line 184) | pub fn t106<'a, B: BufMut + WriteLV>(
  function t107 (line 235) | pub fn t107<B: BufMut + WriteLV>(pic_type: u16) -> impl PacketWriter<B> {
  function t108 (line 244) | pub fn t108<B: BufMut + WriteLV>(ksid: &[u8]) -> impl PacketWriter<B> + ...
  function t109 (line 248) | pub fn t109<B: BufMut + WriteLV>(android_id: &str) -> impl PacketWriter<...
  function t112 (line 254) | pub fn t112<B: BufMut + WriteLV>(uin: i64) -> impl PacketWriter<B> {
  function t116 (line 260) | pub fn t116<B: BufMut + WriteLV>(misc_bitmap: u32, sub_sig_map: u32) -> ...
  function t124 (line 270) | pub fn t124<'a, B: BufMut + WriteLV>(
  function t128 (line 286) | pub fn t128<'a, B: BufMut + WriteLV>(
  function t141 (line 307) | pub fn t141<'a, B: BufMut + WriteLV>(sim_info: &'a str, apn: &'a str) ->...
  function t142 (line 316) | pub fn t142<B: BufMut + WriteLV>(apk_id: &str) -> impl PacketWriter<B> +...
  function t143 (line 323) | pub fn t143<B: BufMut + WriteLV>(arr: &[u8]) -> impl PacketWriter<B> + '_ {
  function t144 (line 327) | pub fn t144<'a, B: BufMut + WriteLV>(
  function t145 (line 366) | pub fn t145<B: BufMut + WriteLV>(guid: &[u8]) -> impl PacketWriter<B> + ...
  function t147 (line 370) | pub fn t147<'a, B: BufMut + WriteLV>(
  function t154 (line 382) | pub fn t154<B: BufMut + WriteLV>(seq: u16) -> impl PacketWriter<B> {
  function t166 (line 386) | pub fn t166<B: BufMut + WriteLV>(image_type: u8) -> impl PacketWriter<B> {
  function t174 (line 390) | pub fn t174<B: BufMut + WriteLV>(data: &[u8]) -> impl PacketWriter<B> + ...
  function t177 (line 394) | pub fn t177<B: BufMut + WriteLV>(build_time: u32, sdk_version: &str) -> ...
  function t187 (line 402) | pub fn t187<B: BufMut + WriteLV>(mac_address: &str) -> impl PacketWriter...
  function t188 (line 408) | pub fn t188<B: BufMut + WriteLV>(android_id: &str) -> impl PacketWriter<...
  function t191 (line 414) | pub fn t191<B: BufMut + WriteLV>(k: u8) -> impl PacketWriter<B> {
  function t193 (line 418) | pub fn t193<B: BufMut + WriteLV>(ticket: &str) -> impl PacketWriter<B> +...
  function t194 (line 422) | pub fn t194<B: BufMut + WriteLV>(imsi_md5: &[u8]) -> impl PacketWriter<B...
  function t197 (line 426) | pub fn t197<B: BufMut + WriteLV>() -> impl PacketWriter<B> {
  function t198 (line 430) | pub fn t198<B: BufMut + WriteLV>() -> impl PacketWriter<B> {
  function t202 (line 434) | pub fn t202<'a, B: BufMut + WriteLV>(
  function t318 (line 444) | pub fn t318<B: BufMut + WriteLV>(tgt_qr: &[u8]) -> impl PacketWriter<B> ...
  function t400 (line 448) | pub fn t400<'a, B: BufMut + WriteLV>(
  function t401 (line 473) | pub fn t401<B: BufMut + WriteLV>(d: &[u8]) -> impl PacketWriter<B> + '_ {
  function t511 (line 477) | pub fn t511<B: BufMut + WriteLV>(domains: Vec<&str>) -> impl PacketWrite...
  function t516 (line 520) | pub fn t516<B: BufMut + WriteLV>() -> impl PacketWriter<B> {
  function t521 (line 524) | pub fn t521<B: BufMut + WriteLV>(i: u32) -> impl PacketWriter<B> {
  function t525 (line 531) | pub fn t525<'a, B: BufMut + WriteLV, T: PacketWriter<B> + 'a>(
  function t536 (line 540) | pub fn t536<B: BufMut + WriteLV>(login_extra_data: &[u8]) -> impl Packet...
  function guid_flag (line 544) | pub fn guid_flag() -> u32 {
  constant GUID (line 555) | const GUID: [u8; 16] = [
  constant TGTGT_KEY (line 558) | const TGTGT_KEY: [u8; 16] = [
  constant UIN (line 561) | const UIN: u32 = 349195854;
  constant OS_NAME (line 562) | const OS_NAME: &str = "android";
  constant OS_VERSION (line 563) | const OS_VERSION: &str = "7.1.2";
  constant SIM_INFO (line 564) | const SIM_INFO: &str = "T-Mobile";
  constant IMEI (line 565) | const IMEI: &str = "468356291846738";
  constant IMEI_MD5 (line 566) | const IMEI_MD5: &[u8] = "9792b1bba1867318bf782af418306ef8".as_bytes();
  constant WIFI_BSSID (line 567) | const WIFI_BSSID: &str = "00:50:56:C0:00:08";
  constant WIFI_SSID (line 568) | const WIFI_SSID: &str = "<unknown ssid>";
  constant APN (line 569) | const APN: &str = "wifi";
  constant APK_SIGN (line 570) | const APK_SIGN: [u8; 16] = [
  constant APK_ID (line 574) | const APK_ID: &str = "com.tencent.mobileqq";
  constant APP_ID (line 575) | const APP_ID: u32 = 537066738;
  constant SUB_APP_ID (line 576) | const SUB_APP_ID: u32 = 537066738;
  constant SSO_VERSION (line 577) | const SSO_VERSION: u32 = 15;
  constant SDK_VERSION (line 578) | const SDK_VERSION: &str = "6.0.0.2454";
  constant MISC_BITMAP (line 579) | const MISC_BITMAP: u32 = 184024956;
  constant SUB_SIG_MAP (line 580) | const SUB_SIG_MAP: u32 = 0x10400;
  constant MAIN_SIG_MAP (line 581) | const MAIN_SIG_MAP: u32 = 34869472;
  constant MAC_ADDRESS (line 582) | const MAC_ADDRESS: &str = "00:50:56:C0:00:08";
  constant IS_ROOT (line 583) | const IS_ROOT: bool = false;
  constant ANDROID_ID (line 584) | const ANDROID_ID: &str = "QKQ1.191117.002";
  constant APK_VERSION_NAME (line 585) | const APK_VERSION_NAME: &str = "2.0.5";
  constant DEV_INFO (line 586) | const DEV_INFO: &[u8] = "dev_info_dev_info_dev_info_dev_info_dev_info_"....
  constant BUILD_MODEL (line 587) | const BUILD_MODEL: &str = "mirai";
  constant BUILD_BRAND (line 588) | const BUILD_BRAND: &str = "mamoe";
  constant OS_TYPE (line 589) | const OS_TYPE: &str = "android";
  function test_param (line 592) | fn test_param() {
  function get_buf (line 596) | fn get_buf<W: PacketWriter<Vec<u8>>>(w: W) -> Vec<u8> {
  function test_t1 (line 602) | fn test_t1() {
  function test_t1b (line 610) | fn test_t1b() {
  function test_t1d (line 618) | fn test_t1d() {
  function test_t1f (line 626) | fn test_t1f() {
  function test_t2 (line 634) | fn test_t2() {
  function test_t8 (line 642) | fn test_t8() {
  function test_t10a (line 650) | fn test_t10a() {
  function test_t16 (line 658) | fn test_t16() {
  function test_t16a (line 674) | fn test_t16a() {
  function test_t16e (line 682) | fn test_t16e() {
  function test_t17a (line 690) | fn test_t17a() {
  function test_t17c (line 698) | fn test_t17c() {
  function test_t18 (line 706) | fn test_t18() {
  function test_t33 (line 714) | fn test_t33() {
  function test_t35 (line 722) | fn test_t35() {
  function test_t52d (line 731) | fn test_t52d() {
  function test_t100 (line 739) | fn test_t100() {
  function test_t104 (line 747) | fn test_t104() {
  function test_t106 (line 755) | fn test_t106() {
  function test_t107 (line 773) | fn test_t107() {
  function test_t108 (line 781) | fn test_t108() {
  function test_t109 (line 789) | fn test_t109() {
  function test_t116 (line 797) | fn test_t116() {
  function test_t124 (line 805) | fn test_t124() {
  function test_t128 (line 813) | fn test_t128() {
  function test_t141 (line 821) | fn test_t141() {
  function test_t142 (line 829) | fn test_t142() {
  function test_t143 (line 837) | fn test_t143() {
  function test_t144 (line 845) | fn test_t144() {
  function test_t145 (line 868) | fn test_t145() {
  function test_t147 (line 876) | fn test_t147() {
  function test_t154 (line 884) | fn test_t154() {
  function test_t166 (line 894) | fn test_t166() {
  function test_t174 (line 902) | fn test_t174() {
  function test_t177 (line 910) | fn test_t177() {
  function test_t187 (line 918) | fn test_t187() {
  function test_t188 (line 926) | fn test_t188() {
  function test_t191 (line 934) | fn test_t191() {
  function test_t193 (line 942) | fn test_t193() {
  function test_t194 (line 950) | fn test_t194() {
  function test_t197 (line 958) | fn test_t197() {
  function test_t198 (line 966) | fn test_t198() {
  function test_t202 (line 974) | fn test_t202() {
  function test_t400 (line 982) | fn test_t400() {
  function test_t401 (line 990) | fn test_t401() {
  function test_t511 (line 998) | fn test_t511() {
  function test_t516 (line 1021) | fn test_t516() {
  function test_t521 (line 1029) | fn test_t521() {
  function test_t525 (line 1037) | fn test_t525() {
  function test_tlv (line 1045) | fn test_tlv() {

FILE: ricq-core/src/common.rs
  function group_code2uin (line 3) | pub fn group_code2uin(code: i64) -> i64 {
  function group_uin2code (line 28) | pub fn group_uin2code(uin: i64) -> i64 {
  type RQAddr (line 51) | pub struct RQAddr(pub u32, pub u16);
    method from (line 62) | fn from(addr: SocketAddr) -> Self {
  method from (line 54) | fn from(addr: RQAddr) -> Self {
  function test_group_code2uin (line 74) | fn test_group_code2uin() {
  function test_group_uin2code (line 79) | fn test_group_uin2code() {

FILE: ricq-core/src/crypto/encrypt.rs
  type IEncryptMethod (line 10) | pub trait IEncryptMethod {
    method id (line 11) | fn id(&self) -> u8;
    method do_encrypt (line 12) | fn do_encrypt(&self, data: &[u8], key: &[u8]) -> Vec<u8>;
    method id (line 51) | fn id(&self) -> u8 {
    method do_encrypt (line 55) | fn do_encrypt(&self, data: &[u8], key: &[u8]) -> Vec<u8> {
    method id (line 82) | fn id(&self) -> u8 {
    method do_encrypt (line 86) | fn do_encrypt(&self, data: &[u8], key: &[u8]) -> Vec<u8> {
  type EncryptECDH (line 16) | pub struct EncryptECDH {
    method generate_key (line 35) | pub fn generate_key(&mut self, s_pub_key: &str) {
  method default (line 23) | fn default() -> Self {
  type EncryptSession (line 69) | pub struct EncryptSession {
    method new (line 74) | pub fn new(t133: &[u8]) -> EncryptSession {
  function test_ecdh_generate_key (line 101) | fn test_ecdh_generate_key() {

FILE: ricq-core/src/crypto/qqtea.rs
  function qqtea_encrypt (line 7) | pub fn qqtea_encrypt(text: &[u8], key: &[u8]) -> Vec<u8> {
  function qqtea_decrypt (line 49) | pub fn qqtea_decrypt(text: &[u8], key: &[u8]) -> Vec<u8> {
  constant TEA_DELTA (line 92) | const TEA_DELTA: u32 = 0x9E3779B9;
  type Tea16 (line 94) | pub struct Tea16 {
    method encrypt (line 100) | pub fn encrypt(&self, n: u64) -> u64 {
    method decrypt (line 122) | pub fn decrypt(&self, n: u64) -> u64 {
    method new (line 144) | pub fn new(key: &GenericArray<u8, U16>) -> Self {
  function tea16_encrypt (line 157) | pub fn tea16_encrypt(text: &mut [u8], key: &[u8]) {
  function tea16_decrypt (line 168) | pub fn tea16_decrypt(text: &mut [u8], key: &[u8]) {

FILE: ricq-core/src/error.rs
  type RQResult (line 5) | pub type RQResult<T> = Result<T, RQError>;
  type RQError (line 8) | pub enum RQError {

FILE: ricq-core/src/hex.rs
  function decode_hex (line 4) | pub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
  function encode_hex (line 11) | pub fn encode_hex(bytes: &[u8]) -> String {
  function test_decode (line 24) | fn test_decode() {
  function test_encode (line 30) | fn test_encode() {

FILE: ricq-core/src/highway/mod.rs
  type Session (line 11) | pub struct Session {
    method next_seq (line 32) | fn next_seq(&self) -> i32 {
    method build_basehead (line 36) | pub fn build_basehead(
    method build_seghead (line 56) | pub fn build_seghead(
    method build_bdh_head (line 75) | pub fn build_bdh_head(
    method decode_rsp_head (line 100) | pub fn decode_rsp_head(&self, payload: Bytes) -> RQResult<pb::RspDataH...
    method build_heartbreak (line 104) | pub fn build_heartbreak(&self) -> Bytes {
  type BdhInput (line 21) | pub struct BdhInput {

FILE: ricq-core/src/jce/mod.rs
  type RequestPacket (line 17) | pub struct RequestPacket {
  type FriendListResponse (line 636) | pub struct FriendListResponse {
  type FriendListGroupInfo (line 649) | pub struct FriendListGroupInfo {
  type FriendListSetGroupReq (line 664) | pub struct FriendListSetGroupReq {
  type GetRichSigReq (line 675) | pub struct GetRichSigReq {
  type ReqRichInfo (line 687) | pub struct ReqRichInfo {
  type GetRichSigRes (line 696) | pub struct GetRichSigRes {
  type ResRichSigInfo (line 704) | pub struct ResRichSigInfo {
  type RespSummaryCard (line 910) | pub struct RespSummaryCard {
  type RespSummaryCardHead (line 932) | pub struct RespSummaryCardHead {
  type QQServiceReqHead (line 965) | pub struct QQServiceReqHead {
  type ReqFavorite (line 981) | pub struct ReqFavorite {
  type MsgType0x210 (line 995) | pub struct MsgType0x210 {
  type RequestPushForceOffline (line 1003) | pub struct RequestPushForceOffline {
  type RequestMSFForceOffline (line 1015) | pub struct RequestMSFForceOffline {
  type RspMSFForceOffline (line 1035) | pub struct RspMSFForceOffline {
  function sso_address_resp_decode (line 1054) | fn sso_address_resp_decode() {

FILE: ricq-core/src/lib.rs
  type Engine (line 39) | pub struct Engine {
    method new (line 51) | pub fn new(device: Device, version: Version) -> Self {
    method uin (line 64) | pub fn uin(&self) -> i64 {
    method next_seq (line 68) | pub fn next_seq(&self) -> u16 {
    method next_packet_seq (line 72) | pub fn next_packet_seq(&self) -> i32 {
    method next_group_seq (line 77) | pub fn next_group_seq(&self) -> i32 {
    method next_friend_seq (line 81) | pub fn next_friend_seq(&self) -> i32 {
    method next_group_data_trans_seq (line 85) | pub fn next_group_data_trans_seq(&self) -> i32 {
    method next_highway_apply_seq (line 89) | pub fn next_highway_apply_seq(&self) -> i32 {
    method gen_token (line 93) | pub fn gen_token(&self) -> Token {
    method load_token (line 108) | pub fn load_token(&mut self, token: Token) {

FILE: ricq-core/src/msg/elem/anonymous.rs
  type Anonymous (line 6) | pub struct Anonymous {
    method from (line 37) | fn from(e: AnonymousGroupMessage) -> Self {
  method from (line 17) | fn from(e: Anonymous) -> Self {
  method push_builder (line 31) | fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {

FILE: ricq-core/src/msg/elem/at.rs
  type At (line 11) | pub struct At {
    method new (line 17) | pub fn new(target: i64) -> Self {
    method from (line 45) | fn from(e: msg::Text) -> Self {
    method fmt (line 56) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method push_to (line 26) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {

FILE: ricq-core/src/msg/elem/face.rs
  type Face (line 11) | pub struct Face {
    method new (line 17) | pub fn new(id: i32) -> Self {
    method name (line 24) | pub fn name(id: i32) -> &'static str {
    method new_from_name (line 28) | pub fn new_from_name(name: &str) -> Option<Self> {
    method from (line 61) | fn from(e: msg::Face) -> Self {
    method from (line 67) | fn from(e: msg::MsgElemInfoServtype33) -> Self {
    method fmt (line 73) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method push_to (line 34) | fn push_to(e: Self, vec: &mut Vec<MessageElem>) {
  function test (line 86) | fn test() {

FILE: ricq-core/src/msg/elem/flash_image.rs
  type FlashImage (line 11) | pub enum FlashImage {
    method url (line 17) | pub fn url(&self) -> String {
    method from (line 54) | fn from(e: FriendImage) -> Self {
    method from (line 60) | fn from(e: GroupImage) -> Self {
    method fmt (line 66) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method push_to (line 26) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {

FILE: ricq-core/src/msg/elem/friend_image.rs
  type FriendImage (line 12) | pub struct FriendImage {
    method flash (line 25) | pub fn flash(self) -> FlashImage {
    method url (line 29) | pub fn url(&self) -> String {
    method from (line 73) | fn from(e: msg::NotOnlineImage) -> Self {
    method fmt (line 89) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  function from (line 45) | fn from(e: FriendImage) -> Self {
  method push_to (line 67) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {

FILE: ricq-core/src/msg/elem/group_image.rs
  type GroupImage (line 14) | pub struct GroupImage {
    method flash (line 31) | pub fn flash(self) -> FlashImage {
    method url (line 35) | pub fn url(&self) -> String {
    method from (line 80) | fn from(custom_face: CustomFace) -> Self {
    method fmt (line 117) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  function from (line 48) | fn from(e: GroupImage) -> Self {
  method push_to (line 74) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {
  function to_uuid (line 101) | fn to_uuid(md5: &str) -> String {
  function calculate_image_resource_id (line 112) | pub fn calculate_image_resource_id(md5: &[u8]) -> String {

FILE: ricq-core/src/msg/elem/light_app.rs
  type LightApp (line 14) | pub struct LightApp {
    method new (line 19) | pub fn new(content: String) -> Self {
    method from (line 38) | fn from(e: msg::LightApp) -> Self {
    method fmt (line 61) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  method push_to (line 25) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {

FILE: ricq-core/src/msg/elem/market_face.rs
  type MarketFace (line 10) | pub struct MarketFace {
    method from (line 45) | fn from(e: msg::MarketFace) -> Self {
    method from (line 71) | fn from(e: Dice) -> Self {
    method from (line 116) | fn from(e: FingerGuessing) -> Self {
  method push_to (line 22) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {
  type Dice (line 60) | pub struct Dice {
    method new (line 65) | pub fn new(value: i32) -> Self {
    method from (line 90) | fn from(e: MarketFace) -> Self {
  method push_to (line 101) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {
  type FingerGuessing (line 108) | pub enum FingerGuessing {
    method from (line 146) | fn from(e: MarketFace) -> Self {
  method push_to (line 140) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {

FILE: ricq-core/src/msg/elem/mod.rs
  type RQElem (line 38) | pub enum RQElem {
    method from (line 55) | fn from(elem: msg::elem::Elem) -> Self {
    method fmt (line 112) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    method from (line 170) | fn from(s: String) -> Self {
    method from (line 176) | fn from(s: &str) -> Self {
  function fmt_extract_attr (line 129) | fn fmt_extract_attr(

FILE: ricq-core/src/msg/elem/reply.rs
  type Reply (line 9) | pub struct Reply {
    method from (line 45) | fn from(e: msg::SourceMsg) -> Self {
    method fmt (line 56) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method from (line 17) | fn from(e: Reply) -> Self {
  method push_builder (line 34) | fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {

FILE: ricq-core/src/msg/elem/rich_msg.rs
  type RichMsg (line 15) | pub struct RichMsg {
    method from (line 21) | fn from(e: msg::RichMsg) -> Self {
    method fmt (line 59) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  method push_to (line 45) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {

FILE: ricq-core/src/msg/elem/text.rs
  type Text (line 8) | pub struct Text {
    method new (line 13) | pub fn new(content: String) -> Self {
    method from (line 21) | fn from(e: msg::Text) -> Self {
    method fmt (line 54) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method push_to (line 29) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {
  method push_builder (line 38) | fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {
  function flush_builder (line 43) | pub fn flush_builder(builder: &mut MessageChainBuilder) {

FILE: ricq-core/src/msg/elem/video_file.rs
  type VideoFile (line 10) | pub struct VideoFile {
    method from (line 20) | fn from(e: msg::VideoFile) -> Self {
    method fmt (line 63) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method push_to (line 33) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {

FILE: ricq-core/src/msg/fragment.rs
  method fragment (line 8) | pub fn fragment(mut self) -> Vec<MessageChain> {

FILE: ricq-core/src/msg/mod.rs
  type MessageElem (line 12) | pub type MessageElem = msg::elem::Elem;
  type MessageChain (line 31) | pub struct MessageChain(pub Vec<MessageElem>);
    method new (line 44) | pub fn new<E: Into<Vec<MessageElem>>>(e: E) -> Self {
    method push (line 59) | pub fn push<E: Into<Vec<MessageElem>>>(&mut self, e: E) {
    method anonymous (line 63) | pub fn anonymous(&self) -> Option<Anonymous> {
    method reply (line 71) | pub fn reply(&self) -> Option<Reply> {
    method with_anonymous (line 78) | pub fn with_anonymous(&mut self, anonymous: Anonymous) {
    method with_reply (line 83) | pub fn with_reply(&mut self, reply: Reply) {
    method from_iter (line 93) | fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {
    method from (line 111) | fn from(elements: Vec<msg::Elem>) -> Self {
    method fmt (line 125) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  type Item (line 99) | type Item = RQElem;
  type IntoIter (line 100) | type IntoIter = impl Iterator<Item = RQElem> + 'static;
  method into_iter (line 102) | fn into_iter(self) -> Self::IntoIter {
  function from (line 117) | fn from(e: MessageChain) -> Self {
  type MessageChainBuilder (line 149) | pub struct MessageChainBuilder {
    method new (line 163) | pub fn new() -> Self {
    method push (line 177) | pub fn push<E: PushBuilder>(&mut self, elem: E) -> &mut Self {
    method push_str (line 193) | pub fn push_str(&mut self, str: &str) -> &mut Self {
    method build (line 207) | pub fn build(mut self) -> MessageChain {
    method flush (line 213) | fn flush(&mut self) {
  type PushElem (line 218) | pub trait PushElem {
    method push_to (line 219) | fn push_to(elem: Self, vec: &mut Vec<MessageElem>);
  type PushBuilder (line 222) | pub trait PushBuilder {
    method push_builder (line 223) | fn push_builder(elem: Self, builder: &mut MessageChainBuilder);
  function test_iter (line 231) | fn test_iter() {
  function test_display (line 240) | fn test_display() {
  function test_builder (line 264) | fn test_builder() {

FILE: ricq-core/src/protocol/device.rs
  type OSVersion (line 11) | pub struct OSVersion {
  method default (line 19) | fn default() -> Self {
  type Device (line 31) | pub struct Device {
    method random (line 60) | pub fn random() -> Self {
    method random_with_rng (line 64) | pub fn random_with_rng<RNG: RngCore>(rng: &mut RNG) -> Self {
    method ksid (line 100) | pub fn ksid(&self) -> Bytes {
    method set_qimei (line 108) | pub fn set_qimei(&mut self, qimei: Qimei) {
  function random_string (line 113) | pub fn random_string(len: usize) -> String {
  function random_uuid (line 117) | pub fn random_uuid<RNG: RngCore>(rng: &mut RNG) -> String {
  function random_imei (line 129) | pub fn random_imei<RNG: RngCore>(rng: &mut RNG) -> String {

FILE: ricq-core/src/protocol/oicq.rs
  type EncryptionMethod (line 10) | pub enum EncryptionMethod {
  type Message (line 17) | pub struct Message {
  type Codec (line 24) | pub struct Codec {
    method encode (line 41) | pub fn encode(&self, m: Message) -> Bytes {
    method decode (line 85) | pub fn decode<B>(&self, mut reader: B) -> RQResult<Message>
  method default (line 31) | fn default() -> Self {

FILE: ricq-core/src/protocol/packet.rs
  type PacketType (line 7) | pub enum PacketType {
    method value (line 14) | pub fn value(&self) -> u32 {
    method from_i32 (line 21) | pub fn from_i32(v: i32) -> RQResult<Self> {
  type EncryptType (line 32) | pub enum EncryptType {
    method value (line 40) | pub fn value(&self) -> u32 {
    method from_u8 (line 47) | pub fn from_u8(v: u8) -> RQResult<Self> {
  type Packet (line 58) | pub struct Packet {
    method check_command_name (line 70) | pub fn check_command_name(self, command_name: &str) -> RQResult<Self> {

FILE: ricq-core/src/protocol/qimei.rs
  constant RSA_PUB_KEY (line 13) | const RSA_PUB_KEY: &str = r#"-----BEGIN PUBLIC KEY-----
  constant SECRET (line 20) | const SECRET: &str = "ZdJqM15EeO2zWc08";
  function aes_decrypt (line 22) | pub fn aes_decrypt(text: &[u8], key: &[u8]) -> RQResult<Vec<u8>> {
  function aes_encrypt (line 28) | pub fn aes_encrypt(text: &[u8], key: &[u8]) -> RQResult<Vec<u8>> {
  function rsa_pub_key (line 33) | pub fn rsa_pub_key() -> RQResult<rsa::RsaPublicKey> {
  type DeviceReserved (line 39) | pub struct DeviceReserved<'a> {
  function from_device (line 59) | pub fn from_device<RNG: RngCore>(rng: &mut RNG, device: &'a Device) -> D...
  function rand_beacon_id (line 84) | pub fn rand_beacon_id<RNG: RngCore>(rng: &mut RNG) -> String {
  type QimeiRequestPayload (line 127) | pub struct QimeiRequestPayload<'a> {
  function new (line 155) | pub fn new<RNG: RngCore>(
  type QimeiRequest (line 195) | pub struct QimeiRequest {
    method new (line 217) | pub fn new<RNG: RngCore + CryptoRng>(
  type QimeiResponse (line 205) | pub struct QimeiResponse {
    method to_payload (line 249) | pub fn to_payload(self, crypt_key: &[u8]) -> RQResult<Qimei> {
  type Qimei (line 211) | pub struct Qimei {

FILE: ricq-core/src/protocol/sig.rs
  type Sig (line 8) | pub struct Sig {
    method new (line 50) | pub fn new(device: &Device) -> Self {

FILE: ricq-core/src/protocol/transport.rs
  type Transport (line 17) | pub struct Transport {
    method new (line 25) | pub fn new(device: Device, version: Version) -> Self {
    method encode_packet (line 36) | pub fn encode_packet(&self, mut pkt: Packet) -> Bytes {
    method decode_packet (line 81) | pub fn decode_packet<B>(&self, mut r: B) -> RQResult<Packet>
    method encode_body (line 109) | fn encode_body(&self, pkt: &Packet, w: &mut BytesMut) {
    method decode_sso_frame (line 166) | fn decode_sso_frame<B>(&self, pkt: &mut Packet, mut r: B) -> RQResult<()>
    method encode_oidb_packet (line 213) | pub fn encode_oidb_packet(&self, cmd: i32, service_type: i32, body: By...

FILE: ricq-core/src/protocol/version.rs
  constant WLOGIN_A2 (line 3) | pub const WLOGIN_A2: u32 = 64;
  constant WLOGIN_A5 (line 4) | pub const WLOGIN_A5: u32 = 2;
  constant WLOGIN_AQSIG (line 5) | pub const WLOGIN_AQSIG: u32 = 2097152;
  constant WLOGIN_D2 (line 6) | pub const WLOGIN_D2: u32 = 262144;
  constant WLOGIN_DA2 (line 7) | pub const WLOGIN_DA2: u32 = 33554432;
  constant WLOGIN_LHSIG (line 8) | pub const WLOGIN_LHSIG: u32 = 4194304;
  constant WLOGIN_LSKEY (line 9) | pub const WLOGIN_LSKEY: u32 = 512;
  constant WLOGIN_OPENKEY (line 10) | pub const WLOGIN_OPENKEY: u32 = 16384;
  constant WLOGIN_PAYTOKEN (line 11) | pub const WLOGIN_PAYTOKEN: u32 = 8388608;
  constant WLOGIN_PF (line 12) | pub const WLOGIN_PF: u32 = 16777216;
  constant WLOGIN_PSKEY (line 13) | pub const WLOGIN_PSKEY: u32 = 1048576;
  constant WLOGIN_PT4_TOKEN (line 14) | pub const WLOGIN_PT4_TOKEN: u32 = 134217728;
  constant WLOGIN_QRPUSH (line 15) | pub const WLOGIN_QRPUSH: u32 = 67108864;
  constant WLOGIN_RESERVED (line 16) | pub const WLOGIN_RESERVED: u32 = 16;
  constant WLOGIN_SID (line 17) | pub const WLOGIN_SID: u32 = 524288;
  constant WLOGIN_SIG64 (line 18) | pub const WLOGIN_SIG64: u32 = 8192;
  constant WLOGIN_SKEY (line 19) | pub const WLOGIN_SKEY: u32 = 4096;
  constant WLOGIN_ST (line 20) | pub const WLOGIN_ST: u32 = 128;
  constant WLOGIN_STWEB (line 21) | pub const WLOGIN_STWEB: u32 = 32;
  constant WLOGIN_TOKEN (line 22) | pub const WLOGIN_TOKEN: u32 = 32768;
  constant WLOGIN_VKEY (line 23) | pub const WLOGIN_VKEY: u32 = 131072;
  type Protocol (line 27) | pub enum Protocol {
    type Error (line 256) | type Error = ();
    method try_from (line 258) | fn try_from(s: &str) -> Result<Self, Self::Error> {
    type Error (line 272) | type Error = ();
    method try_from (line 274) | fn try_from(u: u8) -> Result<Self, Self::Error> {
  type Version (line 38) | pub struct Version {
    method from (line 68) | fn from(p: Protocol) -> Version {
  function get_version (line 56) | pub const fn get_version(p: Protocol) -> Version {
  constant ANDROID_PHONE (line 73) | pub const ANDROID_PHONE: Version = Version {
  constant APAD (line 110) | pub const APAD: Version = Version {
  constant IPAD (line 147) | pub const IPAD: Version = Version {
  constant ANDROID_WATCH (line 175) | pub const ANDROID_WATCH: Version = Version {
  constant MACOS (line 196) | pub const MACOS: Version = Version {
  constant QIDIAN (line 214) | pub const QIDIAN: Version = Version {
  constant ANDROID_PAD (line 234) | pub const ANDROID_PAD: Version = Version {

FILE: ricq-core/src/structs.rs
  type AccountInfo (line 13) | pub struct AccountInfo {
  type AddressInfo (line 20) | pub struct AddressInfo {
  type OtherClientInfo (line 27) | pub struct OtherClientInfo {
  type QiDianAccountInfo (line 34) | pub struct QiDianAccountInfo {
  type BigDataReqSessionInfo (line 44) | pub struct BigDataReqSessionInfo {
  type GroupInfo (line 50) | pub struct GroupInfo {
  type GroupMemberInfo (line 69) | pub struct GroupMemberInfo {
  type GroupMemberPermission (line 86) | pub enum GroupMemberPermission {
  type FriendInfo (line 95) | pub struct FriendInfo {
  type FriendGroupInfo (line 105) | pub struct FriendGroupInfo {
  type SummaryCardInfo (line 114) | pub struct SummaryCardInfo {
  type FriendMessage (line 129) | pub struct FriendMessage {
  type GroupMessage (line 140) | pub struct GroupMessage {
  type GroupTempMessage (line 152) | pub struct GroupTempMessage {
  type NewMember (line 163) | pub struct NewMember {
  type GroupMute (line 169) | pub struct GroupMute {
  type FriendMessageRecall (line 177) | pub struct FriendMessageRecall {
  type GroupMessageRecall (line 184) | pub struct GroupMessageRecall {
  type GroupLeave (line 193) | pub struct GroupLeave {
  type FriendPoke (line 200) | pub struct FriendPoke {
  type GroupPoke (line 206) | pub struct GroupPoke {
  type GroupNameUpdate (line 213) | pub struct GroupNameUpdate {
  type DeleteFriend (line 220) | pub struct DeleteFriend {
  type MemberPermissionChange (line 225) | pub struct MemberPermissionChange {
  type GroupDisband (line 232) | pub struct GroupDisband {
  type MessageReceipt (line 239) | pub struct MessageReceipt {
  type GroupAudio (line 246) | pub struct GroupAudio(pub pb::msg::Ptt);
  type GroupAudioMessage (line 249) | pub struct GroupAudioMessage {
  type FriendAudio (line 261) | pub struct FriendAudio(pub pb::msg::Ptt);
  type FriendAudioMessage (line 264) | pub struct FriendAudioMessage {
  type GroupFileCount (line 275) | pub struct GroupFileCount {
  type GroupFileList (line 284) | pub struct GroupFileList {
  type GroupFileItem (line 293) | pub struct GroupFileItem {
  type GroupFolderInfo (line 301) | pub struct GroupFolderInfo {
  type GroupFileInfo (line 313) | pub struct GroupFileInfo {

FILE: ricq-core/src/token.rs
  type Token (line 4) | pub struct Token {

FILE: ricq-core/src/utils/option_set.rs
  type OptionSet (line 1) | pub trait OptionSet: Sized {
    method option_set (line 3) | fn option_set(&mut self, value: Option<Self>);
    method option_set (line 8) | fn option_set(&mut self, value: Option<Self>) {

FILE: ricq-core/src/wtlogin.rs
  function process_qrcode_confirmed (line 14) | pub fn process_qrcode_confirmed(&mut self, resp: &QRCodeConfirmed) {
  function process_login_response (line 19) | pub fn process_login_response(&mut self, login_response: &LoginResponse) {
  function process_login_success (line 29) | fn process_login_success(&mut self, resp: LoginSuccess) {
  function process_need_captcha (line 64) | fn process_need_captcha(&mut self, resp: &LoginNeedCaptcha) {
  function process_device_locked (line 69) | fn process_device_locked(&mut self, resp: &LoginDeviceLocked) {
  function process_device_lock_login (line 77) | fn process_device_lock_login(&mut self, resp: LoginDeviceLockLogin) {
  function set_t402 (line 86) | fn set_t402(transport: &mut Transport, t402: Bytes) {

FILE: ricq-guild/build.rs
  function recurse_dir (line 3) | fn recurse_dir(v: &mut Vec<PathBuf>, dir: impl AsRef<Path>) {
  function main (line 15) | fn main() {

FILE: ricq-guild/src/client/builder.rs
  function build_sync_channel_first_view_packet (line 9) | pub fn build_sync_channel_first_view_packet(&self) -> Packet {
  function build_get_user_profile_packet (line 21) | pub fn build_get_user_profile_packet(&self, tiny_id: u64) -> Packet {
  function build_send_channel_message_packet (line 43) | pub fn build_send_channel_message_packet(
  function build_guild_image_store_packet (line 104) | pub fn build_guild_image_store_packet(

FILE: ricq-guild/src/client/decoder.rs
  type Decoder (line 10) | pub struct Decoder;
    method decode_guild_first_view_response (line 13) | pub fn decode_guild_first_view_response(
    method decode_first_view_msg (line 45) | pub fn decode_first_view_msg(&self, payload: Bytes) -> RQResult<FirstV...
    method decode_guild_user_profile (line 50) | pub fn decode_guild_user_profile(&self, payload: Bytes) -> RQResult<Op...
    method decode_guild_image_store_response (line 56) | pub fn decode_guild_image_store_response(

FILE: ricq-guild/src/client/mod.rs
  type GuildClient (line 27) | pub struct GuildClient {
    method new (line 33) | pub async fn new(rq_client: &Arc<ricq::Client>) -> Self {
    method engine (line 44) | pub async fn engine(&self) -> Engine<'_> {
    method fetch_guild_first_view (line 48) | pub async fn fetch_guild_first_view(&self) -> RQResult<Option<FirstVie...
    method fetch_guild_self_profile (line 120) | pub async fn fetch_guild_self_profile(
    method send_channel_message (line 145) | pub async fn send_channel_message(
    method upload_channel_image (line 162) | pub async fn upload_channel_image(
    method get_guild_image_store (line 242) | pub async fn get_guild_image_store(
  type Engine (line 264) | pub struct Engine<'a>(RwLockReadGuard<'a, ricq_core::Engine>);
  function from_rq (line 267) | fn from_rq(engine: RwLockReadGuard<'a, ricq_core::Engine>) -> Self {
  type Target (line 273) | type Target = ricq_core::Engine;
  method deref (line 275) | fn deref(&self) -> &Self::Target {

FILE: ricq-guild/src/protocol/mod.rs
  type FirstViewResponse (line 8) | pub struct FirstViewResponse {
  type FirstViewMessage (line 16) | pub struct FirstViewMessage {
  type FirstView (line 25) | pub struct FirstView {
  type GuildUserProfile (line 31) | pub struct GuildUserProfile {
  type GuildSelfProfile (line 39) | pub struct GuildSelfProfile {
  type GuildImage (line 46) | pub struct GuildImage {
    method push_to (line 60) | fn push_to(img: Self, vec: &mut Vec<MessageElem>) {
    method push_builder (line 100) | fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {
  type GuildImageStoreResp (line 106) | pub enum GuildImageStoreResp {

FILE: ricq/src/client/api/friend.rs
  function get_friend_system_messages (line 22) | pub async fn get_friend_system_messages(&self) -> RQResult<FriendSystemM...
  function solve_friend_system_message (line 36) | pub async fn solve_friend_system_message(
  function _get_friend_list (line 53) | pub async fn _get_friend_list(
  function delete_friend (line 85) | pub async fn delete_friend(&self, del_uin: i64) -> RQResult<()> {
  function get_friend_list (line 102) | pub async fn get_friend_list(&self) -> RQResult<FriendListResponse> {
  function friend_list_add_group (line 119) | pub async fn friend_list_add_group(&self, sort_id: u8, group_name: Strin...
  function friend_list_rename_group (line 130) | pub async fn friend_list_rename_group(&self, group_id: u8, group_name: S...
  function friend_list_del_group (line 141) | pub async fn friend_list_del_group(&self, group_id: u8) -> RQResult<()> {
  function friend_poke (line 152) | pub async fn friend_poke(&self, target: i64) -> RQResult<()> {
  function send_friend_message (line 159) | pub async fn send_friend_message(
  function send_friend_audio (line 168) | pub async fn send_friend_audio(
  function _send_friend_message (line 177) | async fn _send_friend_message(
  function upload_friend_image (line 193) | pub async fn upload_friend_image(&self, target: i64, data: &[u8]) -> RQR...
  function get_off_pic_store (line 230) | pub async fn get_off_pic_store(
  function send_friend_music_share (line 252) | pub async fn send_friend_music_share(
  function send_friend_link_share (line 268) | pub async fn send_friend_link_share(&self, uin: i64, link_share: LinkSha...
  function recall_friend_message (line 279) | pub async fn recall_friend_message(
  function upload_friend_audio (line 295) | pub async fn upload_friend_audio(
  function get_friend_audio_url (line 374) | pub async fn get_friend_audio_url(
  function mark_friend_message_readed (line 388) | pub async fn mark_friend_message_readed(&self, uin: i64, time: i64) -> R...
  function get_friend_rich_sig (line 399) | pub async fn get_friend_rich_sig(&self, user_ids: Vec<i64>) -> RQResult<...

FILE: ricq/src/client/api/group.rs
  function get_group_system_messages (line 28) | async fn get_group_system_messages(&self, suspicious: bool) -> RQResult<...
  function get_all_group_system_messages (line 42) | pub async fn get_all_group_system_messages(&self) -> RQResult<GroupSyste...
  function solve_group_system_message (line 53) | pub async fn solve_group_system_message(
  function _get_group_list (line 85) | pub async fn _get_group_list(&self, vec_cookie: &[u8]) -> RQResult<Group...
  function send_group_message (line 99) | pub async fn send_group_message(
  function send_group_audio (line 109) | pub async fn send_group_audio(
  function _send_group_message (line 118) | async fn _send_group_message(
  function send_group_temp_message (line 153) | pub async fn send_group_temp_message(
  function get_group_member_info (line 171) | pub async fn get_group_member_info(
  function get_group_infos (line 189) | pub async fn get_group_infos(&self, group_codes: Vec<i64>) -> RQResult<V...
  function get_group_info (line 203) | pub async fn get_group_info(&self, group_code: i64) -> RQResult<Option<G...
  function get_group_list (line 208) | pub async fn get_group_list(&self) -> RQResult<Vec<GroupInfo>> {
  function _get_group_member_list (line 226) | async fn _get_group_member_list(
  function get_group_member_list (line 245) | pub async fn get_group_member_list(
  function mark_group_message_readed (line 272) | pub async fn mark_group_message_readed(&self, group_code: i64, seq: i32)...
  function group_mute (line 283) | pub async fn group_mute(
  function group_mute_all (line 299) | pub async fn group_mute_all(&self, group_code: i64, mute: bool) -> RQRes...
  function update_group_name (line 310) | pub async fn update_group_name(&self, group_code: i64, name: String) -> ...
  function update_group_memo (line 321) | pub async fn update_group_memo(&self, group_code: i64, memo: String) -> ...
  function group_set_admin (line 334) | pub async fn group_set_admin(&self, group_code: i64, member: i64, flag: ...
  function group_poke (line 345) | pub async fn group_poke(&self, group_code: i64, target: i64) -> RQResult...
  function group_kick (line 356) | pub async fn group_kick(
  function group_invite (line 373) | pub async fn group_invite(&self, group_code: i64, uin: i64) -> RQResult<...
  function group_quit (line 383) | pub async fn group_quit(&self, group_code: i64) -> RQResult<()> {
  function group_at_all_remain (line 390) | pub async fn group_at_all_remain(&self, group_code: i64) -> RQResult<Gro...
  function group_edit_special_title (line 404) | pub async fn group_edit_special_title(
  function get_anony_info (line 420) | pub async fn get_anony_info(&self, group_code: i64) -> RQResult<Option<A...
  function send_group_music_share (line 434) | pub async fn send_group_music_share(
  function send_group_link_share (line 450) | pub async fn send_group_link_share(
  function edit_group_member_card (line 465) | pub async fn edit_group_member_card(
  function recall_group_message (line 481) | pub async fn recall_group_message(
  function get_group_image_store (line 497) | pub async fn get_group_image_store(
  function upload_group_image (line 519) | pub async fn upload_group_image(&self, group_code: i64, data: &[u8]) -> ...
  function upload_group_audio (line 561) | pub async fn upload_group_audio(
  function get_group_audio_url (line 622) | pub async fn get_group_audio_url(
  function get_group_short_video_store (line 636) | pub async fn get_group_short_video_store(
  function upload_group_short_video (line 654) | pub async fn upload_group_short_video(
  function operate_group_essence (line 729) | pub async fn operate_group_essence(
  function send_group_long_message (line 752) | pub async fn send_group_long_message(
  function send_group_forward_message (line 800) | pub async fn send_group_forward_message(
  function get_group_admin_list (line 834) | pub async fn get_group_admin_list(
  function group_sign_in (line 851) | pub async fn group_sign_in(&self, group_code: i64) -> RQResult<()> {
  function get_group_file_list (line 861) | pub async fn get_group_file_list(
  function get_group_files_count (line 880) | pub async fn get_group_files_count(&self, group_code: u64) -> RQResult<G...
  function get_group_file_download (line 911) | pub async fn get_group_file_download(

FILE: ricq/src/client/api/login.rs
  function fetch_qrcode (line 13) | pub async fn fetch_qrcode(&self) -> RQResult<QRCodeState> {
  function query_qrcode_result (line 26) | pub async fn query_qrcode_result(&self, sig: &[u8]) -> RQResult<QRCodeSt...
  function qrcode_login (line 43) | pub async fn qrcode_login(
  function sign (line 60) | pub async fn sign(&self, data: &str) -> RQResult<Vec<u8>> {
  function password_md5_login (line 89) | pub async fn password_md5_login(
  function password_login (line 107) | pub async fn password_login(&self, uin: i64, password: &str) -> RQResult...
  function request_sms (line 113) | pub async fn request_sms(&self) -> RQResult<LoginResponse> {
  function submit_sms_code (line 122) | pub async fn submit_sms_code(&self, code: &str) -> RQResult<LoginRespons...
  function submit_ticket (line 136) | pub async fn submit_ticket(&self, ticket: &str) -> RQResult<LoginRespons...
  function device_lock_login (line 150) | pub async fn device_lock_login(&self) -> RQResult<LoginResponse> {
  function token_login (line 159) | pub async fn token_login(&self, token: Token) -> RQResult<LoginResponse> {
  function request_change_sig (line 165) | pub async fn request_change_sig(&self, main_sig_map: Option<u32>) -> RQR...
  function register_client (line 182) | pub async fn register_client(&self) -> RQResult<SvcRespRegister> {
  function heartbeat (line 197) | pub async fn heartbeat(&self) -> RQResult<()> {
  function send_msg_offline_rsp (line 204) | pub(crate) async fn send_msg_offline_rsp(&self, uin: i64, seq_no: i64) -...
  function send_sid_ticket_expired_response (line 214) | pub(crate) async fn send_sid_ticket_expired_response(&self, seq: i32) ->...

FILE: ricq/src/client/api/mod.rs
  function update_online_status (line 28) | pub async fn update_online_status<T>(&self, status: T) -> RQResult<()>
  function update_signature (line 48) | pub async fn update_signature(&self, signature: String) -> RQResult<()> {
  function update_profile_detail (line 59) | pub async fn update_profile_detail(&self, profile: ProfileDetailUpdate) ...
  function refresh_status (line 70) | pub async fn refresh_status(&self) -> RQResult<()> {
  function get_allowed_clients (line 81) | pub async fn get_allowed_clients(&self) -> RQResult<Vec<SvcDevLoginInfo>> {
  function translate (line 88) | pub async fn translate(
  function send_like (line 114) | pub async fn send_like(
  function image_ocr (line 132) | pub async fn image_ocr(
  function delete_message (line 156) | pub async fn delete_message(&self, items: Vec<pb::MessageItem>) -> RQRes...
  function delete_online_push (line 167) | pub async fn delete_online_push(
  function sync_message (line 185) | async fn sync_message(&self, sync_flag: i32) -> RQResult<MessageSyncResp...
  function sync_all_message (line 200) | pub(crate) async fn sync_all_message(&self) -> RQResult<Vec<pb::msg::Mes...
  function get_summary_info (line 271) | pub async fn get_summary_info(&self, uin: i64) -> RQResult<SummaryCardIn...
  function multi_msg_apply_up (line 285) | async fn multi_msg_apply_up(
  function upload_msgs (line 305) | pub async fn upload_msgs(
  function multi_msg_apply_down (line 356) | async fn multi_msg_apply_down(
  function download_msgs (line 372) | pub async fn download_msgs(&self, res_id: String) -> RQResult<Vec<Forwar...
  function send_message (line 400) | pub async fn send_message(

FILE: ricq/src/client/event.rs
  type EventWithClient (line 17) | pub struct EventWithClient<T> {
  type GroupMessageEvent (line 23) | pub type GroupMessageEvent = EventWithClient<GroupMessage>;
    method recall (line 26) | pub async fn recall(&self) -> RQResult<()> {
  type FriendMessageEvent (line 38) | pub type FriendMessageEvent = EventWithClient<FriendMessage>;
  type GroupTempMessageEvent (line 39) | pub type GroupTempMessageEvent = EventWithClient<GroupTempMessage>;
  type JoinGroupRequestEvent (line 40) | pub type JoinGroupRequestEvent = EventWithClient<JoinGroupRequest>;
    method accept (line 43) | pub async fn accept(&self) -> RQResult<()> {
    method reject (line 58) | pub async fn reject(&self, reason: String, block: bool) -> RQResult<()> {
  type NewFriendRequestEvent (line 74) | pub type NewFriendRequestEvent = EventWithClient<NewFriendRequest>;
    method accept (line 77) | pub async fn accept(&self) -> RQResult<()> {
    method reject (line 83) | pub async fn reject(&self) -> RQResult<()> {
  type NewMemberEvent (line 90) | pub type NewMemberEvent = EventWithClient<NewMember>;
  type GroupMuteEvent (line 91) | pub type GroupMuteEvent = EventWithClient<GroupMute>;
  type FriendMessageRecallEvent (line 92) | pub type FriendMessageRecallEvent = EventWithClient<FriendMessageRecall>;
  type GroupMessageRecallEvent (line 93) | pub type GroupMessageRecallEvent = EventWithClient<GroupMessageRecall>;
  type NewFriendEvent (line 94) | pub type NewFriendEvent = EventWithClient<FriendInfo>;
  type GroupLeaveEvent (line 95) | pub type GroupLeaveEvent = EventWithClient<GroupLeave>;
  type GroupDisbandEvent (line 96) | pub type GroupDisbandEvent = EventWithClient<GroupDisband>;
  type FriendPokeEvent (line 97) | pub type FriendPokeEvent = EventWithClient<FriendPoke>;
  type GroupPokeEvent (line 98) | pub type GroupPokeEvent = EventWithClient<GroupPoke>;
  type GroupNameUpdateEvent (line 99) | pub type GroupNameUpdateEvent = EventWithClient<GroupNameUpdate>;
  type DeleteFriendEvent (line 100) | pub type DeleteFriendEvent = EventWithClient<DeleteFriend>;
  type MemberPermissionChangeEvent (line 101) | pub type MemberPermissionChangeEvent = EventWithClient<MemberPermissionC...
  type SelfInvitedEvent (line 102) | pub type SelfInvitedEvent = EventWithClient<SelfInvited>;
  type GroupAudioMessageEvent (line 103) | pub type GroupAudioMessageEvent = EventWithClient<GroupAudioMessage>;
    method url (line 106) | pub async fn url(&self) -> RQResult<String> {
  type FriendAudioMessageEvent (line 113) | pub type FriendAudioMessageEvent = EventWithClient<FriendAudioMessage>;
    method url (line 116) | pub async fn url(&self) -> RQResult<String> {
  type KickedOfflineEvent (line 123) | pub type KickedOfflineEvent = EventWithClient<jce::RequestPushForceOffli...
  type MSFOfflineEvent (line 124) | pub type MSFOfflineEvent = EventWithClient<jce::RequestMSFForceOffline>;
  type DisconnectReason (line 128) | pub enum DisconnectReason {
    method status (line 137) | pub fn status(&self) -> NetworkStatus {
  type ClientDisconnect (line 145) | pub type ClientDisconnect = EventWithClient<DisconnectReason>;
    method reason (line 148) | pub fn reason(&self) -> DisconnectReason {

FILE: ricq/src/client/handler/mod.rs
  type QEvent (line 16) | pub enum QEvent {
  type Handler (line 99) | pub trait Handler: Sync {
    method handle (line 100) | async fn handle(&self, event: QEvent);
    method handle (line 108) | fn handle<'a: 'b, 'b>(&'a self, e: QEvent) -> Pin<Box<dyn Future<Outpu...
    method handle (line 118) | async fn handle(&self, e: QEvent) {
    method handle (line 155) | async fn handle(&self, msg: QEvent) {
    method handle (line 162) | async fn handle(&self, msg: QEvent) {
    method handle (line 169) | async fn handle(&self, msg: QEvent) {
    method handle (line 176) | async fn handle(&self, msg: QEvent) {
    method handle (line 214) | async fn handle(&self, event: QEvent) {
  type DefaultHandler (line 114) | pub struct DefaultHandler;
  type PartlyHandler (line 182) | pub trait PartlyHandler: Sync {
    method handle_login (line 183) | async fn handle_login(&self, _: i64) {}
    method handle_group_message (line 184) | async fn handle_group_message(&self, _event: GroupMessageEvent) {}
    method handle_group_audio (line 185) | async fn handle_group_audio(&self, _event: GroupAudioMessageEvent) {}
    method handle_friend_message (line 186) | async fn handle_friend_message(&self, _event: FriendMessageEvent) {}
    method handle_friend_audio (line 187) | async fn handle_friend_audio(&self, _event: FriendAudioMessageEvent) {}
    method handle_group_temp_message (line 188) | async fn handle_group_temp_message(&self, _event: GroupTempMessageEven...
    method handle_group_request (line 189) | async fn handle_group_request(&self, _event: JoinGroupRequestEvent) {}
    method handle_self_invited (line 190) | async fn handle_self_invited(&self, _event: SelfInvitedEvent) {}
    method handle_friend_request (line 191) | async fn handle_friend_request(&self, _event: NewFriendRequestEvent) {}
    method handle_new_member (line 192) | async fn handle_new_member(&self, _event: NewMemberEvent) {}
    method handle_group_mute (line 193) | async fn handle_group_mute(&self, _event: GroupMuteEvent) {}
    method handle_friend_message_recall (line 194) | async fn handle_friend_message_recall(&self, _event: FriendMessageReca...
    method handle_group_message_recall (line 195) | async fn handle_group_message_recall(&self, _event: GroupMessageRecall...
    method handle_new_friend (line 196) | async fn handle_new_friend(&self, _event: NewFriendEvent) {}
    method handle_group_leave (line 197) | async fn handle_group_leave(&self, _event: GroupLeaveEvent) {}
    method handle_group_disband (line 198) | async fn handle_group_disband(&self, _event: GroupDisbandEvent) {}
    method handle_friend_poke (line 199) | async fn handle_friend_poke(&self, _event: FriendPokeEvent) {}
    method handle_group_poke (line 200) | async fn handle_group_poke(&self, _event: GroupPokeEvent) {}
    method handle_group_name_update (line 201) | async fn handle_group_name_update(&self, _event: GroupNameUpdateEvent) {}
    method handle_delete_friend (line 202) | async fn handle_delete_friend(&self, _event: DeleteFriendEvent) {}
    method handle_member_permission_change (line 203) | async fn handle_member_permission_change(&self, _event: MemberPermissi...
    method handle_kicked_offline (line 204) | async fn handle_kicked_offline(&self, _event: KickedOfflineEvent) {}
    method handle_msf_offline (line 205) | async fn handle_msf_offline(&self, _event: MSFOfflineEvent) {}
    method handle_client_disconnect (line 206) | async fn handle_client_disconnect(&self, _event: ClientDisconnect) {}

FILE: ricq/src/client/highway/codec.rs
  type HighwayCodec (line 8) | pub struct HighwayCodec;
    type Error (line 11) | type Error = RQError;
    method encode (line 13) | fn encode(&mut self, item: HighwayFrame, dst: &mut BytesMut) -> Result...
  type Item (line 25) | type Item = HighwayFrame;
  type Error (line 26) | type Error = RQError;
  method decode (line 28) | fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, S...

FILE: ricq/src/client/highway/mod.rs
  type HighwayFrame (line 6) | pub struct HighwayFrame {

FILE: ricq/src/client/highway/net.rs
  method highway_upload_bdh (line 20) | pub async fn highway_upload_bdh(
  function read_response (line 105) | async fn read_response(stream: &mut Framed<TcpStream, HighwayCodec>) -> ...

FILE: ricq/src/client/mod.rs
  constant SIGN_COMMANDS (line 37) | const SIGN_COMMANDS: &str = r#"ConnAuthSvr.fast_qq_login
  type Client (line 125) | pub struct Client {
  function new (line 180) | pub fn new<H>(
  function new_with_config (line 223) | pub fn new_with_config<H>(
  function uin (line 235) | pub async fn uin(&self) -> i64 {
  function sign_packet (line 239) | pub async fn sign_packet(&self, pkt: &mut Packet) -> RQResult<QSignRespo...
  function process_sign_callback (line 297) | pub async fn process_sign_callback(&self, callbacks: Vec<RequestCallback...
  function send (line 334) | pub async fn send(&self, pkt: Packet) -> RQResult<usize> {
  function send_and_wait (line 344) | pub async fn send_and_wait(&self, mut pkt: Packet) -> RQResult<Packet> {
  function do_heartbeat (line 382) | pub async fn do_heartbeat(&self) {
  function gen_token (line 401) | pub async fn gen_token(&self) -> Token {
  function load_token (line 406) | pub async fn load_token(&self, token: Token) {
  function device (line 410) | pub async fn device(&self) -> Device {
  function version (line 414) | pub async fn version(&self) -> Version {
  function get_highway_session_key (line 418) | pub async fn get_highway_session_key(&self) -> Vec<u8> {
  function listen_command (line 423) | pub async fn listen_command<S: ToString>(&self, command: S) -> broadcast...
  method drop (line 433) | fn drop(&mut self) {
  type NetworkStatus (line 440) | pub enum NetworkStatus {

FILE: ricq/src/client/net.rs
  type OutPktSender (line 21) | pub type OutPktSender = broadcast::Sender<Bytes>;
  type Connector (line 24) | pub trait Connector<T: AsyncRead + AsyncWrite> {
    method connect (line 25) | async fn connect(&self, client: &Client) -> io::Result<T>;
  type DefaultConnector (line 28) | pub struct DefaultConnector;
    method connect (line 32) | async fn connect(&self, client: &Client) -> io::Result<TcpStream> {
  function get_address_list (line 39) | pub async fn get_address_list(&self) -> Vec<SocketAddr> {
  function get_status (line 57) | pub fn get_status(&self) -> u8 {
  function start (line 64) | pub async fn start(self: &Arc<Self>, stream: impl AsyncRead + AsyncWrite) {
  function stop (line 108) | pub fn stop(&self, status: NetworkStatus) {
  function disconnect (line 114) | fn disconnect(&self) {
  function net_loop (line 120) | async fn net_loop(self: &Arc<Client>, stream: impl AsyncRead + AsyncWrit...

FILE: ricq/src/client/processor/c2c/friend_msg.rs
  method process_friend_message (line 13) | pub(crate) async fn process_friend_message(
  function parse_friend_message (line 53) | pub fn parse_friend_message(msg: pb::msg::Message) -> RQResult<FriendMes...
  function parse_friend_audio_message (line 72) | pub fn parse_friend_audio_message(

FILE: ricq/src/client/processor/c2c/friend_system_msg.rs
  method process_friend_system_messages (line 8) | pub(crate) async fn process_friend_system_messages(

FILE: ricq/src/client/processor/c2c/group_system_msg.rs
  method process_group_system_messages (line 10) | pub(crate) async fn process_group_system_messages(self: &Arc<Self>, msgs...
  method self_invited_exists (line 43) | async fn self_invited_exists(&self, msg_seq: i64, msg_time: i64) -> bool {
  method join_group_request_exists (line 55) | async fn join_group_request_exists(&self, msg_seq: i64, msg_time: i64) -...

FILE: ricq/src/client/processor/c2c/new_member.rs
  method process_join_group (line 12) | pub(crate) async fn process_join_group(

FILE: ricq/src/client/processor/c2c/temp_session.rs
  method process_temp_message (line 12) | pub(crate) async fn process_temp_message(
  function parse_temp_message (line 27) | pub fn parse_temp_message(msg: pb::msg::Message) -> RQResult<GroupTempMe...

FILE: ricq/src/client/processor/config_push_svc.rs
  method process_config_push_req (line 14) | pub(crate) async fn process_config_push_req(

FILE: ricq/src/client/processor/message_svc.rs
  method process_push_notify (line 13) | pub(crate) async fn process_push_notify(self: &Arc<Self>, notify: jce::R...
  method process_push_force_offline (line 53) | pub(crate) async fn process_push_force_offline(
  method process_message_sync (line 66) | pub(crate) async fn process_message_sync(self: &Arc<Self>, msgs: Vec<pb:...
  method msg_exists (line 96) | async fn msg_exists(&self, head: &pb::msg::MessageHead) -> bool {

FILE: ricq/src/client/processor/mod.rs
  function process_income_packet (line 25) | pub async fn process_income_packet(self: &Arc<Self>, pkt: Packet) {

FILE: ricq/src/client/processor/online_push.rs
  method process_group_message_part (line 27) | pub(crate) async fn process_group_message_part(
  method parse_group_message (line 99) | pub(crate) async fn parse_group_message(
  method process_push_req (line 143) | pub(crate) async fn process_push_req(self: &Arc<Self>, msg_infos: Vec<jc...
  method push_req_exists (line 357) | async fn push_req_exists(&self, info: &jce::PushMessageInfo) -> bool {
  method process_push_trans (line 375) | pub(crate) async fn process_push_trans(self: &Arc<Self>, push_trans: Onl...
  method push_trans_exists (line 409) | async fn push_trans_exists(&self, info: &OnlinePushTrans) -> bool {
  method process_c2c_sync (line 427) | pub(crate) async fn process_c2c_sync(
  method process_sid_ticket_expired (line 446) | pub(crate) async fn process_sid_ticket_expired(self: &Arc<Self>, seq: i3...

FILE: ricq/src/client/processor/reg_prxy_svc.rs
  method process_push_param (line 7) | pub(crate) async fn process_push_param(

FILE: ricq/src/client/processor/stat_svc.rs
  method process_msf_force_offline (line 11) | pub(crate) async fn process_msf_force_offline(

FILE: ricq/src/client/processor/wtlogin.rs
  method process_login_response (line 6) | pub(crate) async fn process_login_response(&self, login_response: &Login...
  method process_trans_emp_response (line 22) | pub(crate) async fn process_trans_emp_response(&self, qrcode_state: &QRC...

FILE: ricq/src/client/qimei.rs
  function get_qimei (line 7) | pub async fn get_qimei<RNG: RngCore + CryptoRng>(
  function test_qimei (line 32) | async fn test_qimei() {

FILE: ricq/src/client/tcp.rs
  function tcp_connect_timeout (line 7) | pub async fn tcp_connect_timeout(
  function race_addrs (line 19) | async fn race_addrs(
  function sort_addrs (line 33) | pub async fn sort_addrs<Addr>(addrs: Vec<Addr>, timeout: Duration) -> Ve...
  function tcp_connect_fastest (line 48) | pub async fn tcp_connect_fastest(
  function test_sort (line 71) | async fn test_sort() {

FILE: ricq/src/config.rs
  type Config (line 10) | pub struct Config {
    method new (line 25) | pub fn new(device: Device, version: Version) -> Self {
  method default (line 16) | fn default() -> Self {

FILE: ricq/src/ext/common.rs
  function after_login (line 7) | pub async fn after_login(client: &Arc<Client>) {
  function start_heartbeat (line 18) | pub async fn start_heartbeat(client: Arc<Client>) {

FILE: ricq/src/ext/image.rs
  function upload_group_image_ext (line 13) | pub async fn upload_group_image_ext<F>(
  function upload_friend_image_ext (line 57) | pub async fn upload_friend_image_ext<F>(

FILE: ricq/src/ext/login.rs
  function auto_query_qrcode (line 10) | pub async fn auto_query_qrcode(client: &Arc<Client>, sig: &[u8]) -> RQRe...

FILE: ricq/src/ext/reconnect.rs
  function auto_reconnect (line 15) | pub async fn auto_reconnect<T: AsyncRead + AsyncWrite + 'static + Send>(
  type Password (line 66) | pub struct Password {
  type Credential (line 71) | pub enum Credential {
  type FastLogin (line 78) | pub trait FastLogin {
    method fast_login (line 79) | async fn fast_login(&self, client: &Arc<Client>) -> RQResult<()>;
    method fast_login (line 84) | async fn fast_login(&self, client: &Arc<Client>) -> RQResult<()> {
    method fast_login (line 94) | async fn fast_login(&self, client: &Arc<Client>) -> RQResult<()> {
  function fast_login (line 111) | pub async fn fast_login(client: &Arc<Client>, credential: &Credential) -...

FILE: ricq/src/qsign.rs
  type QSignClient (line 7) | pub struct QSignClient {
    method new (line 46) | pub fn new(mut url: String, key: String, timeout: Duration) -> reqwest...
    method register (line 59) | pub async fn register(
    method custom_energy (line 83) | pub async fn custom_energy(
    method calc_salt (line 108) | pub fn calc_salt(uin: u64, guid: &[u8], sdk_version: &str, sub_cmd: u3...
    method energy (line 125) | pub async fn energy(
    method sign (line 152) | pub async fn sign(
    method submit (line 182) | pub async fn submit(
  type QSignResponse (line 14) | pub struct QSignResponse<T> {
  type SignData (line 22) | pub struct SignData {
  type RequestCallback (line 37) | pub struct RequestCallback {
  function get_device (line 212) | async fn get_device() -> Device {
  function test_custom_energy (line 222) | async fn test_custom_energy() {
  function test_energy (line 248) | async fn test_energy() {

FILE: ricq/src/structs/image_info.rs
  type ImageInfo (line 11) | pub struct ImageInfo {
    method try_new (line 21) | pub fn try_new(data: &[u8]) -> RQResult<Self> {
    method into_friend_image (line 58) | pub fn into_friend_image(self, res_id: String, download_path: String) ...
    method into_group_image (line 72) | pub fn into_group_image(self, file_id: u64, addr: RQAddr, signature: V...
Condensed preview — 263 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (978K chars).
[
  {
    "path": ".gitignore",
    "chars": 137,
    "preview": ".idea\n/target\n/examples/target\n/examples/*/target\n.DS_Store\nCargo.lock\n.vscode\nqrcode.png\ndevice.json\ntest.png\n*.amr\n*.s"
  },
  {
    "path": "Cargo.toml",
    "chars": 834,
    "preview": "[workspace]\nresolver = \"2\"\nmembers = [\n    \"ricq\",\n    \"ricq-core\",\n    \"ricq-guild\"\n]\n\n[workspace.dependencies]\nasync-t"
  },
  {
    "path": "LICENSE",
    "chars": 16725,
    "preview": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\""
  },
  {
    "path": "examples/.gitignore",
    "chars": 7,
    "preview": "static/"
  },
  {
    "path": "examples/Cargo.toml",
    "chars": 170,
    "preview": "[workspace]\nmembers = [\"*\"]\nexclude = [\"target\", \"static\"]\n\n[profile.release]\nopt-level = 'z'\ndebug = false\nlto = true\ni"
  },
  {
    "path": "examples/password_login/Cargo.toml",
    "chars": 425,
    "preview": "[package]\nname = \"password_login\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nricq = { path = \"../../ricq\" }\ntoki"
  },
  {
    "path": "examples/password_login/src/main.rs",
    "chars": 6362,
    "preview": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse futures_util::StreamExt;\nuse rand::prelude::StdRng;\nuse rand::Seedable"
  },
  {
    "path": "examples/qrcode_login/Cargo.toml",
    "chars": 267,
    "preview": "[package]\nname = \"qrcode_login\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nricq = { path = \"../../ricq\" }\ntokio "
  },
  {
    "path": "examples/qrcode_login/src/main.rs",
    "chars": 5292,
    "preview": "use std::path::Path;\nuse std::sync::Arc;\n\nuse bytes::Bytes;\nuse tokio::time::{sleep, Duration};\nuse tracing::Level;\nuse "
  },
  {
    "path": "examples/ricq-axum-api/Cargo.toml",
    "chars": 896,
    "preview": "[package]\nname = \"ricq-axum-api\"\nversion = \"0.1.0\"\nedition = \"2021\"\ndescription = \"ricq axum api\"\nlicense = \"AGPL-3.0\"\nh"
  },
  {
    "path": "examples/ricq-axum-api/README.md",
    "chars": 3204,
    "preview": "# ricq-axum-api\n\n1. 下载 UI 文件,放在工作目录 static 文件夹\n\n```bash\n wget https://github.com/lz1998/ricq-react-ui/releases/latest/do"
  },
  {
    "path": "examples/ricq-axum-api/src/bin/main.rs",
    "chars": 7112,
    "preview": "#![feature(async_closure)]\n\nuse std::net::SocketAddr;\nuse std::str::FromStr;\nuse std::sync::Arc;\nuse std::time::Duration"
  },
  {
    "path": "examples/ricq-axum-api/src/handler/bot.rs",
    "chars": 1012,
    "preview": "use std::sync::Arc;\n\nuse axum::http::StatusCode;\nuse axum::{Extension, Json};\nuse serde::{Deserialize, Serialize};\n\nuse "
  },
  {
    "path": "examples/ricq-axum-api/src/handler/mod.rs",
    "chars": 47,
    "preview": "pub mod bot;\npub mod password;\npub mod qrcode;\n"
  },
  {
    "path": "examples/ricq-axum-api/src/handler/password.rs",
    "chars": 10709,
    "preview": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse axum::http::StatusCode;\nuse axum::{Extension, Json};\nuse rand::{prelud"
  },
  {
    "path": "examples/ricq-axum-api/src/handler/qrcode.rs",
    "chars": 7672,
    "preview": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse axum::http::StatusCode;\nuse axum::{Extension, Json};\nuse bytes::Bytes;"
  },
  {
    "path": "examples/ricq-axum-api/src/lib.rs",
    "chars": 1435,
    "preview": "use bytes::Bytes;\nuse dashmap::DashMap;\nuse ricq::ext::reconnect::Credential;\nuse ricq::handler::QEvent;\nuse ricq::{Clie"
  },
  {
    "path": "examples/ricq-axum-api/src/processor.rs",
    "chars": 544,
    "preview": "use std::sync::Arc;\n\nuse tokio::sync::broadcast;\nuse tokio::task::JoinHandle;\n\nuse ricq::{ext::reconnect::Credential, ha"
  },
  {
    "path": "examples/ricq-axum-api/src/u8_protocol.rs",
    "chars": 719,
    "preview": "use ricq::version::Protocol;\n\npub trait U8Protocol {\n    fn to_u8(&self) -> u8;\n    fn from_u8(n: u8) -> Self;\n}\n\nimpl U"
  },
  {
    "path": "examples/token_login/Cargo.toml",
    "chars": 347,
    "preview": "[package]\nname = \"token_login\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nricq = { path = \"../../ricq\" }\ntokio ="
  },
  {
    "path": "examples/token_login/src/main.rs",
    "chars": 2693,
    "preview": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse rand::prelude::StdRng;\nuse rand::SeedableRng;\nuse tracing::Level;\nuse "
  },
  {
    "path": "ricq/Cargo.toml",
    "chars": 1127,
    "preview": "[package]\nname = \"ricq\"\nversion = \"0.1.20\"\nedition = \"2021\"\ndescription = \"Android IM protocol\"\nlicense = \"MPL-2.0\"\nhome"
  },
  {
    "path": "ricq/README.md",
    "chars": 3170,
    "preview": "# RICQ\n\n![](https://socialify.git.ci/lz1998/ricq/image?forks=1&issues=1&language=1&owner=1&pattern=Circuit%20Board&pulls"
  },
  {
    "path": "ricq/src/client/api/friend.rs",
    "chars": 12316,
    "preview": "use std::time::Duration;\n\nuse bytes::BufMut;\n\nuse ricq_core::command::long_conn::OffPicUpResp;\nuse ricq_core::command::o"
  },
  {
    "path": "ricq/src/client/api/group.rs",
    "chars": 29061,
    "preview": "use std::collections::HashMap;\nuse std::time::{Duration, UNIX_EPOCH};\n\nuse bytes::Bytes;\nuse cached::Cached;\nuse prost::"
  },
  {
    "path": "ricq/src/client/api/login.rs",
    "chars": 7422,
    "preview": "use std::sync::atomic::Ordering;\n\nuse crate::jce::SvcRespRegister;\nuse crate::qsign::QSignClient;\nuse crate::{RQError, R"
  },
  {
    "path": "ricq/src/client/api/mod.rs",
    "chars": 13614,
    "preview": "use std::net::SocketAddr;\nuse std::sync::atomic::Ordering;\nuse std::time::UNIX_EPOCH;\n\nuse bytes::Bytes;\nuse cached::Cac"
  },
  {
    "path": "ricq/src/client/event.rs",
    "chars": 4699,
    "preview": "use std::sync::Arc;\n\nuse ricq_core::command::profile_service::{JoinGroupRequest, NewFriendRequest, SelfInvited};\nuse ric"
  },
  {
    "path": "ricq/src/client/handler/mod.rs",
    "chars": 8194,
    "preview": "use std::future::Future;\nuse std::pin::Pin;\n\nuse async_trait::async_trait;\nuse tokio::sync::{\n    broadcast::Sender as B"
  },
  {
    "path": "ricq/src/client/highway/codec.rs",
    "chars": 1217,
    "preview": "use bytes::{Buf, BufMut, BytesMut};\nuse tokio_util::codec::{Decoder, Encoder};\n\nuse ricq_core::RQError;\n\nuse crate::clie"
  },
  {
    "path": "ricq/src/client/highway/mod.rs",
    "chars": 110,
    "preview": "use bytes::Bytes;\n\nmod codec;\nmod net;\n\npub struct HighwayFrame {\n    pub head: Bytes,\n    pub body: Bytes,\n}\n"
  },
  {
    "path": "ricq/src/client/highway/net.rs",
    "chars": 3597,
    "preview": "use std::net::SocketAddr;\nuse std::time::Duration;\n\nuse bytes::Bytes;\nuse futures_util::{SinkExt, StreamExt};\nuse tokio:"
  },
  {
    "path": "ricq/src/client/mod.rs",
    "chars": 14962,
    "preview": "use bytes::Bytes;\nuse std::collections::HashMap;\nuse std::sync::atomic::{AtomicBool, AtomicI64, AtomicU8, Ordering};\nuse"
  },
  {
    "path": "ricq/src/client/net.rs",
    "chars": 5318,
    "preview": "use std::net::SocketAddr;\nuse std::sync::atomic::Ordering;\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse crate::clie"
  },
  {
    "path": "ricq/src/client/processor/c2c/friend_msg.rs",
    "chars": 2972,
    "preview": "use cached::Cached;\nuse std::sync::Arc;\n\nuse ricq_core::msg::MessageChain;\nuse ricq_core::structs::{FriendAudio, FriendA"
  },
  {
    "path": "ricq/src/client/processor/c2c/friend_system_msg.rs",
    "chars": 597,
    "preview": "use crate::client::event::NewFriendRequestEvent;\nuse crate::handler::QEvent;\nuse crate::Client;\nuse ricq_core::command::"
  },
  {
    "path": "ricq/src/client/processor/c2c/group_system_msg.rs",
    "chars": 1992,
    "preview": "use std::sync::Arc;\n\nuse ricq_core::command::profile_service::GroupSystemMessages;\n\nuse crate::client::event::{JoinGroup"
  },
  {
    "path": "ricq/src/client/processor/c2c/mod.rs",
    "chars": 115,
    "preview": "pub mod friend_msg;\npub mod friend_system_msg;\npub mod group_system_msg;\npub mod new_member;\npub mod temp_session;\n"
  },
  {
    "path": "ricq/src/client/processor/c2c/new_member.rs",
    "chars": 827,
    "preview": "use std::sync::Arc;\n\nuse ricq_core::common::group_uin2code;\nuse ricq_core::structs::NewMember;\nuse ricq_core::{pb, RQErr"
  },
  {
    "path": "ricq/src/client/processor/c2c/temp_session.rs",
    "chars": 1475,
    "preview": "use std::sync::Arc;\n\nuse ricq_core::msg::MessageChain;\nuse ricq_core::structs::GroupTempMessage;\nuse ricq_core::{pb, RQE"
  },
  {
    "path": "ricq/src/client/processor/config_push_svc.rs",
    "chars": 2979,
    "preview": "use std::time::Duration;\n\nuse bytes::Bytes;\n\nuse ricq_core::command::config_push_svc::ConfigPushBody;\nuse ricq_core::com"
  },
  {
    "path": "ricq/src/client/processor/message_svc.rs",
    "chars": 4144,
    "preview": "use std::sync::Arc;\nuse std::time::UNIX_EPOCH;\n\nuse cached::Cached;\n\nuse ricq_core::{jce, pb};\n\nuse crate::client::event"
  },
  {
    "path": "ricq/src/client/processor/mod.rs",
    "chars": 8228,
    "preview": "use std::sync::Arc;\n\nuse bytes::Bytes;\n\nuse ricq_core::protocol::packet::Packet;\n\npub mod c2c;\npub mod config_push_svc;\n"
  },
  {
    "path": "ricq/src/client/processor/online_push.rs",
    "chars": 19825,
    "preview": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse bytes::{Buf, Bytes};\nuse cached::Cached;\n\nuse prost::Message;\nuse ricq"
  },
  {
    "path": "ricq/src/client/processor/reg_prxy_svc.rs",
    "chars": 409,
    "preview": "use crate::client::{Client, OtherClientInfo};\nuse crate::RQError;\n\n// use crate::client::income::decoder::online_push::G"
  },
  {
    "path": "ricq/src/client/processor/stat_svc.rs",
    "chars": 658,
    "preview": "use std::sync::Arc;\n\nuse ricq_core::jce;\n\nuse crate::client::event::MSFOfflineEvent;\nuse crate::client::{Client, Network"
  },
  {
    "path": "ricq/src/client/processor/wtlogin.rs",
    "chars": 988,
    "preview": "use crate::handler::QEvent;\nuse crate::Client;\nuse ricq_core::command::wtlogin::*;\n\nimpl Client {\n    pub(crate) async f"
  },
  {
    "path": "ricq/src/client/qimei.rs",
    "chars": 1283,
    "preview": "use rand::{CryptoRng, RngCore};\nuse ricq_core::protocol::device::Device;\nuse ricq_core::protocol::qimei::{Qimei, QimeiRe"
  },
  {
    "path": "ricq/src/client/tcp.rs",
    "chars": 2234,
    "preview": "use std::net::SocketAddr;\nuse std::time::Duration;\n\nuse tokio::net::TcpStream;\nuse tokio::task::JoinSet;\n\npub async fn t"
  },
  {
    "path": "ricq/src/config.rs",
    "chars": 512,
    "preview": "use std::fmt::Debug;\n\nuse ricq_core::protocol::{\n    device::Device,\n    version::Version,\n    version::{get_version, Pr"
  },
  {
    "path": "ricq/src/ext/common.rs",
    "chars": 658,
    "preview": "use std::sync::atomic::Ordering;\nuse std::sync::Arc;\n\nuse crate::Client;\n\n/// 登录后必须执行的操作\npub async fn after_login(client"
  },
  {
    "path": "ricq/src/ext/image.rs",
    "chars": 2950,
    "preview": "use std::future::Future;\nuse std::pin::Pin;\n\nuse ricq_core::command::img_store::GroupImageStoreResp;\nuse ricq_core::comm"
  },
  {
    "path": "ricq/src/ext/login.rs",
    "chars": 1674,
    "preview": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse ricq_core::command::wtlogin::{LoginResponse, QRCodeConfirmed, QRCodeSt"
  },
  {
    "path": "ricq/src/ext/mod.rs",
    "chars": 65,
    "preview": "pub mod common;\npub mod image;\npub mod login;\npub mod reconnect;\n"
  },
  {
    "path": "ricq/src/ext/reconnect.rs",
    "chars": 3640,
    "preview": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse async_trait::async_trait;\nuse tokio::io::{AsyncRead, AsyncWrite};\n\nuse"
  },
  {
    "path": "ricq/src/lib.rs",
    "chars": 653,
    "preview": "#![feature(async_closure)]\n#![feature(let_chains)]\n#![feature(result_flattening)]\npub mod client;\nmod config;\npub mod ex"
  },
  {
    "path": "ricq/src/qsign.rs",
    "chars": 7206,
    "preview": "use bytes::{BufMut, BytesMut};\nuse ricq_core::binary::packet_writer::WriteLV;\nuse ricq_core::hex::encode_hex;\nuse serde:"
  },
  {
    "path": "ricq/src/structs/image_info.rs",
    "chars": 2882,
    "preview": "use serde::{Deserialize, Serialize};\n\nuse ricq_core::common::RQAddr;\n\nuse ricq_core::hex::encode_hex;\nuse ricq_core::msg"
  },
  {
    "path": "ricq/src/structs/mod.rs",
    "chars": 71,
    "preview": "pub use image_info::*;\npub use ricq_core::structs::*;\n\nmod image_info;\n"
  },
  {
    "path": "ricq-core/Cargo.toml",
    "chars": 1149,
    "preview": "[package]\nname = \"ricq-core\"\nversion = \"0.1.20\"\nedition = \"2021\"\ndescription = \"Packet encoders and decoders for ricq\"\nl"
  },
  {
    "path": "ricq-core/build.rs",
    "chars": 613,
    "preview": "use std::path::{Path, PathBuf};\nfn recurse_dir(v: &mut Vec<PathBuf>, dir: impl AsRef<Path>) {\n    for entry in\n        s"
  },
  {
    "path": "ricq-core/src/binary/binary_reader.rs",
    "chars": 1822,
    "preview": "use std::collections::HashMap;\n\nuse bytes::{Buf, Bytes};\n\npub trait BinaryReader {\n    fn read_string(&mut self) -> Stri"
  },
  {
    "path": "ricq-core/src/binary/binary_writer.rs",
    "chars": 2329,
    "preview": "use crate::crypto::qqtea_encrypt;\nuse crate::hex::decode_hex;\nuse bytes::{Buf, BufMut, BytesMut};\n\npub trait BinaryWrite"
  },
  {
    "path": "ricq-core/src/binary/mod.rs",
    "chars": 136,
    "preview": "mod binary_reader;\nmod binary_writer;\npub mod packet_writer;\n\npub use binary_reader::BinaryReader;\npub use binary_writer"
  },
  {
    "path": "ricq-core/src/binary/packet_writer.rs",
    "chars": 3187,
    "preview": "use bytes::*;\nuse std::marker::PhantomData;\n\npub trait PacketWriter<B: BufMut> {\n    fn write(self, buf: &mut B);\n}\n\npub"
  },
  {
    "path": "ricq-core/src/command/common.rs",
    "chars": 1522,
    "preview": "use bytes::{BufMut, Bytes, BytesMut};\nuse prost::Message;\n\nuse crate::protocol::oicq;\nuse crate::protocol::packet::*;\nus"
  },
  {
    "path": "ricq-core/src/command/config_push_svc/builder.rs",
    "chars": 1056,
    "preview": "use std::collections::HashMap;\n\nuse bytes::Bytes;\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;"
  },
  {
    "path": "ricq-core/src/command/config_push_svc/decoder.rs",
    "chars": 2071,
    "preview": "use bytes::Bytes;\n\nuse crate::command::config_push_svc::*;\nuse crate::{jce, pb, RQError, RQResult};\nuse prost::Message;\n"
  },
  {
    "path": "ricq-core/src/command/config_push_svc/mod.rs",
    "chars": 682,
    "preview": "#![allow(clippy::large_enum_variant)]\nuse bytes::Bytes;\n\nuse crate::{jce, pb};\n\npub mod builder;\npub mod decoder;\n\n#[der"
  },
  {
    "path": "ricq-core/src/command/friendlist/builder.rs",
    "chars": 8533,
    "preview": "use std::collections::HashMap;\n\nuse bytes::{BufMut, Bytes, BytesMut};\nuse jcers::JcePut;\n\nuse crate::command::common::pa"
  },
  {
    "path": "ricq-core/src/command/friendlist/decoder.rs",
    "chars": 5913,
    "preview": "use bytes::{Buf, Bytes};\nuse jcers::Jce;\n\nuse crate::command::friendlist::*;\nuse crate::structs::{FriendInfo, GroupInfo,"
  },
  {
    "path": "ricq-core/src/command/friendlist/mod.rs",
    "chars": 597,
    "preview": "use std::collections::HashMap;\n\nuse bytes::Bytes;\n\nuse crate::structs::*;\n\npub mod builder;\npub mod decoder;\n\n#[derive(D"
  },
  {
    "path": "ricq-core/src/command/group_anonymous_generate_nick/builder.rs",
    "chars": 617,
    "preview": "use crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::En"
  },
  {
    "path": "ricq-core/src/command/group_anonymous_generate_nick/decoder.rs",
    "chars": 1082,
    "preview": "use bytes::Bytes;\nuse prost::Message;\n\nuse crate::msg::elem::Anonymous;\nuse crate::{pb, RQError, RQResult};\n\nimpl super:"
  },
  {
    "path": "ricq-core/src/command/group_anonymous_generate_nick/mod.rs",
    "chars": 34,
    "preview": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/group_member_card/builder.rs",
    "chars": 597,
    "preview": "use crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::*;\n\nimpl super::super::super::Engine "
  },
  {
    "path": "ricq-core/src/command/group_member_card/decoder.rs",
    "chars": 1431,
    "preview": "use bytes::Bytes;\nuse prost::Message;\n\nuse crate::structs::{GroupMemberInfo, GroupMemberPermission};\nuse crate::{pb, RQE"
  },
  {
    "path": "ricq-core/src/command/group_member_card/mod.rs",
    "chars": 34,
    "preview": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/heartbeat/builder.rs",
    "chars": 454,
    "preview": "use crate::protocol::packet::*;\n\nimpl super::super::super::Engine {\n    // Heartbeat.Alive\n    pub fn build_heartbeat_pa"
  },
  {
    "path": "ricq-core/src/command/heartbeat/mod.rs",
    "chars": 17,
    "preview": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/img_store/builder.rs",
    "chars": 1411,
    "preview": "use crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::En"
  },
  {
    "path": "ricq-core/src/command/img_store/decoder.rs",
    "chars": 1484,
    "preview": "use bytes::Bytes;\nuse prost::Message;\n\nuse crate::command::img_store::GroupImageStoreResp;\nuse crate::common::RQAddr;\nus"
  },
  {
    "path": "ricq-core/src/command/img_store/mod.rs",
    "chars": 297,
    "preview": "use crate::common::RQAddr;\n\npub mod builder;\npub mod decoder;\n\n#[derive(Debug, Clone)]\npub enum GroupImageStoreResp {\n  "
  },
  {
    "path": "ricq-core/src/command/long_conn/builder.rs",
    "chars": 1310,
    "preview": "use crate::command::common::PbToBytes;\nuse crate::protocol::packet::Packet;\n\nimpl crate::Engine {\n    // LongConn.OffPic"
  },
  {
    "path": "ricq-core/src/command/long_conn/decoder.rs",
    "chars": 1687,
    "preview": "use bytes::Bytes;\n\nuse crate::common::RQAddr;\nuse crate::{pb, RQError, RQResult};\nuse prost::Message;\n\nuse super::OffPic"
  },
  {
    "path": "ricq-core/src/command/long_conn/mod.rs",
    "chars": 308,
    "preview": "use crate::common::RQAddr;\n\nmod builder;\nmod decoder;\n\n#[derive(Debug, Clone)]\npub enum OffPicUpResp {\n    Exist {\n     "
  },
  {
    "path": "ricq-core/src/command/longmsg/builder.rs",
    "chars": 642,
    "preview": "use crate::pb;\nuse prost::Message;\n\nimpl super::super::super::Engine {\n    pub fn build_long_req(&self, dst_uin: i64, ms"
  },
  {
    "path": "ricq-core/src/command/longmsg/mod.rs",
    "chars": 17,
    "preview": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/message_svc/builder.rs",
    "chars": 7554,
    "preview": "use prost::Message;\n\nuse crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl su"
  },
  {
    "path": "ricq-core/src/command/message_svc/decoder.rs",
    "chars": 2332,
    "preview": "use bytes::{Buf, Bytes};\n\nuse crate::pb::msg::GetMessageResponse;\nuse crate::{jce, RQError, RQResult};\nuse prost::Messag"
  },
  {
    "path": "ricq-core/src/command/message_svc/mod.rs",
    "chars": 257,
    "preview": "use crate::pb;\n\npub mod builder;\npub mod decoder;\n\npub struct MessageSyncResponse {\n    pub msg_rsp_type: i32,\n    pub s"
  },
  {
    "path": "ricq-core/src/command/mod.rs",
    "chars": 491,
    "preview": "pub mod common;\npub mod config_push_svc;\npub mod friendlist;\npub mod group_anonymous_generate_nick;\npub mod group_member"
  },
  {
    "path": "ricq-core/src/command/multi_msg/builder.rs",
    "chars": 6397,
    "preview": "use std::collections::HashMap;\nuse std::io::Write;\n\nuse flate2::write::GzEncoder;\nuse flate2::Compression;\n\nuse crate::c"
  },
  {
    "path": "ricq-core/src/command/multi_msg/decoder.rs",
    "chars": 750,
    "preview": "use bytes::Bytes;\n\nuse crate::{pb, RQError, RQResult};\nuse prost::Message;\n\nimpl super::super::super::Engine {\n    pub f"
  },
  {
    "path": "ricq-core/src/command/multi_msg/mod.rs",
    "chars": 1342,
    "preview": "use std::collections::HashMap;\nuse std::fmt::Write;\n\nuse crate::msg::MessageChain;\nuse crate::pb;\n\npub mod builder;\npub "
  },
  {
    "path": "ricq-core/src/command/oidb_svc/builder.rs",
    "chars": 18076,
    "preview": "use bytes::{BufMut, BytesMut};\n\nuse super::*;\nuse crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol:"
  },
  {
    "path": "ricq-core/src/command/oidb_svc/decoder.rs",
    "chars": 8586,
    "preview": "use std::collections::HashMap;\n\nuse bytes::{Bytes, BytesMut};\n\nuse crate::command::oidb_svc::GroupAtAllRemainInfo;\nuse c"
  },
  {
    "path": "ricq-core/src/command/oidb_svc/mod.rs",
    "chars": 3417,
    "preview": "use std::collections::HashMap;\n\nuse crate::pb;\n\npub mod builder;\npub mod decoder;\n\n// 群 @全体 剩余次数\n#[derive(Default, Debug"
  },
  {
    "path": "ricq-core/src/command/online_push/builder.rs",
    "chars": 1642,
    "preview": "use std::collections::HashMap;\n\nuse bytes::Bytes;\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;"
  },
  {
    "path": "ricq-core/src/command/online_push/decoder.rs",
    "chars": 6801,
    "preview": "use bytes::{Buf, Bytes};\nuse jcers::Jce;\n\nuse super::*;\nuse crate::common::group_uin2code;\nuse crate::structs::{GroupDis"
  },
  {
    "path": "ricq-core/src/command/online_push/mod.rs",
    "chars": 1066,
    "preview": "use crate::structs::{GroupDisband, GroupLeave, MemberPermissionChange};\nuse crate::{jce, pb};\n\npub mod builder;\npub mod "
  },
  {
    "path": "ricq-core/src/command/pb_message_svc/builder.rs",
    "chars": 1335,
    "preview": "use crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::En"
  },
  {
    "path": "ricq-core/src/command/pb_message_svc/mod.rs",
    "chars": 17,
    "preview": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/profile_service/builder.rs",
    "chars": 6584,
    "preview": "use jcers::JcePut;\n\nuse crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl sup"
  },
  {
    "path": "ricq-core/src/command/profile_service/decoder.rs",
    "chars": 6194,
    "preview": "use std::collections::HashMap;\n\nuse bytes::{Buf, Bytes};\nuse prost::Message;\n\nuse crate::command::profile_service::*;\nus"
  },
  {
    "path": "ricq-core/src/command/profile_service/mod.rs",
    "chars": 1654,
    "preview": "use std::collections::HashMap;\n\nuse bytes::Bytes;\n\npub mod builder;\npub mod decoder;\n\n#[derive(Debug, Default, Clone)]\np"
  },
  {
    "path": "ricq-core/src/command/ptt_center_svr/builder.rs",
    "chars": 2499,
    "preview": "use crate::command::common::PbToBytes;\nuse crate::hex::encode_hex;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\n"
  },
  {
    "path": "ricq-core/src/command/ptt_center_svr/decoder.rs",
    "chars": 891,
    "preview": "use bytes::Bytes;\n\nuse crate::pb::short_video::{ShortVideoRspBody, ShortVideoUploadRsp};\nuse crate::{pb, RQError, RQResu"
  },
  {
    "path": "ricq-core/src/command/ptt_center_svr/mod.rs",
    "chars": 34,
    "preview": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/ptt_store/builder.rs",
    "chars": 3303,
    "preview": "use bytes::Bytes;\n\nuse crate::command::common::PbToBytes;\nuse crate::hex::encode_hex;\nuse crate::pb;\nuse crate::protocol"
  },
  {
    "path": "ricq-core/src/command/ptt_store/decoder.rs",
    "chars": 1243,
    "preview": "use bytes::Bytes;\n\nuse crate::RQResult;\nuse crate::{pb, RQError};\nuse prost::Message;\n\nimpl super::super::super::Engine "
  },
  {
    "path": "ricq-core/src/command/ptt_store/mod.rs",
    "chars": 34,
    "preview": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/reg_prxy_svc/builder.rs",
    "chars": 6457,
    "preview": "use std::collections::HashMap;\nuse std::time::UNIX_EPOCH;\n\nuse bytes::{BufMut, BytesMut};\nuse jcers::JcePut;\n\nuse crate:"
  },
  {
    "path": "ricq-core/src/command/reg_prxy_svc/decoder.rs",
    "chars": 1883,
    "preview": "use bytes::{Buf, Bytes};\n\nuse crate::structs::OtherClientInfo;\nuse crate::{jce, RQError, RQResult};\n\nimpl super::super::"
  },
  {
    "path": "ricq-core/src/command/reg_prxy_svc/mod.rs",
    "chars": 34,
    "preview": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/signature/builder.rs",
    "chars": 1478,
    "preview": "use crate::command::common::PbToBytes;\nuse crate::pb::sig_act;\nuse crate::protocol::packet::Packet;\nuse std::time::UNIX_"
  },
  {
    "path": "ricq-core/src/command/signature/mod.rs",
    "chars": 17,
    "preview": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/stat_svc/builder.rs",
    "chars": 6199,
    "preview": "use std::collections::HashMap;\nuse std::time::UNIX_EPOCH;\n\nuse bytes::{BufMut, Bytes, BytesMut};\nuse jcers::JcePut;\n\nuse"
  },
  {
    "path": "ricq-core/src/command/stat_svc/decoder.rs",
    "chars": 3036,
    "preview": "use bytes::{Buf, Bytes};\nuse jcers::Jce;\n\nuse crate::{jce, RQError, RQResult};\n\nimpl super::super::super::Engine {\n    /"
  },
  {
    "path": "ricq-core/src/command/stat_svc/mod.rs",
    "chars": 1897,
    "preview": "pub mod builder;\npub mod decoder;\n\n#[derive(Debug, Clone)]\npub struct Status {\n    pub online_status: i32,\n    pub ext_o"
  },
  {
    "path": "ricq-core/src/command/summary_card/builder.rs",
    "chars": 3666,
    "preview": "use std::collections::HashMap;\n\nuse bytes::{BufMut, Bytes, BytesMut};\nuse jcers::JcePut;\n\nuse crate::command::common::{p"
  },
  {
    "path": "ricq-core/src/command/summary_card/decoder.rs",
    "chars": 1738,
    "preview": "use bytes::{Buf, Bytes};\n\nuse crate::jce::{RespSummaryCard, RespSummaryCardHead};\nuse crate::structs::SummaryCardInfo;\nu"
  },
  {
    "path": "ricq-core/src/command/summary_card/mod.rs",
    "chars": 34,
    "preview": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/visitor_svc/builder.rs",
    "chars": 1326,
    "preview": "use std::collections::HashMap;\n\nuse bytes::Bytes;\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;"
  },
  {
    "path": "ricq-core/src/command/visitor_svc/mod.rs",
    "chars": 17,
    "preview": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/wtlogin/builder.rs",
    "chars": 28536,
    "preview": "use bytes::{BufMut, BytesMut};\n\nuse crate::binary::packet_writer::{CounterWriter, Either, PacketAppender, PacketWriter, "
  },
  {
    "path": "ricq-core/src/command/wtlogin/decoder.rs",
    "chars": 4389,
    "preview": "use bytes::{Buf, Bytes};\n\nuse crate::binary::BinaryReader;\nuse crate::command::wtlogin::{LoginResponse, QRCodeConfirmed,"
  },
  {
    "path": "ricq-core/src/command/wtlogin/mod.rs",
    "chars": 8430,
    "preview": "#![allow(clippy::large_enum_variant)]\nuse std::collections::HashMap;\nuse std::time::UNIX_EPOCH;\n\nuse bytes::{Buf, BufMut"
  },
  {
    "path": "ricq-core/src/command/wtlogin/tlv_reader.rs",
    "chars": 3617,
    "preview": "use std::collections::HashMap;\n\nuse bytes::{Buf, BufMut, Bytes, BytesMut};\n\nuse crate::binary::BinaryReader;\nuse crate::"
  },
  {
    "path": "ricq-core/src/command/wtlogin/tlv_writer.rs",
    "chars": 27737,
    "preview": "#![allow(clippy::too_many_arguments)]\n\nuse bytes::{BufMut, BytesMut};\nuse std::time::UNIX_EPOCH;\n\nuse crate::binary::pac"
  },
  {
    "path": "ricq-core/src/common.rs",
    "chars": 2259,
    "preview": "use std::net::{IpAddr, Ipv4Addr, SocketAddr};\n\npub fn group_code2uin(code: i64) -> i64 {\n    let mut left = code / 10000"
  },
  {
    "path": "ricq-core/src/crypto/encrypt.rs",
    "chars": 3177,
    "preview": "use bytes::{BufMut, Bytes};\n// use openssl::bn::{BigNum, BigNumContext};\n// use openssl::ec::{EcGroup, EcPoint, EcKey, P"
  },
  {
    "path": "ricq-core/src/crypto/mod.rs",
    "chars": 148,
    "preview": "mod encrypt;\nmod qqtea;\n\npub use self::encrypt::{EncryptECDH, EncryptSession, IEncryptMethod};\npub use self::qqtea::{qqt"
  },
  {
    "path": "ricq-core/src/crypto/qqtea.rs",
    "chars": 4736,
    "preview": "// copy from https://github.com/zkonge/rtea\nuse byteorder::{BigEndian, ByteOrder};\nuse rand::{thread_rng, RngCore};\n\nuse"
  },
  {
    "path": "ricq-core/src/error.rs",
    "chars": 1938,
    "preview": "use std::io;\n\nuse thiserror::Error;\n\npub type RQResult<T> = Result<T, RQError>;\n\n#[derive(Error, Debug)]\npub enum RQErro"
  },
  {
    "path": "ricq-core/src/hex.rs",
    "chars": 807,
    "preview": "use std::fmt::Write;\nuse std::num::ParseIntError;\n\npub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {\n    (0"
  },
  {
    "path": "ricq-core/src/highway/mod.rs",
    "chars": 2764,
    "preview": "use std::net::IpAddr;\nuse std::sync::atomic::{AtomicI32, Ordering};\n\nuse bytes::Bytes;\nuse prost::Message;\n\nuse crate::c"
  },
  {
    "path": "ricq-core/src/jce/mod.rs",
    "chars": 32467,
    "preview": "use std::collections::HashMap;\n\nuse bytes::Bytes;\nuse jcers::{JceGet, JcePut};\n\nmacro_rules! JceStruct {\n    ($struct_na"
  },
  {
    "path": "ricq-core/src/lib.rs",
    "chars": 3961,
    "preview": "#![feature(impl_trait_in_assoc_type)]\n#![feature(type_alias_impl_trait)]\n#![feature(return_position_impl_trait_in_trait)"
  },
  {
    "path": "ricq-core/src/msg/elem/anonymous.rs",
    "chars": 1520,
    "preview": "use crate::msg::{MessageChainBuilder, MessageElem, PushBuilder};\nuse crate::pb::msg;\nuse crate::pb::msg::AnonymousGroupM"
  },
  {
    "path": "ricq-core/src/msg/elem/at.rs",
    "chars": 1492,
    "preview": "use std::fmt;\n\nuse bytes::{Buf, BufMut};\n\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageEl"
  },
  {
    "path": "ricq-core/src/msg/elem/face.rs",
    "chars": 7341,
    "preview": "use std::fmt;\n\nuse prost::Message;\n\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, Pu"
  },
  {
    "path": "ricq-core/src/msg/elem/flash_image.rs",
    "chars": 2146,
    "preview": "use std::fmt;\n\nuse crate::command::common::PbToBytes;\nuse crate::msg::elem::{FriendImage, GroupImage};\nuse crate::msg::{"
  },
  {
    "path": "ricq-core/src/msg/elem/friend_image.rs",
    "chars": 2797,
    "preview": "use std::fmt;\n\nuse serde::{Deserialize, Serialize};\n\nuse crate::msg::elem::flash_image::FlashImage;\nuse crate::msg::{Mes"
  },
  {
    "path": "ricq-core/src/msg/elem/group_image.rs",
    "chars": 3540,
    "preview": "use std::fmt;\n\nuse serde::{Deserialize, Serialize};\n\nuse crate::hex::encode_hex;\nuse crate::msg::elem::flash_image::Flas"
  },
  {
    "path": "ricq-core/src/msg/elem/light_app.rs",
    "chars": 2398,
    "preview": "use std::fmt;\nuse std::io::{Read, Write};\n\nuse flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};\n\nuse super:"
  },
  {
    "path": "ricq-core/src/msg/elem/market_face.rs",
    "chars": 4778,
    "preview": "use derivative;\n\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate:"
  },
  {
    "path": "ricq-core/src/msg/elem/mod.rs",
    "chars": 5724,
    "preview": "use std::fmt;\n\nuse prost::Message;\n\npub use group_image::calculate_image_resource_id;\npub(crate) use text::flush_builder"
  },
  {
    "path": "ricq-core/src/msg/elem/reply.rs",
    "chars": 1494,
    "preview": "use std::fmt;\n\nuse crate::msg::{MessageChainBuilder, MessageElem, PushBuilder};\nuse crate::pb::msg;\n\nuse super::super::M"
  },
  {
    "path": "ricq-core/src/msg/elem/rich_msg.rs",
    "chars": 2317,
    "preview": "use std::fmt;\nuse std::io::{Read, Write};\n\nuse flate2::read::ZlibDecoder;\nuse flate2::write::ZlibEncoder;\nuse flate2::Co"
  },
  {
    "path": "ricq-core/src/msg/elem/text.rs",
    "chars": 1295,
    "preview": "use std::{fmt, mem};\n\nuse crate::msg::{MessageChainBuilder, MessageElem, PushBuilder, PushElem};\nuse crate::pb::msg;\nuse"
  },
  {
    "path": "ricq-core/src/msg/elem/video_file.rs",
    "chars": 2205,
    "preview": "use std::fmt;\n\nuse crate::hex::encode_hex;\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageE"
  },
  {
    "path": "ricq-core/src/msg/fragment.rs",
    "chars": 2038,
    "preview": "use crate::pb::msg;\n\nuse super::MessageChain;\n\nimpl MessageChain {\n    // TODO test\n    // https://github.com/mamoe/mira"
  },
  {
    "path": "ricq-core/src/msg/macros.rs",
    "chars": 583,
    "preview": "#[macro_export]\nmacro_rules! to_elem_vec_impl {\n    ($t:ty) => {\n        impl From<$t> for Vec<MessageElem> {\n          "
  },
  {
    "path": "ricq-core/src/msg/mod.rs",
    "chars": 7427,
    "preview": "use std::fmt;\n\nuse elem::RQElem;\nuse elem::*;\n\nuse crate::pb::msg;\n\npub mod elem;\nmod fragment;\nmod macros;\n\npub type Me"
  },
  {
    "path": "ricq-core/src/pb/cmd0x346/cmd0x346.proto",
    "chars": 10448,
    "preview": "syntax = \"proto3\";\n\npackage cmd0x346;\n\nmessage ApplyCleanTrafficRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n}\nmess"
  },
  {
    "path": "ricq-core/src/pb/cmd0x352/cmd0x352.proto",
    "chars": 3947,
    "preview": "syntax = \"proto2\";\n\npackage cmd0x352;\n/*\nmessage DelImgReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;"
  },
  {
    "path": "ricq-core/src/pb/cmd0x388/cmd0x388.proto",
    "chars": 6841,
    "preview": "syntax = \"proto2\";\n\npackage cmd0x388;\n\nmessage DelImgReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n "
  },
  {
    "path": "ricq-core/src/pb/cmd0x3bb/cmd0x3bb.proto",
    "chars": 633,
    "preview": "syntax = \"proto2\";\n\npackage cmd0x3bb;\n\nmessage AnonyMsg {\n  optional uint32 cmd = 1;\n  optional C3BBReqBody anonyReq = 1"
  },
  {
    "path": "ricq-core/src/pb/cmd0x6ff/smbcmd0x519.proto",
    "chars": 3188,
    "preview": "syntax = \"proto2\";\npackage cmd0x6ff;\n\nmessage C519CRMMsgHead {\n  optional uint32 crmSubCmd = 1;\n  optional uint32 headLe"
  },
  {
    "path": "ricq-core/src/pb/cmd0x6ff/subcmd0x501.proto",
    "chars": 811,
    "preview": "syntax = \"proto2\";\npackage cmd0x6ff;\n\nmessage C501ReqBody {\n  optional SubCmd0x501ReqBody ReqBody = 1281;\n}\nmessage C501"
  },
  {
    "path": "ricq-core/src/pb/cmd0x899/cmd0x899.proto",
    "chars": 1382,
    "preview": "syntax = \"proto2\";\n\npackage cmd0x899;\n\nmessage ReqBody {\n  optional uint64 groupCode = 1;\n  optional uint64 startUin = 2"
  },
  {
    "path": "ricq-core/src/pb/data.proto",
    "chars": 7780,
    "preview": "syntax = \"proto3\";\n\npackage pb;\n\nmessage SSOReserveField {\n  int32 flag = 9;\n  string qimei = 12;\n  int32 newconn_flag ="
  },
  {
    "path": "ricq-core/src/pb/longmsg/longmsg.proto",
    "chars": 1055,
    "preview": "syntax = \"proto3\";\n\npackage longmsg;\n\nmessage LongMsgDeleteReq {\n  bytes msgResid = 1;\n  int32 msgType = 2;\n}\nmessage Lo"
  },
  {
    "path": "ricq-core/src/pb/mod.rs",
    "chars": 560,
    "preview": "#![allow(clippy::all)]\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/pb.rs\"));\n\nmacro_rules! add_includes {\n    ($( $name:ident )"
  },
  {
    "path": "ricq-core/src/pb/msf/register_proxy.proto",
    "chars": 1547,
    "preview": "syntax = \"proto2\";\n\npackage msf;\n\nmessage DiscussList {\n  optional uint64 discussCode = 1;\n  optional uint64 discussSeq "
  },
  {
    "path": "ricq-core/src/pb/msg/TextMsgExt.proto",
    "chars": 560,
    "preview": "syntax = \"proto2\";\n\npackage msg;\n\nmessage ExtChannelInfo {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = "
  },
  {
    "path": "ricq-core/src/pb/msg/head.proto",
    "chars": 3253,
    "preview": "syntax = \"proto2\";\npackage msg;\n\nmessage C2CHead {\n  optional uint64 toUin = 1;\n  optional uint64 fromUin = 2;\n  optiona"
  },
  {
    "path": "ricq-core/src/pb/msg/msg.proto",
    "chars": 22566,
    "preview": "syntax = \"proto2\";\n\npackage msg;\n\nmessage GetMessageRequest {\n  optional SyncFlag syncFlag = 1;\n  optional bytes syncCoo"
  },
  {
    "path": "ricq-core/src/pb/msg/objmsg.proto",
    "chars": 591,
    "preview": "syntax = \"proto3\";\n\npackage msg;\n\nmessage MsgPic {\n  bytes smallPicUrl = 1;\n  bytes originalPicUrl = 2;\n  int32 localPic"
  },
  {
    "path": "ricq-core/src/pb/msg/report.proto",
    "chars": 1669,
    "preview": "syntax = \"proto2\";\n\npackage msg;\n\nmessage PbMsgReadedReportReq {\n  repeated PbGroupReadedReportReq grpReadReport = 1;\n  "
  },
  {
    "path": "ricq-core/src/pb/msgtype0x210/subMsgType0x27.proto",
    "chars": 8111,
    "preview": "syntax = \"proto2\";\n\npackage msgtype0x210;\n\nmessage AddGroup {\n  optional uint32 groupid = 1;\n  optional uint32 sortid = "
  },
  {
    "path": "ricq-core/src/pb/multimsg/multimsg.proto",
    "chars": 1434,
    "preview": "syntax = \"proto3\";\n\npackage multimsg;\n\nmessage ExternMsg {\n  int32 channelType = 1;\n}\nmessage MultiMsgApplyDownReq {\n  b"
  },
  {
    "path": "ricq-core/src/pb/notify/group0x857.proto",
    "chars": 1694,
    "preview": "syntax = \"proto3\";\n\npackage notify;\n\nmessage NotifyMsgBody {\n  AIOGrayTipsInfo optMsgGrayTips = 5;\n  RedGrayTipsInfo opt"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb.proto",
    "chars": 2511,
    "preview": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage OIDBSSOPkg {\n  int32 command = 1;\n  int32 serviceType = 2;\n  int32 result = 3"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x6d6.proto",
    "chars": 3617,
    "preview": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage DeleteFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = "
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x6d8.proto",
    "chars": 3684,
    "preview": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage D6D8ReqBody {\n  optional GetFileInfoReqBody fileInfoReq = 1;\n  optional GetFi"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x758.proto",
    "chars": 604,
    "preview": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage InviteUinInfo {\n  optional uint64 uin = 1;\n  optional uint64 judgeGroupCode ="
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x769.proto",
    "chars": 2139,
    "preview": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage CPU {\n  optional string model = 1;\n  optional uint32 cores = 2;\n  optional ui"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x88d.proto",
    "chars": 4902,
    "preview": "syntax = \"proto2\"; // 似乎查询服务端是通过 exists flag 来返回 group info 的 这地方只能用 proto2\n\npackage oidb;\n\nmessage D88DGroupHeadPortrai"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x8a7.proto",
    "chars": 458,
    "preview": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage D8A7ReqBody {\n  optional uint32 subCmd = 1;\n  optional uint32 limitIntervalTy"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x8fc.proto",
    "chars": 1411,
    "preview": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage D8FCReqBody {\n  optional int64 groupCode = 1;\n  optional int32 showFlag = 2;\n"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x990.proto",
    "chars": 585,
    "preview": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage TranslateReqBody {\n  // TranslateReq translate_req = 1;\n  BatchTranslateReq b"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0xb77.proto",
    "chars": 916,
    "preview": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage DB77ReqBody {\n  uint64 appId = 1;\n  uint32 appType = 2;\n  uint32 msgStyle = 3"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0xe07.proto",
    "chars": 1176,
    "preview": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage DE07ReqBody {\n  int32 version = 1;\n  int32 client = 2;\n  int32 entrance = 3;\n"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0xeac.proto",
    "chars": 2970,
    "preview": "syntax = \"proto3\";\n\npackage oidb;\n\n/*\nmessage ArkMsg {\n  optional string appName = 1;\n  optional string json = 2;\n}\n\nmes"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0xeb7.proto",
    "chars": 3997,
    "preview": "syntax = \"proto2\";\n\npackage oidb;\n\n// DEB7 prefix\nmessage DEB7ReqBody {\n  optional StSignInStatusReq signInStatusReq = 1"
  },
  {
    "path": "ricq-core/src/pb/online_status/OnlineStatusExtInfo.java.proto",
    "chars": 772,
    "preview": "syntax = \"proto2\";\n\npackage online_status;\n\nmessage AutoStateBizInfo {\n  optional uint64 updateTime = 1;\n}\n\nmessage Cust"
  },
  {
    "path": "ricq-core/src/pb/profilecard/busi.proto",
    "chars": 2224,
    "preview": "syntax = \"proto2\";\npackage profilecard;\n\nmessage BusiColor {\n  optional int32 r = 1;\n  optional int32 g = 2;\n  optional "
  },
  {
    "path": "ricq-core/src/pb/profilecard/gate.proto",
    "chars": 1945,
    "preview": "syntax = \"proto2\";\npackage profilecard;\n\nmessage GateCommTaskInfo {\n  optional int32 appid = 1;\n  optional bytes taskDat"
  },
  {
    "path": "ricq-core/src/pb/short_video/short_video.proto",
    "chars": 2311,
    "preview": "syntax = \"proto3\";\npackage short_video;\n\nmessage ShortVideoReqBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  ShortVideoUploa"
  },
  {
    "path": "ricq-core/src/pb/sig_act/sig_act.proto",
    "chars": 1416,
    "preview": "syntax = \"proto2\";\npackage sig_act;\n\nmessage Platform {\n  optional int64 platform = 1;\n  optional string osver = 2;\n  op"
  },
  {
    "path": "ricq-core/src/pb/structmsg/structmsg.proto",
    "chars": 5626,
    "preview": "syntax = \"proto3\";\n\n//option go_package = \"./;structmsg\";\npackage structmsg;\n\nmessage AddFrdSNInfo {\n  int32 notSeeDynam"
  },
  {
    "path": "ricq-core/src/protocol/device.rs",
    "chars": 4111,
    "preview": "use bytes::Bytes;\nuse rand::distributions::DistString;\nuse rand::{distributions::Alphanumeric, Rng, RngCore};\nuse serde:"
  },
  {
    "path": "ricq-core/src/protocol/mod.rs",
    "chars": 110,
    "preview": "pub mod device;\npub mod oicq;\npub mod packet;\npub mod qimei;\npub mod sig;\npub mod transport;\npub mod version;\n"
  },
  {
    "path": "ricq-core/src/protocol/oicq.rs",
    "chars": 3368,
    "preview": "use bytes::{Buf, BufMut, Bytes, BytesMut};\nuse rand::Rng;\n\nuse crate::binary::BinaryWriter;\nuse crate::crypto::{qqtea_de"
  },
  {
    "path": "ricq-core/src/protocol/packet.rs",
    "chars": 1836,
    "preview": "use bytes::Bytes;\n\nuse crate::{RQError, RQResult};\n\n#[derive(PartialEq, derivative::Derivative, Eq)]\n#[derivative(Defaul"
  },
  {
    "path": "ricq-core/src/protocol/qimei.rs",
    "chars": 8279,
    "preview": "use crate::hex::encode_hex;\nuse crate::protocol::device::Device;\nuse crate::protocol::version::Version;\nuse crate::{RQEr"
  },
  {
    "path": "ricq-core/src/protocol/sig.rs",
    "chars": 1564,
    "preview": "use std::collections::HashMap;\n\nuse bytes::Bytes;\n\nuse crate::protocol::device::Device;\n\n#[derive(Default, Debug)]\npub s"
  },
  {
    "path": "ricq-core/src/protocol/transport.rs",
    "chars": 6778,
    "preview": "use std::io::Read;\n\nuse bytes::{Buf, BufMut, Bytes, BytesMut};\nuse flate2::read::ZlibDecoder;\n\nuse crate::binary::{Binar"
  }
]

// ... and 63 more files (download for full content)

About this extraction

This page contains the full source code of the lz1998/rs-qq GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 263 files (894.4 KB), approximately 258.1k tokens, and a symbol index with 1085 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!