[
  {
    "path": ".gitignore",
    "content": ".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*.silk\n*.token\n*.mp4"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n    \"ricq\",\n    \"ricq-core\",\n    \"ricq-guild\"\n]\n\n[workspace.dependencies]\nasync-trait = \"0.1\"\nbytes = \"1\"\ncached = { version = \"0.35\", default-features = false }\nderivative = \"2\"\nflate2 = { version = \"1\", features = [\"rust_backend\"], default-features = false }\nfutures-util = \"0.3\"\nimage = \"0.24\"\njcers = \"0.1\"\nmd5 = \"0.7\"\nprost = { version = \"0.9\", default-features = false }\nrand = \"0.8\"\nserde = \"1\"\ntokio = \"1\"\ntokio-util = \"0.7\"\ntracing = \"0.1\"\n\nbyteorder = \"1\"\ngeneric-array = \"0.14\"\np256 = { version = \"0.10\", default-features = false }\nprost-types = \"0.9\"\nthiserror = \"1\"\n\ndynamic-protobuf = \"0\"\nreqwest = \"0.11\"\ncrypto-common = \"0.1\"\nserde_json = \"1.0\"\nblock-padding = \"0.3\"\nspki = \"0.7\"\nbase64 = \"0.21\"\naes = \"0.8\"\nrsa = \"0.9\"\nx509-cert = \"0.2\"\nchrono = \"0.4\"\ncbc = \"0.1\"\nsha2 = \"0.10\"\n"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in\n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "examples/.gitignore",
    "content": "static/"
  },
  {
    "path": "examples/Cargo.toml",
    "content": "[workspace]\nmembers = [\"*\"]\nexclude = [\"target\", \"static\"]\n\n[profile.release]\nopt-level = 'z'\ndebug = false\nlto = true\nincremental = false\ncodegen-units = 1\nstrip = true\n"
  },
  {
    "path": "examples/password_login/Cargo.toml",
    "content": "[package]\nname = \"password_login\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nricq = { path = \"../../ricq\" }\ntokio = { version = \"1\", features = [\"full\"] }\ntokio-util = { version = \"0.7\", features = [\"codec\"] }\nfutures-util = \"0.3\"\ntracing = \"0.1\"\nserde_json = \"1\"\ntracing-subscriber = { version = \"0.3\", features = [\"fmt\", \"local-time\"] }\ntime = { version = \"0.3\", features = [\"macros\", \"local-offset\"] }\nrand = \"0.8\""
  },
  {
    "path": "examples/password_login/src/main.rs",
    "content": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse futures_util::StreamExt;\nuse rand::prelude::StdRng;\nuse rand::SeedableRng;\nuse tokio_util::codec::{FramedRead, LinesCodec};\nuse tracing::Level;\nuse tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};\n\nuse ricq::client::qimei::get_qimei;\nuse ricq::client::{Connector as _, DefaultConnector};\nuse ricq::ext::common::after_login;\nuse ricq::handler::DefaultHandler;\nuse ricq::qsign::QSignClient;\nuse ricq::structs::ExtOnlineStatus;\nuse ricq::{Client, Device, Protocol};\nuse ricq::{LoginDeviceLocked, LoginNeedCaptcha, LoginResponse, LoginSuccess, LoginUnknownStatus};\n\n#[tokio::main(flavor = \"current_thread\")]\nasync fn main() {\n    tracing_subscriber::registry()\n        .with(\n            tracing_subscriber::fmt::layer()\n                .with_target(true)\n                .with_timer(tracing_subscriber::fmt::time::OffsetTime::new(\n                    time::UtcOffset::__from_hms_unchecked(8, 0, 0),\n                    time::macros::format_description!(\n                        \"[year repr:last_two]-[month]-[day] [hour]:[minute]:[second]\"\n                    ),\n                )),\n        )\n        .with(\n            tracing_subscriber::filter::Targets::new()\n                .with_target(\"ricq\", Level::DEBUG)\n                .with_target(\"password_login\", Level::DEBUG),\n        )\n        .init();\n\n    // load uin and password from env\n    let uin: i64 = std::env::var(\"UIN\")\n        .expect(\"failed to read UIN from env\")\n        .parse()\n        .expect(\"failed to parse UIN\");\n    let password = std::env::var(\"PASSWORD\").expect(\"failed to read PASSWORD from env\");\n\n    let mut rng = StdRng::seed_from_u64(uin as u64);\n    let version = Protocol::AndroidPhone.into();\n    let mut device = Device::random_with_rng(&mut rng);\n    let qimei = get_qimei(&mut rng, &device, &version)\n        .await\n        .expect(\"failed to get qimei\");\n    device.set_qimei(qimei);\n\n    let client = Arc::new(Client::new(\n        device,\n        version,\n        Arc::new(\n            QSignClient::new(\n                String::from(\"http://localhost:8080\"),\n                String::from(\"114514\"),\n                Duration::from_secs(60),\n            )\n            .unwrap(),\n        ),\n        DefaultHandler,\n    ));\n    let handle = tokio::spawn({\n        let client = client.clone();\n        // 连接所有服务器，哪个最快用哪个，可以使用 TcpStream::connect 代替\n        let stream = DefaultConnector.connect(&client).await.unwrap();\n        async move { client.start(stream).await }\n    });\n\n    tokio::task::yield_now().await; // 等一下，确保连上了\n    let mut resp = client\n        .password_login(uin, &password)\n        .await\n        .expect(\"failed to login with password\");\n    loop {\n        match resp {\n            LoginResponse::Success(LoginSuccess {\n                ref account_info, ..\n            }) => {\n                tracing::info!(\"login success: {:?}\", account_info);\n                break;\n            }\n            LoginResponse::DeviceLocked(LoginDeviceLocked {\n                ref sms_phone,\n                ref verify_url,\n                ref message,\n                ..\n            }) => {\n                tracing::info!(\"device locked: {:?}\", message);\n                tracing::info!(\"sms_phone: {:?}\", sms_phone);\n                tracing::info!(\"verify_url: {:?}\", verify_url);\n                tracing::info!(\"手机打开url，处理完成后重启程序\");\n                std::process::exit(0);\n                //也可以走短信验证\n                // resp = client.request_sms().await.expect(\"failed to request sms\");\n            }\n            LoginResponse::NeedCaptcha(LoginNeedCaptcha {\n                ref verify_url,\n                // 图片应该没了\n                image_captcha: ref _image_captcha,\n                ..\n            }) => {\n                tracing::info!(\"滑块URL: {:?}\", verify_url);\n                tracing::info!(\"请输入ticket:\");\n                let mut reader = FramedRead::new(tokio::io::stdin(), LinesCodec::new());\n                let ticket = reader\n                    .next()\n                    .await\n                    .transpose()\n                    .expect(\"failed to read ticket\")\n                    .expect(\"failed to read ticket\");\n                resp = client\n                    .submit_ticket(&ticket)\n                    .await\n                    .expect(\"failed to submit ticket\");\n            }\n            LoginResponse::DeviceLockLogin { .. } => {\n                resp = client\n                    .device_lock_login()\n                    .await\n                    .expect(\"failed to login with device lock\");\n            }\n            LoginResponse::AccountFrozen => {\n                panic!(\"account frozen\");\n            }\n            LoginResponse::TooManySMSRequest => {\n                panic!(\"too many sms request\");\n            }\n            LoginResponse::UnknownStatus(LoginUnknownStatus {\n                ref status,\n                ref tlv_map,\n                ref message,\n            }) => {\n                panic!(\n                    \"unknown login status: {:?}, {:?}, {:?}\",\n                    message, status, tlv_map\n                );\n            }\n        }\n    }\n    tracing::info!(\"{:?}\", resp);\n    after_login(&client).await;\n    {\n        tracing::info!(\"{:?}\", client.get_friend_list().await);\n        tracing::info!(\"{:?}\", client.get_group_list().await);\n    }\n    let d = client.get_allowed_clients().await;\n    tracing::info!(\"{:?}\", d);\n\n    // 等一下，收到 ConfigPushSvc.PushReq 才可以发\n    // use ricq::msg::MessageChain;\n    // tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n    // let img_bytes = tokio::fs::read(\"test.png\").await.unwrap();\n    // let group_image = client\n    //     .upload_group_image(982166018, img_bytes)\n    //     .await\n    //     .unwrap();\n    // let mut chain = MessageChain::default();\n    // chain.push(group_image);\n    // client.send_group_message(982166018, chain).await.ok();\n    let aaa = client\n        .update_online_status(ExtOnlineStatus::StudyOnline)\n        .await;\n    println!(\"{:?}\", aaa);\n\n    // client.delete_essence_message(1095020555, 8114, 2107692422).await\n    // let mem_info = client.get_group_member_info(335783090, 875543543).await;\n    // println!(\"{:?}\", mem_info);\n    // let mem_list = client.get_group_member_list(335783090).await;\n    // println!(\"{:?}\", mem_list);\n    handle.await.unwrap();\n}\n"
  },
  {
    "path": "examples/qrcode_login/Cargo.toml",
    "content": "[package]\nname = \"qrcode_login\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nricq = { path = \"../../ricq\" }\ntokio = { version = \"1\", features = [\"full\"] }\nserde_json = \"1\"\nbytes = \"1\"\ntracing = \"0.1\"\ntracing-subscriber = { version = \"0.3\", features = [\"fmt\"] }\n"
  },
  {
    "path": "examples/qrcode_login/src/main.rs",
    "content": "use std::path::Path;\nuse std::sync::Arc;\n\nuse bytes::Bytes;\nuse tokio::time::{sleep, Duration};\nuse tracing::Level;\nuse tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};\n\nuse ricq::client::{Connector as _, DefaultConnector};\nuse ricq::ext::common::after_login;\nuse ricq::handler::DefaultHandler;\nuse ricq::qsign::QSignClient;\nuse ricq::{Client, Device, Protocol};\nuse ricq::{LoginResponse, QRCodeConfirmed, QRCodeImageFetch, QRCodeState};\n\n#[tokio::main(flavor = \"current_thread\")]\nasync fn main() {\n    tracing_subscriber::registry()\n        .with(tracing_subscriber::fmt::layer().with_target(true))\n        .with(\n            tracing_subscriber::filter::Targets::new()\n                .with_target(\"ricq\", Level::DEBUG)\n                .with_target(\"qrcode_login\", Level::DEBUG),\n        )\n        .init();\n\n    let device = match Path::new(\"device.json\").exists() {\n        true => serde_json::from_str(\n            &tokio::fs::read_to_string(\"device.json\")\n                .await\n                .expect(\"failed to read device.json\"),\n        )\n        .expect(\"failed to parse device info\"),\n        false => {\n            let d = Device::random();\n            tokio::fs::write(\"device.json\", serde_json::to_string(&d).unwrap())\n                .await\n                .expect(\"failed to write device info to file\");\n            d\n        }\n    };\n\n    let client = Arc::new(Client::new(\n        device,\n        Protocol::AndroidWatch.into(),\n        Arc::new(\n            QSignClient::new(\n                String::from(\"http://localhost:8080\"),\n                String::from(\"114514\"),\n                Duration::from_secs(60),\n            )\n            .unwrap(),\n        ),\n        DefaultHandler,\n    ));\n    let handle = tokio::spawn({\n        let client = client.clone();\n        let stream = DefaultConnector.connect(&client).await.unwrap();\n        async move { client.start(stream).await }\n    });\n    tokio::task::yield_now().await; // 等一下，确保连上了\n    let mut resp = client.fetch_qrcode().await.expect(\"failed to fetch qrcode\");\n\n    // // vvv 如果不关心二维码状态，可以用这个替换下面的 vvv\n    // use ricq::ext::login::auto_query_qrcode;\n    // match resp {\n    //     QRCodeState::QRCodeImageFetch {\n    //         ref image_data,\n    //         ref sig,\n    //     } => {\n    //         tokio::fs::write(\"qrcode.png\", &image_data).await.expect(\"failed to write file\");\n    //         if let Err(err) = auto_query_qrcode(&client, sig).await {\n    //             panic!(\"登录失败 {}\", err)\n    //         };\n    //     }\n    //     _ => {\n    //         panic!(\"resp error\")\n    //     }\n    // }\n    // // ^^^ 如果不关心二维码状态，可以用这个替换下面的 ^^^\n\n    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n    // vvv 如果关心二维码状态，可以用这个 vvv\n    let mut image_sig = Bytes::new();\n    loop {\n        match resp {\n            QRCodeState::ImageFetch(QRCodeImageFetch {\n                ref image_data,\n                ref sig,\n            }) => {\n                tokio::fs::write(\"qrcode.png\", &image_data)\n                    .await\n                    .expect(\"failed to write file\");\n                image_sig = sig.clone();\n                tracing::info!(\"二维码: qrcode.png\");\n            }\n            QRCodeState::WaitingForScan => {\n                tracing::info!(\"二维码待扫描\")\n            }\n            QRCodeState::WaitingForConfirm => {\n                tracing::info!(\"二维码待确认\")\n            }\n            QRCodeState::Timeout => {\n                tracing::info!(\"二维码已超时，重新获取\");\n                if let QRCodeState::ImageFetch(QRCodeImageFetch {\n                    ref image_data,\n                    ref sig,\n                }) = client.fetch_qrcode().await.expect(\"failed to fetch qrcode\")\n                {\n                    tokio::fs::write(\"qrcode.png\", &image_data)\n                        .await\n                        .expect(\"failed to write file\");\n                    image_sig = sig.clone();\n                    tracing::info!(\"二维码: qrcode.png\");\n                }\n            }\n            QRCodeState::Confirmed(QRCodeConfirmed {\n                ref tmp_pwd,\n                ref tmp_no_pic_sig,\n                ref tgt_qr,\n                ..\n            }) => {\n                tracing::info!(\"二维码已确认\");\n                let mut login_resp = client\n                    .qrcode_login(tmp_pwd, tmp_no_pic_sig, tgt_qr)\n                    .await\n                    .expect(\"failed to qrcode login\");\n                if let LoginResponse::DeviceLockLogin { .. } = login_resp {\n                    login_resp = client\n                        .device_lock_login()\n                        .await\n                        .expect(\"failed to device lock login\");\n                }\n                tracing::info!(\"{:?}\", login_resp);\n                break;\n            }\n            QRCodeState::Canceled => {\n                panic!(\"二维码已取消\")\n            }\n        }\n        sleep(Duration::from_secs(5)).await;\n        resp = client\n            .query_qrcode_result(&image_sig)\n            .await\n            .expect(\"failed to query qrcode result\");\n    }\n    // ^^^ 如果不关心二维码状态，可以用这个 ^^^\n\n    after_login(&client).await;\n    {\n        tracing::info!(\"{:?}\", client.get_friend_list().await);\n        tracing::info!(\"{:?}\", client.get_group_list().await);\n    }\n\n    handle.await.unwrap();\n}\n"
  },
  {
    "path": "examples/ricq-axum-api/Cargo.toml",
    "content": "[package]\nname = \"ricq-axum-api\"\nversion = \"0.1.0\"\nedition = \"2021\"\ndescription = \"ricq axum api\"\nlicense = \"AGPL-3.0\"\nhomepage = \"https://github.com/lz1998/ricq\"\nrepository = \"https://github.com/lz1998/ricq\"\nreadme = \"README.md\"\nkeywords = [\"ricq\", \"axum\", \"http\", \"api\"]\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\ntokio = { version = \"1\", features = [\"full\"] }\nricq = { path = \"../../ricq\" }\ndashmap = \"5.2\"\nbytes = \"1\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\naxum = { version = \"0.5\", features = [\"json\"] }\nasync-trait = \"0.1\"\nrand = \"0.8\"\ntracing = \"0.1\"\nbase64 = \"0.13\"\ntower = { version = \"0.4\" }\ntower-http = { version = \"0.3\", features = [\"fs\"] }\ntracing-subscriber = { version = \"0.3\", features = [\"fmt\", \"local-time\"] }\ntime = { version = \"0.3\", features = [\"macros\", \"local-offset\"] }"
  },
  {
    "path": "examples/ricq-axum-api/README.md",
    "content": "# ricq-axum-api\n\n1. 下载 UI 文件，放在工作目录 static 文件夹\n\n```bash\n wget https://github.com/lz1998/ricq-react-ui/releases/latest/download/static.zip && unzip static.zip && rm static.zip\n```\n\n2. 实现 Processor trait（仅包含登录后处理逻辑）\n\n> 参考 [processor.rs](https://github.com/lz1998/ricq/blob/master/examples/ricq-axum-api/src/processor.rs#L28)\n\n```rust\n#[async_trait::async_trait]\npub trait Processor {\n    async fn on_login_success(\n        &self,\n        client: Arc<Client>,\n        event_receiver: broadcast::Receiver<QEvent>,\n        credential: Credential,\n        network_join_handle: JoinHandle<()>,\n    );\n    async fn list_client(&self) -> Vec<ClientInfo>;\n    async fn delete_client(&self, uin: i64, protocol: u8);\n} \n```\n\n3. 创建 RicqAxumApi，并启动 axum 服务器\n\n```rust\n#![feature(async_closure)]\n\nuse std::net::SocketAddr;\nuse std::str::FromStr;\nuse std::sync::Arc;\n\nuse axum::{\n    routing::{get, get_service, post},\n    Extension, Router,\n};\nuse dashmap::DashMap;\nuse tower::ServiceBuilder;\nuse tower_http::services::ServeDir;\n\nuse ricq::Client;\nuse ricq_axum_api::handler::{bot, password, qrcode};\nuse ricq_axum_api::RicqAxumApi;\n\ntype ClientProcessor = DashMap<(i64, u8), Arc<Client>>;\n\n#[tokio::main]\nasync fn main() {\n    // 默认处理器，登录后什么也不做，仅作为容器\n    let processor = ClientProcessor::default();\n    let ricq_axum_api = Arc::new(RicqAxumApi::new(processor));\n\n    let app = Router::new()\n        .route(\"/ping\", get(async move || \"pong\"))\n        .nest(\n            \"/login\",\n            Router::new()\n                .nest(\n                    \"/qrcode\",\n                    Router::new()\n                        .route(\"/create\", post(qrcode::create))\n                        .route(\"/list\", get(qrcode::list))\n                        .route(\"/delete\", post(qrcode::delete))\n                        .route(\"/query\", post(qrcode::query)),\n                )\n                .nest(\n                    \"/password\",\n                    Router::new()\n                        .route(\"/create\", post(password::login))\n                        .route(\"/request_sms\", post(password::request_sms))\n                        .route(\"/submit_sms\", post(password::submit_sms))\n                        .route(\"/submit_ticket\", post(password::submit_ticket))\n                        .route(\"/list\", get(password::list))\n                        .route(\"/delete\", post(password::delete)),\n                ),\n        )\n        .nest(\n            \"/bot\",\n            Router::new()\n                .route(\"/list\", get(bot::list))\n                .route(\"/delete\", post(bot::delete)),\n        )\n        .fallback(get_service(ServeDir::new(\"static\")).handle_error(handle_error))\n        .layer(\n            ServiceBuilder::new()\n                .layer(Extension(ricq_axum_api))\n                .into_inner(),\n        );\n    let addr = SocketAddr::from_str(\"0.0.0.0:9000\").expect(\"failed to parse bind_addr\");\n    println!(\"listening on {}\", addr);\n    axum::Server::bind(&addr)\n        .serve(app.into_make_service())\n        .await\n        .unwrap();\n}\n\nasync fn handle_error(_: std::io::Error) -> impl axum::response::IntoResponse {\n    (axum::http::StatusCode::NOT_FOUND, \"Something went wrong...\")\n}\n```\n\n4. 访问 `http://localhost:9000`\n"
  },
  {
    "path": "examples/ricq-axum-api/src/bin/main.rs",
    "content": "#![feature(async_closure)]\n\nuse std::net::SocketAddr;\nuse std::str::FromStr;\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse axum::{\n    routing::{get, get_service, post},\n    Extension, Router,\n};\nuse dashmap::DashMap;\nuse tokio::sync::broadcast;\nuse tokio::task::JoinHandle;\nuse tower::ServiceBuilder;\nuse tower_http::services::ServeDir;\nuse tracing::Level;\nuse tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};\n\nuse ricq::client::event::{FriendMessageEvent, GroupMessageEvent};\nuse ricq::client::{DefaultConnector, NetworkStatus};\nuse ricq::ext::common::after_login;\nuse ricq::ext::reconnect::{auto_reconnect, Credential};\nuse ricq::handler::QEvent;\nuse ricq::Client;\nuse ricq_axum_api::handler::{bot, password, qrcode};\nuse ricq_axum_api::processor::Processor;\nuse ricq_axum_api::u8_protocol::U8Protocol;\nuse ricq_axum_api::{ClientInfo, RicqAxumApi};\n\n// 默认处理器\nstruct ClientProcessor(DashMap<(i64, u8), Arc<Client>>);\n\n#[async_trait::async_trait]\nimpl Processor for ClientProcessor {\n    async fn on_login_success(\n        &self,\n        client: Arc<Client>,\n        mut event_receiver: broadcast::Receiver<QEvent>,\n        credential: Credential,\n        network_join_handle: JoinHandle<()>,\n    ) {\n        let uin = client.uin().await;\n        let protocol = client.version().await.protocol.to_u8();\n        self.0.insert((uin, protocol), client.clone());\n        after_login(&client).await;\n\n        tokio::spawn(async move {\n            while let Ok(event) = event_receiver.recv().await {\n                match event {\n                    QEvent::GroupMessage(e) => {\n                        let GroupMessageEvent {\n                            inner: message,\n                            client,\n                        } = e;\n                        tracing::info!(\n                            \"GROUP_MSG, code: {}, content: {}\",\n                            message.group_code,\n                            message.elements.to_string()\n                        );\n                        client\n                            .send_group_message(message.group_code, message.elements)\n                            .await\n                            .ok();\n                    }\n                    QEvent::FriendMessage(e) => {\n                        let FriendMessageEvent {\n                            inner: message,\n                            client,\n                        } = e;\n                        tracing::info!(\n                            \"FRIEND_MSG, code: {}, content: {}\",\n                            message.from_uin,\n                            message.elements.to_string()\n                        );\n                        client\n                            .send_friend_message(message.from_uin, message.elements)\n                            .await\n                            .ok();\n                    }\n                    other => {\n                        tracing::info!(\"{:?}\", other)\n                    }\n                }\n            }\n        });\n\n        // DONT BLOCK\n        tokio::spawn(async move {\n            network_join_handle.await.ok();\n            auto_reconnect(\n                client,\n                credential,\n                Duration::from_secs(10),\n                10,\n                DefaultConnector,\n            )\n            .await;\n        });\n    }\n\n    async fn list_client(&self) -> Vec<ClientInfo> {\n        let mut infos = Vec::new();\n        for cli in self.0.iter() {\n            let (uin, protocol) = cli.key();\n            let client = cli.value();\n            infos.push(ClientInfo {\n                uin: *uin,\n                nick: client.account_info.read().await.nickname.clone(),\n                status: client.get_status(),\n                protocol: *protocol,\n            });\n        }\n        infos\n    }\n\n    async fn delete_client(&self, uin: i64, protocol: u8) {\n        if let Some((_, client)) = self.0.remove(&(uin, protocol)) {\n            client.stop(NetworkStatus::Stop);\n        }\n    }\n}\n\n#[tokio::main]\nasync fn main() {\n    // 初始化日志\n    tracing_subscriber::registry()\n        .with(\n            tracing_subscriber::fmt::layer()\n                .with_target(true)\n                .with_timer(tracing_subscriber::fmt::time::OffsetTime::new(\n                    time::UtcOffset::__from_hms_unchecked(8, 0, 0),\n                    time::macros::format_description!(\n                        \"[year repr:last_two]-[month]-[day] [hour]:[minute]:[second]\"\n                    ),\n                )),\n        )\n        .with(\n            tracing_subscriber::filter::Targets::new()\n                .with_target(\"main\", Level::DEBUG)\n                .with_target(\"ricq\", Level::DEBUG)\n                .with_target(\"ricq_axum_api\", Level::DEBUG),\n        )\n        .init();\n\n    let processor = ClientProcessor(Default::default());\n    let ricq_axum_api = Arc::new(RicqAxumApi::new(processor));\n\n    let app = Router::new()\n        .route(\"/ping\", get(async move || \"pong\"))\n        .nest(\n            \"/login\",\n            Router::new()\n                .nest(\n                    \"/qrcode\",\n                    Router::new()\n                        .route(\"/create\", post(qrcode::create::<ClientProcessor>))\n                        .route(\"/list\", get(qrcode::list::<ClientProcessor>))\n                        .route(\"/delete\", post(qrcode::delete::<ClientProcessor>))\n                        .route(\"/query\", post(qrcode::query::<ClientProcessor>)),\n                )\n                .nest(\n                    \"/password\",\n                    Router::new()\n                        .route(\"/create\", post(password::login::<ClientProcessor>))\n                        .route(\n                            \"/request_sms\",\n                            post(password::request_sms::<ClientProcessor>),\n                        )\n                        .route(\"/submit_sms\", post(password::submit_sms::<ClientProcessor>))\n                        .route(\n                            \"/submit_ticket\",\n                            post(password::submit_ticket::<ClientProcessor>),\n                        )\n                        .route(\"/list\", get(password::list::<ClientProcessor>))\n                        .route(\"/delete\", post(password::delete::<ClientProcessor>)),\n                ),\n        )\n        .nest(\n            \"/bot\",\n            Router::new()\n                .route(\"/list\", get(bot::list::<ClientProcessor>))\n                .route(\"/delete\", post(bot::delete::<ClientProcessor>)),\n        )\n        .fallback(get_service(ServeDir::new(\"static\")).handle_error(handle_error))\n        .layer(\n            ServiceBuilder::new()\n                .layer(Extension(ricq_axum_api))\n                .into_inner(),\n        );\n    let addr = SocketAddr::from_str(\"0.0.0.0:9000\").expect(\"failed to parse bind_addr\");\n    println!(\"listening on {}\", addr);\n    axum::Server::bind(&addr)\n        .serve(app.into_make_service())\n        .await\n        .unwrap();\n}\n\nasync fn handle_error(_: std::io::Error) -> impl axum::response::IntoResponse {\n    (axum::http::StatusCode::NOT_FOUND, \"Something went wrong...\")\n}\n"
  },
  {
    "path": "examples/ricq-axum-api/src/handler/bot.rs",
    "content": "use std::sync::Arc;\n\nuse axum::http::StatusCode;\nuse axum::{Extension, Json};\nuse serde::{Deserialize, Serialize};\n\nuse crate::processor::Processor;\nuse crate::{ClientInfo, RicqAxumApi};\n\n#[derive(Debug, Default, Serialize, Deserialize)]\npub struct ListBotResp {\n    pub bots: Vec<ClientInfo>,\n}\n\npub async fn list<P: Processor>(\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<ListBotResp>, StatusCode> {\n    Ok(Json(ListBotResp {\n        bots: ricq_axum_api.processor.list_client().await,\n    }))\n}\n\n#[derive(Debug, Default, Serialize, Deserialize)]\npub struct DeleteBotReq {\n    uin: i64,\n    protocol: u8,\n}\n\n#[derive(Debug, Default, Serialize, Deserialize)]\npub struct DeleteBotResp {}\n\npub async fn delete<P: Processor>(\n    Json(req): Json<DeleteBotReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<DeleteBotResp>, StatusCode> {\n    ricq_axum_api\n        .processor\n        .delete_client(req.uin, req.protocol)\n        .await;\n    Ok(Json(DeleteBotResp {}))\n}\n"
  },
  {
    "path": "examples/ricq-axum-api/src/handler/mod.rs",
    "content": "pub mod bot;\npub mod password;\npub mod qrcode;\n"
  },
  {
    "path": "examples/ricq-axum-api/src/handler/password.rs",
    "content": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse axum::http::StatusCode;\nuse axum::{Extension, Json};\nuse rand::{prelude::StdRng, SeedableRng};\nuse serde::{Deserialize, Serialize};\n\nuse ricq::client::{Connector as _, DefaultConnector, NetworkStatus};\nuse ricq::ext::reconnect::{Credential, Password};\nuse ricq::qsign::QSignClient;\nuse ricq::version::get_version;\nuse ricq::{Client, Device, LoginDeviceLocked, LoginNeedCaptcha, LoginResponse, Protocol};\n\nuse crate::processor::Processor;\nuse crate::u8_protocol::U8Protocol;\nuse crate::{PasswordClient, RicqAxumApi};\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize)]\npub struct CreateClientReq {\n    pub uin: i64,\n    pub protocol: u8,\n    pub password: String,\n    pub device_seed: Option<u64>,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize)]\npub struct SubmitTicketReq {\n    pub uin: i64,\n    pub protocol: u8,\n    pub ticket: String,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize)]\npub struct RequestSmsReq {\n    pub uin: i64,\n    pub protocol: u8,\n}\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize)]\npub struct SubmitSmsReq {\n    pub uin: i64,\n    pub protocol: u8,\n    pub sms: String,\n}\n\n#[derive(Default, Debug, Clone, Serialize)]\npub struct PasswordLoginResp {\n    pub state: String,\n    pub captcha_url: Option<String>,\n    pub verify_url: Option<String>,\n    pub sms_phone: Option<String>,\n    pub message: Option<String>,\n}\n\nimpl From<LoginResponse> for PasswordLoginResp {\n    fn from(login_response: LoginResponse) -> Self {\n        let mut resp = PasswordLoginResp::default();\n        match login_response {\n            LoginResponse::Success(_) => {\n                resp.state = \"success\".into();\n            }\n            LoginResponse::NeedCaptcha(LoginNeedCaptcha { ref verify_url, .. }) => {\n                resp.state = \"need_captcha\".into();\n                resp.captcha_url = verify_url.clone();\n            }\n            LoginResponse::AccountFrozen => {\n                resp.state = \"account_frozen\".into();\n            }\n            LoginResponse::DeviceLocked(LoginDeviceLocked {\n                ref verify_url,\n                ref message,\n                ref sms_phone,\n                ..\n            }) => {\n                resp.state = \"device_locked\".into();\n                resp.verify_url = verify_url.clone();\n                resp.sms_phone = sms_phone.clone();\n                resp.message = message.clone();\n            }\n            LoginResponse::TooManySMSRequest => {\n                resp.state = \"too_many_sms_request\".into();\n            }\n            LoginResponse::DeviceLockLogin(_) => {\n                resp.state = \"device_lock_login\".into();\n            }\n            LoginResponse::UnknownStatus(status) => {\n                resp.state = \"unknown\".into();\n                resp.message = Some(format!(\n                    \"status: {} message: {}\",\n                    status.status, status.message\n                ));\n            }\n        };\n        resp\n    }\n}\n\npub async fn login<P: Processor>(\n    Json(req): Json<CreateClientReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<PasswordLoginResp>, StatusCode> {\n    let mut rand_seed = req.device_seed.unwrap_or(req.uin as u64);\n    if rand_seed == 0 {\n        rand_seed = req.uin as u64;\n    }\n    let device = Device::random_with_rng(&mut StdRng::seed_from_u64(rand_seed));\n    let protocol = Protocol::from_u8(req.protocol);\n    let (sender, receiver) = tokio::sync::broadcast::channel(10);\n    let cli = Arc::new(Client::new(\n        device,\n        get_version(protocol.clone()),\n        Arc::new(\n            QSignClient::new(\n                String::from(\"http://localhost:8080\"),\n                String::from(\"114514\"),\n                Duration::from_secs(60),\n            )\n            .unwrap(),\n        ),\n        sender,\n    ));\n    let connector = DefaultConnector;\n    let stream = connector\n        .connect(&cli)\n        .await\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    let c = cli.clone();\n    let network_join_handle = tokio::spawn(async move { c.start(stream).await });\n    tokio::task::yield_now().await;\n    let mut resp = cli\n        .password_login(req.uin, &req.password)\n        .await\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    if let LoginResponse::DeviceLockLogin(_) = resp {\n        resp = cli\n            .device_lock_login()\n            .await\n            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    }\n    let credential = Credential::Password(Password {\n        uin: req.uin,\n        password: req.password,\n    });\n    if let LoginResponse::Success(_) = resp {\n        tracing::info!(\"login success: {} {:?}\", req.uin, req.protocol);\n        ricq_axum_api\n            .processor\n            .on_login_success(cli, receiver, credential, network_join_handle)\n            .await;\n    } else if let Some(old) = ricq_axum_api.password_clients.insert(\n        (req.uin, protocol.to_u8()),\n        PasswordClient {\n            client: cli,\n            login_response: resp.clone(),\n            event_receiver: receiver,\n            network_join_handle,\n            credential,\n        },\n    ) {\n        old.client.stop(NetworkStatus::Stop);\n    }\n    Ok(Json(PasswordLoginResp::from(resp)))\n}\n\npub async fn submit_ticket<P: Processor>(\n    Json(req): Json<SubmitTicketReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<PasswordLoginResp>, StatusCode> {\n    let mut resp = ricq_axum_api\n        .password_clients\n        .get(&(req.uin, req.protocol))\n        .ok_or(StatusCode::BAD_REQUEST)?\n        .client\n        .submit_ticket(&req.ticket)\n        .await\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    if let LoginResponse::DeviceLockLogin(_) = resp {\n        resp = ricq_axum_api\n            .password_clients\n            .get(&(req.uin, req.protocol))\n            .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?\n            .client\n            .device_lock_login()\n            .await\n            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    }\n    if let LoginResponse::Success(_) = resp {\n        if let Some(((uin, protocol), client)) = ricq_axum_api\n            .password_clients\n            .remove(&(req.uin, req.protocol))\n        {\n            tracing::info!(\"login success: {} {:?}\", uin, Protocol::from_u8(protocol));\n            ricq_axum_api\n                .processor\n                .on_login_success(\n                    client.client,\n                    client.event_receiver,\n                    client.credential,\n                    client.network_join_handle,\n                )\n                .await;\n        } else {\n            tracing::warn!(\"failed to remove client: {}\", req.uin);\n        }\n    } else {\n        ricq_axum_api\n            .password_clients\n            .get_mut(&(req.uin, req.protocol))\n            .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?\n            .login_response = resp.clone();\n    }\n    Ok(Json(PasswordLoginResp::from(resp)))\n}\n\npub async fn request_sms<P: Processor>(\n    Json(req): Json<RequestSmsReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<PasswordLoginResp>, StatusCode> {\n    let resp = ricq_axum_api\n        .password_clients\n        .get(&(req.uin, req.protocol))\n        .ok_or(StatusCode::BAD_REQUEST)?\n        .client\n        .request_sms()\n        .await\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    ricq_axum_api\n        .password_clients\n        .get_mut(&(req.uin, req.protocol))\n        .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?\n        .login_response = resp.clone();\n    Ok(Json(PasswordLoginResp::from(resp)))\n}\n\npub async fn submit_sms<P: Processor>(\n    Json(req): Json<SubmitSmsReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<PasswordLoginResp>, StatusCode> {\n    let mut resp = ricq_axum_api\n        .password_clients\n        .get(&(req.uin, req.protocol))\n        .ok_or(StatusCode::BAD_REQUEST)?\n        .client\n        .submit_sms_code(&req.sms)\n        .await\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    if let LoginResponse::DeviceLockLogin(_) = resp {\n        resp = ricq_axum_api\n            .password_clients\n            .get(&(req.uin, req.protocol))\n            .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?\n            .client\n            .device_lock_login()\n            .await\n            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    }\n    if let LoginResponse::Success(_) = resp {\n        let cli = ricq_axum_api\n            .password_clients\n            .remove(&(req.uin, req.protocol));\n        if let Some(((uin, protocol), client)) = cli {\n            tracing::info!(\"login success: {} {:?}\", uin, Protocol::from_u8(protocol));\n            ricq_axum_api\n                .processor\n                .on_login_success(\n                    client.client,\n                    client.event_receiver,\n                    client.credential,\n                    client.network_join_handle,\n                )\n                .await;\n        } else {\n            tracing::warn!(\"failed to remove client: {}\", req.uin);\n        }\n    } else {\n        ricq_axum_api\n            .password_clients\n            .get_mut(&(req.uin, req.protocol))\n            .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?\n            .login_response = resp.clone();\n    }\n    Ok(Json(PasswordLoginResp::from(resp)))\n}\n\n#[derive(Default, Serialize)]\npub struct ListClientResp {\n    pub clients: Vec<ListClientRespClient>,\n}\n\n#[derive(Default, Serialize)]\npub struct ListClientRespClient {\n    pub uin: i64,\n    pub protocol: u8,\n    pub resp: PasswordLoginResp,\n}\n\npub async fn list<P: Processor>(\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<ListClientResp>, StatusCode> {\n    let mut clients = Vec::new();\n    for c in ricq_axum_api.password_clients.iter() {\n        clients.push(ListClientRespClient {\n            uin: c.key().0,\n            protocol: c.client.version().await.protocol.to_u8(),\n            resp: PasswordLoginResp::from(c.login_response.clone()),\n        })\n    }\n    Ok(Json(ListClientResp { clients }))\n}\n\n#[derive(Default, Serialize, Deserialize)]\npub struct DeleteClientReq {\n    pub uin: i64,\n    pub protocol: u8,\n}\n\n#[derive(Default, Serialize, Deserialize)]\npub struct DeleteClientResp {}\n\npub async fn delete<P: Processor>(\n    Json(req): Json<DeleteClientReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<DeleteClientResp>, StatusCode> {\n    if let Some((_, cli)) = ricq_axum_api\n        .password_clients\n        .remove(&(req.uin, req.protocol))\n    {\n        cli.client.stop(NetworkStatus::Stop);\n    }\n    Ok(Json(DeleteClientResp {}))\n}\n"
  },
  {
    "path": "examples/ricq-axum-api/src/handler/qrcode.rs",
    "content": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse axum::http::StatusCode;\nuse axum::{Extension, Json};\nuse bytes::Bytes;\nuse rand::{prelude::StdRng, SeedableRng};\nuse serde::{Deserialize, Serialize};\n\nuse ricq::client::NetworkStatus;\nuse ricq::client::{Connector as _, DefaultConnector};\nuse ricq::device::Device;\nuse ricq::ext::reconnect::Credential;\nuse ricq::qsign::QSignClient;\nuse ricq::version::{get_version, Protocol};\nuse ricq::{Client, LoginResponse, QRCodeState};\n\nuse crate::processor::Processor;\nuse crate::u8_protocol::U8Protocol;\nuse crate::QRCodeClient;\nuse crate::RicqAxumApi;\n\nmod base64 {\n    extern crate base64;\n\n    use serde::{de, Deserialize, Deserializer, Serializer};\n\n    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: Serializer,\n    {\n        serializer.serialize_str(&base64::encode(bytes))\n    }\n\n    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        let s = <&str>::deserialize(deserializer)?;\n        base64::decode(s).map_err(de::Error::custom)\n    }\n}\n\n#[derive(Debug, Default, Serialize, Deserialize)]\npub struct CreateClientReq {\n    pub device_seed: Option<u64>,\n    pub protocol: u8,\n}\n\n#[derive(Debug, Default, Serialize, Deserialize)]\npub struct CreateClientResp {\n    #[serde(with = \"base64\")]\n    pub sig: Vec<u8>,\n    #[serde(with = \"base64\")]\n    pub image: Vec<u8>,\n}\n\npub async fn create<P: Processor>(\n    Json(req): Json<CreateClientReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<CreateClientResp>, StatusCode> {\n    let rand_seed = req.device_seed.unwrap_or_else(rand::random);\n    let device = Device::random_with_rng(&mut StdRng::seed_from_u64(rand_seed));\n    let protocol = match Protocol::from_u8(req.protocol) {\n        Protocol::MacOS => Protocol::MacOS,\n        Protocol::AndroidWatch => Protocol::AndroidWatch,\n        _ => return Err(StatusCode::BAD_REQUEST),\n    };\n    let (sender, receiver) = tokio::sync::broadcast::channel(10);\n    let cli = Arc::new(Client::new(\n        device,\n        get_version(protocol),\n        Arc::new(\n            QSignClient::new(\n                String::from(\"http://localhost:8080\"),\n                String::from(\"114514\"),\n                Duration::from_secs(60),\n            )\n            .unwrap(),\n        ),\n        sender,\n    ));\n    let connector = DefaultConnector;\n    let stream = connector\n        .connect(&cli)\n        .await\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    let c = cli.clone();\n    let network_join_handle = tokio::spawn(async move { c.start(stream).await });\n    tokio::task::yield_now().await;\n    let resp = cli\n        .fetch_qrcode()\n        .await\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n\n    if let QRCodeState::ImageFetch(image_fetch) = resp {\n        ricq_axum_api.qrcode_clients.insert(\n            image_fetch.sig.clone(),\n            QRCodeClient {\n                sig: image_fetch.sig.to_vec(),\n                image: image_fetch.image_data.to_vec(),\n                state: QRCodeState::ImageFetch(image_fetch.clone()),\n                client: cli,\n                event_receiver: receiver,\n                network_join_handle,\n            },\n        );\n        Ok(Json(CreateClientResp {\n            sig: image_fetch.sig.to_vec(),\n            image: image_fetch.image_data.to_vec(),\n        }))\n    } else {\n        Err(StatusCode::INTERNAL_SERVER_ERROR)\n    }\n}\n\n#[derive(Debug, Default, Serialize, Deserialize)]\npub struct QueryQRCodeReq {\n    #[serde(with = \"base64\")]\n    pub sig: Vec<u8>,\n}\n\n#[derive(Debug, Default, Serialize, Deserialize)]\npub struct QueryQRCodeResp {\n    pub state: String,\n}\n\npub async fn query<P: Processor>(\n    Json(req): Json<QueryQRCodeReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<QueryQRCodeResp>, StatusCode> {\n    let sig = Bytes::from(req.sig);\n\n    let resp = ricq_axum_api\n        .qrcode_clients\n        .get(&sig)\n        .ok_or(StatusCode::BAD_REQUEST)?\n        .client\n        .query_qrcode_result(&sig)\n        .await\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    let state = match resp {\n        QRCodeState::ImageFetch(_) => \"image_fetch\",\n        QRCodeState::WaitingForScan => \"waiting_for_scan\",\n        QRCodeState::WaitingForConfirm => \"waiting_for_confirm\",\n        QRCodeState::Timeout => \"timeout\",\n        QRCodeState::Confirmed(_) => \"confirmed\",\n        QRCodeState::Canceled => \"canceled\",\n    }\n    .to_string();\n    ricq_axum_api\n        .qrcode_clients\n        .get_mut(&sig)\n        .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?\n        .state = resp.clone();\n    if let QRCodeState::Confirmed(confirmed) = resp {\n        let (_, cli) = ricq_axum_api.qrcode_clients.remove(&sig).unwrap();\n        let mut resp = cli\n            .client\n            .qrcode_login(\n                &confirmed.tmp_pwd,\n                &confirmed.tmp_no_pic_sig,\n                &confirmed.tgt_qr,\n            )\n            .await\n            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n\n        if let LoginResponse::DeviceLockLogin(_) = resp {\n            resp = cli\n                .client\n                .device_lock_login()\n                .await\n                .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n        }\n        if let LoginResponse::Success(_) = resp {\n            let uin = cli.client.uin().await;\n            let credential = Credential::Token(cli.client.gen_token().await);\n            tracing::info!(\"login success: {}\", uin);\n            ricq_axum_api\n                .processor\n                .on_login_success(\n                    cli.client,\n                    cli.event_receiver,\n                    credential,\n                    cli.network_join_handle,\n                )\n                .await;\n        }\n    }\n    Ok(Json(QueryQRCodeResp { state }))\n}\n\n#[derive(Default, Serialize)]\npub struct ListClientResp {\n    pub clients: Vec<ListClientRespClient>,\n}\n\n#[derive(Default, Serialize)]\npub struct ListClientRespClient {\n    #[serde(with = \"base64\")]\n    pub sig: Vec<u8>,\n    #[serde(with = \"base64\")]\n    pub image: Vec<u8>,\n    pub protocol: u8,\n    pub state: String,\n}\n\npub async fn list<P: Processor>(\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<ListClientResp>, StatusCode> {\n    let mut clients = Vec::new();\n    for c in ricq_axum_api.qrcode_clients.iter() {\n        clients.push(ListClientRespClient {\n            sig: c.sig.to_vec(),\n            image: c.image.clone(),\n            protocol: c.client.version().await.protocol.to_u8(),\n            state: match c.state {\n                QRCodeState::ImageFetch(_) => \"image_fetch\",\n                QRCodeState::WaitingForScan => \"waiting_for_scan\",\n                QRCodeState::WaitingForConfirm => \"waiting_for_confirm\",\n                QRCodeState::Timeout => \"timeout\",\n                QRCodeState::Confirmed(_) => \"confirmed\",\n                QRCodeState::Canceled => \"canceled\",\n            }\n            .into(),\n        })\n    }\n    Ok(Json(ListClientResp { clients }))\n}\n\n#[derive(Default, Serialize, Deserialize)]\npub struct DeleteClientReq {\n    #[serde(with = \"base64\")]\n    pub sig: Vec<u8>,\n}\n\n#[derive(Default, Serialize, Deserialize)]\npub struct DeleteClientResp {}\n\npub async fn delete<P: Processor>(\n    Json(req): Json<DeleteClientReq>,\n    ricq_axum_api: Extension<Arc<RicqAxumApi<P>>>,\n) -> Result<Json<DeleteClientResp>, StatusCode> {\n    if let Some((_, cli)) = ricq_axum_api.qrcode_clients.remove(&Bytes::from(req.sig)) {\n        cli.client.stop(NetworkStatus::Stop);\n    }\n    Ok(Json(DeleteClientResp {}))\n}\n"
  },
  {
    "path": "examples/ricq-axum-api/src/lib.rs",
    "content": "use bytes::Bytes;\nuse dashmap::DashMap;\nuse ricq::ext::reconnect::Credential;\nuse ricq::handler::QEvent;\nuse ricq::{Client, LoginResponse, QRCodeState};\nuse std::sync::Arc;\nuse tokio::sync::broadcast;\nuse tokio::task::JoinHandle;\n\npub mod handler;\npub mod processor;\npub mod u8_protocol;\nuse serde::{Deserialize, Serialize};\n\nuse crate::processor::Processor;\n\n#[derive(Debug, Default, Serialize, Deserialize)]\npub struct ClientInfo {\n    pub uin: i64,\n    pub nick: String,\n    pub status: u8,\n    pub protocol: u8,\n}\n\npub struct PasswordClient {\n    pub client: Arc<Client>,\n    pub login_response: LoginResponse,\n    pub event_receiver: broadcast::Receiver<QEvent>,\n    pub network_join_handle: JoinHandle<()>,\n    pub credential: Credential,\n}\n\npub struct QRCodeClient {\n    pub sig: Vec<u8>,\n    pub image: Vec<u8>,\n    pub state: QRCodeState,\n    pub client: Arc<Client>,\n    pub event_receiver: broadcast::Receiver<QEvent>,\n    pub network_join_handle: JoinHandle<()>,\n}\n\npub struct RicqAxumApi<P: Processor> {\n    // key: uin+protocol\n    password_clients: DashMap<(i64, u8), PasswordClient>,\n\n    // key: sig\n    qrcode_clients: DashMap<Bytes, QRCodeClient>,\n\n    // 仅负责登录后的逻辑\n    processor: P,\n}\n\nimpl<P: Processor> RicqAxumApi<P> {\n    pub fn new(processor: P) -> Self {\n        Self {\n            password_clients: Default::default(),\n            qrcode_clients: Default::default(),\n            processor,\n        }\n    }\n}\n"
  },
  {
    "path": "examples/ricq-axum-api/src/processor.rs",
    "content": "use std::sync::Arc;\n\nuse tokio::sync::broadcast;\nuse tokio::task::JoinHandle;\n\nuse ricq::{ext::reconnect::Credential, handler::QEvent, Client};\n\nuse crate::ClientInfo;\n\n#[async_trait::async_trait]\npub trait Processor {\n    async fn on_login_success(\n        &self,\n        client: Arc<Client>,\n        event_receiver: broadcast::Receiver<QEvent>,\n        credential: Credential,\n        network_join_handle: JoinHandle<()>,\n    );\n    async fn list_client(&self) -> Vec<ClientInfo>;\n    async fn delete_client(&self, uin: i64, protocol: u8);\n}\n"
  },
  {
    "path": "examples/ricq-axum-api/src/u8_protocol.rs",
    "content": "use ricq::version::Protocol;\n\npub trait U8Protocol {\n    fn to_u8(&self) -> u8;\n    fn from_u8(n: u8) -> Self;\n}\n\nimpl U8Protocol for Protocol {\n    fn to_u8(&self) -> u8 {\n        match self {\n            Protocol::AndroidPhone => 1,\n            Protocol::AndroidWatch => 2,\n            Protocol::MacOS => 3,\n            Protocol::QiDian => 4,\n            Protocol::IPad => 5,\n            Protocol::AndroidPad => 6,\n        }\n    }\n\n    fn from_u8(n: u8) -> Self {\n        match n {\n            1 => Protocol::AndroidPhone,\n            2 => Protocol::AndroidWatch,\n            3 => Protocol::MacOS,\n            4 => Protocol::QiDian,\n            5 => Protocol::IPad,\n            _ => Protocol::IPad,\n        }\n    }\n}\n"
  },
  {
    "path": "examples/token_login/Cargo.toml",
    "content": "[package]\nname = \"token_login\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nricq = { path = \"../../ricq\" }\ntokio = { version = \"1\", features = [\"full\"] }\ntracing = \"0.1\"\nserde_json = \"1\"\ntracing-subscriber = { version = \"0.3\", features = [\"fmt\", \"local-time\"] }\ntime = { version = \"0.3\", features = [\"macros\", \"local-offset\"] }\nrand = \"0.8\"\n"
  },
  {
    "path": "examples/token_login/src/main.rs",
    "content": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse rand::prelude::StdRng;\nuse rand::SeedableRng;\nuse tracing::Level;\nuse tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};\n\nuse ricq::client::{Connector as _, DefaultConnector, Token};\nuse ricq::ext::common::after_login;\nuse ricq::handler::DefaultHandler;\nuse ricq::qsign::QSignClient;\nuse ricq::{Client, Device, Protocol};\n\n#[tokio::main(flavor = \"current_thread\")]\nasync fn main() {\n    tracing_subscriber::registry()\n        .with(\n            tracing_subscriber::fmt::layer()\n                .with_target(true)\n                .with_timer(tracing_subscriber::fmt::time::OffsetTime::new(\n                    time::UtcOffset::__from_hms_unchecked(8, 0, 0),\n                    time::macros::format_description!(\n                        \"[year repr:last_two]-[month]-[day] [hour]:[minute]:[second]\"\n                    ),\n                )),\n        )\n        .with(\n            tracing_subscriber::filter::Targets::new()\n                .with_target(\"ricq\", Level::DEBUG)\n                .with_target(\"token_login\", Level::DEBUG),\n        )\n        .init();\n\n    let token = tokio::fs::read_to_string(\"session.token\")\n        .await\n        .expect(\"failed to read token\");\n    let token: Token = serde_json::from_str(&token).expect(\"failed to parse token\");\n    let device = Device::random_with_rng(&mut StdRng::seed_from_u64(token.uin as u64));\n\n    let client = Arc::new(Client::new(\n        device,\n        Protocol::IPad.into(),\n        Arc::new(\n            QSignClient::new(\n                String::from(\"http://localhost:8080\"),\n                String::from(\"114514\"),\n                Duration::from_secs(60),\n            )\n            .unwrap(),\n        ),\n        DefaultHandler,\n    ));\n\n    let handle = tokio::spawn({\n        let client = client.clone();\n        let stream = DefaultConnector.connect(&client).await.unwrap();\n        async move { client.start(stream).await }\n    });\n    // 直接使用 TcpStream，目前不推荐\n    // let stream = TcpStream::connect(client.get_address())\n    //     .await\n    //     .expect(\"failed to connect\");\n    // let c = client.clone();\n    // let handle = tokio::spawn(async move { c.start(stream).await });\n\n    tokio::task::yield_now().await; // 等一下，确保连上了\n    let resp = client\n        .token_login(token)\n        .await\n        .expect(\"failed to login with token\");\n\n    tracing::info!(\"{:?}\", resp);\n    after_login(&client).await;\n    {\n        tracing::info!(\"{:?}\", client.get_friend_list().await);\n        tracing::info!(\"{:?}\", client.get_group_list().await);\n    }\n    let d = client.get_allowed_clients().await;\n    tracing::info!(\"{:?}\", d);\n\n    handle.await.unwrap();\n}\n"
  },
  {
    "path": "ricq/Cargo.toml",
    "content": "[package]\nname = \"ricq\"\nversion = \"0.1.20\"\nedition = \"2021\"\ndescription = \"Android IM protocol\"\nlicense = \"MPL-2.0\"\nhomepage = \"https://github.com/lz1998/ricq\"\nrepository = \"https://github.com/lz1998/ricq\"\nreadme = \"README.md\"\nkeywords = [\"qq\", \"protocol\", \"android\", \"mirai\"]\n\n[features]\ndefault = []\nimage-detail = [\"image\"]\n\n[dependencies]\nricq-core = { path = \"../ricq-core\" }\nasync-trait.workspace = true\nbytes.workspace = true\ncached = { workspace = true, default-features = false }\nderivative.workspace = true\nflate2 = { workspace = true, features = [\"rust_backend\"], default-features = false }\nfutures-util = { workspace = true, features = [\"sink\"] }\nimage = { workspace = true, optional = true }\njcers.workspace = true\nmd5.workspace = true\nprost = { workspace = true, features = [\"std\"], default-features = false }\nrand.workspace = true\nserde = { workspace = true, features = [\"derive\"] }\ntokio = { workspace = true, features = [\"rt\", \"macros\", \"net\", \"time\"] }\ntokio-util = { workspace = true, features = [\"codec\"] }\ntracing.workspace = true\nreqwest = { workspace = true, features = [\"json\"] }\nasync-recursion = \"1.0\""
  },
  {
    "path": "ricq/README.md",
    "content": "# RICQ\n\n![](https://socialify.git.ci/lz1998/ricq/image?forks=1&issues=1&language=1&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Dark)\n\nQQ Android 协议的 Rust 实现，移植于 [OICQ](https://github.com/takayama-lily/oicq)。\n\n- [ricq](https://crates.io/crates/ricq)：提供异步 API\n- [ricq-core](https://crates.io/crates/ricq-core)：不带 IO 的数据包构造器、解析器（通常用于 FFI）\n- [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)，只需要开发登录后的逻辑。\n\n## 如何使用\n\n本项目是协议 Lib，如果需要直接使用，可以参考 [examples](https://github.com/lz1998/ricq/tree/master/examples) 中的例子进行开发。\n\n可以配合前端界面 [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 查看调用方式。\n\n普通开发者推荐使用 SDK、框架进行开发：\n\n| 框架 / SDK | 语言 | 说明 |\n| ---- | ---- | ---- |\n| [rust_proc_qq](https://github.com/niuhuan/rust_proc_qq) | Rust | 模仿rocket |\n| [Walle-Q](https://github.com/abrahum/walle-q) | - | onebot协议 |\n| [pbrq](https://github.com/ProtobufBot/pbrq) | - | websocket+protobuf协议（附带[Web-UI](https://github.com/ProtobufBot/pbrq-react-ui)） |\n| [atri_qq](https://github.com/LaoLittle/atri_qq) | - | 加载原生动态库插件，高性能低占用 |\n| [awr](https://github.com/Wybxc/awr) | Python | 基于 ricq 包装，供 Python 使用的 QQ 无头客户端。 |\n\n> 本项目是一个年轻的项目，请使用 Nightly 工具链构建本项目哦（正经人谁用 Stable 啊）\n\n## 相关项目\n\n| 项目 | 描述 |\n| ---- | ---- |\n| [lomirus/gtk-qq](https://github.com/lomirus/gtk-qq) | Unofficial Linux QQ client, based on GTK4 and libadwaita, developed with Rust and Relm4. |\n| [a1967629423/esp32c3-rs-qq](https://github.com/a1967629423/esp32c3-rs-qq) | 在单片机上运行QQ |\n| [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 |\n\n## 已完成功能 / 开发计划\n\n### 登录\n\n- [x] 账号密码登录\n- [x] 二维码登录\n- [x] 验证码提交\n- [x] 设备锁验证\n- [x] 错误信息解析\n\n### 消息类型\n\n- [x] 文本\n- [x] 表情\n- [x] At\n- [x] 回复\n- [x] 匿名\n- [x] 骰子\n- [x] 石头剪刀布\n- [x] 图片\n- [x] 语音\n- [x] 长消息（仅支持群聊发送）\n- [x] 合并转发（仅支持群聊发送）\n- [x] 链接分享\n- [ ] 小程序（暂只支持 RAW）\n- [ ] 短视频\n- [ ] 群文件（上传与接收信息）\n\n### 事件\n\n- [x] 群消息\n- [x] 好友消息\n- [x] 新好友请求\n- [x] 收到其他用户进群请求\n- [x] 新好友\n- [x] 群禁言\n- [x] 好友消息撤回\n- [x] 群消息撤回\n- [x] 收到邀请进群请求\n- [x] 群名称变更\n- [x] 好友删除\n- [x] 群成员权限变更\n- [x] 新成员进群 / 退群\n- [x] 登录号加群\n- [x] 临时会话消息\n- [x] 群解散\n- [x] 登录号退群（包含踢出）\n- [x] 客户端离线\n- [ ] 群提示（戳一戳 / 运气王等）\n\n### 主动操作\n\n> 为防止滥用，将不支持主动邀请新成员进群\n\n- [x] 修改昵称\n- [x] 发送群消息\n- [x] 获取群列表\n- [x] 获取群成员列表\n- [x] 获取好友列表 / 分组\n- [x] 获取好友个性签名\n- [x] 添加 / 删除 / 重命名好友分组\n- [x] 群成员禁言 / 解除禁言\n- [x] 踢出群成员\n- [x] 戳一戳群友\n- [x] 戳一戳好友\n- [x] 设置群管理员\n- [x] 设置群公告\n- [x] 设置群名称\n- [x] 全员禁言\n- [x] 获取群@全体剩余次数\n- [x] 翻译\n- [x] 修改群成员头衔\n- [x] 设置群精华消息\n- [x] 发送好友消息\n- [x] 发送临时会话消息\n- [x] 修改群成员 Card\n- [x] 撤回群消息\n- [x] 撤回好友消息\n- [x] 处理被邀请加群请求\n- [x] 处理加群请求\n- [x] 处理好友请求\n- [x] 删除好友\n- [x] 获取陌生人信息\n- [x] 设置在线状态\n- [x] 修改个人资料\n- [x] 修改个性签名\n- [x] 获取群文件下载链接\n- [ ] 获取群荣誉（龙王 / 群聊火焰等）\n- [ ] ~~群成员邀请~~\n\n### 敏感操作\n\n> 由于 [QQ 钱包支付用户服务协议](https://www.tenpay.com/v2/html5/basic/public/agreement/protocol_mqq_pay.shtml), 将不提供一切有关 QQ 钱包的功能。\n\n> 4.13 您不得利用本服务实施下列任一的行为：\n> \\\n> （9） **侵害 QQ 钱包支付服务系統；**\n\n- [ ] ~~QQ 钱包协议（收款 / 付款等）~~\n"
  },
  {
    "path": "ricq/src/client/api/friend.rs",
    "content": "use std::time::Duration;\n\nuse bytes::BufMut;\n\nuse ricq_core::command::long_conn::OffPicUpResp;\nuse ricq_core::command::oidb_svc::{LinkShare, MusicShare, MusicVersion, ShareTarget};\nuse ricq_core::command::{friendlist::*, profile_service::*};\nuse ricq_core::hex::encode_hex;\nuse ricq_core::highway::BdhInput;\nuse ricq_core::msg::elem::FriendImage;\nuse ricq_core::msg::MessageChain;\nuse ricq_core::pb;\nuse ricq_core::pb::msg::routing_head::RoutingHead;\nuse ricq_core::structs::FriendAudio;\nuse ricq_core::structs::MessageReceipt;\n\nuse crate::structs::ImageInfo;\nuse crate::{RQError, RQResult};\n\nimpl super::super::Client {\n    /// 获取好友请求\n    pub async fn get_friend_system_messages(&self) -> RQResult<FriendSystemMessages> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_system_msg_new_friend_packet();\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_system_msg_friend_packet(resp.body)\n    }\n\n    /// 处理好友申请\n    pub async fn solve_friend_system_message(\n        &self,\n        msg_seq: i64,\n        req_uin: i64,\n        accept: bool,\n    ) -> RQResult<()> {\n        let pkt = self\n            .engine\n            .read()\n            .await\n            .build_system_msg_friend_action_packet(msg_seq, req_uin, accept);\n        self.send_and_wait(pkt).await?;\n        Ok(())\n    }\n\n    /// 获取好友列表\n    /// 第一个参数offset，从0开始；第二个参数count，150，另外两个都是0\n    pub async fn _get_friend_list(\n        &self,\n        friend_start_index: i16,\n        friend_list_count: i16,\n        group_start_index: i16,\n        group_list_count: i16,\n    ) -> RQResult<FriendListResponse> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_friend_group_list_request_packet(\n                friend_start_index,\n                friend_list_count,\n                group_start_index,\n                group_list_count,\n            );\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_friend_group_list_response(resp.body)\n    }\n\n    /// 删除好友\n    /// ## Args\n    /// - `del_uin` 为要删除的好友QQid\n    ///\n    /// ## Return\n    /// - 如果删除好友成功 返回 Ok(())\n    /// - 如果删除好友失败 返回 Err(RQError::Other)\n    /// - 其他异常 返回 Err(..)\n    pub async fn delete_friend(&self, del_uin: i64) -> RQResult<()> {\n        let req = self.engine.read().await.build_delete_friend_packet(del_uin);\n\n        let resp = self.send_and_wait(req).await?;\n\n        let resp = self.engine.read().await.decode_remove_friend(resp.body)?;\n        if resp.error_code != 0 {\n            Err(RQError::Other(format!(\n                \"Delete Friend Failure : code = {}\",\n                resp.error_code\n            )))\n        } else {\n            Ok(())\n        }\n    }\n\n    /// 刷新好友列表\n    pub async fn get_friend_list(&self) -> RQResult<FriendListResponse> {\n        let mut output = FriendListResponse::default();\n        loop {\n            let resp = self\n                ._get_friend_list(output.friends.len() as i16, 150, 0, 0)\n                .await?;\n            output.friend_groups.extend(resp.friend_groups);\n            output.friends.extend(resp.friends);\n            output.total_count = resp.total_count;\n            if output.friends.len() as i16 >= resp.total_count {\n                break;\n            }\n        }\n        Ok(output)\n    }\n\n    /// 好友列表-添加好友分组\n    pub async fn friend_list_add_group(&self, sort_id: u8, group_name: String) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_friend_list_add_group_req_packet(sort_id, &group_name);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 好友列表-重命名好友分组\n    pub async fn friend_list_rename_group(&self, group_id: u8, group_name: String) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_friend_list_rename_group_req_packet(group_id, &group_name);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 好友列表-删除好友分组\n    pub async fn friend_list_del_group(&self, group_id: u8) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_friend_list_del_group_req_packet(group_id);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 好友戳一戳\n    pub async fn friend_poke(&self, target: i64) -> RQResult<()> {\n        let req = self.engine.read().await.build_friend_poke_packet(target);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 发送好友消息\n    pub async fn send_friend_message(\n        &self,\n        target: i64,\n        message_chain: MessageChain,\n    ) -> RQResult<MessageReceipt> {\n        self._send_friend_message(target, message_chain, None).await\n    }\n\n    /// 发送好友语音\n    pub async fn send_friend_audio(\n        &self,\n        target: i64,\n        audio: FriendAudio,\n    ) -> RQResult<MessageReceipt> {\n        self._send_friend_message(target, MessageChain::default(), Some(audio.0))\n            .await\n    }\n\n    async fn _send_friend_message(\n        &self,\n        target: i64,\n        message_chain: MessageChain,\n        ptt: Option<pb::msg::Ptt>,\n    ) -> RQResult<MessageReceipt> {\n        self.send_message(\n            RoutingHead::C2c(pb::msg::C2c {\n                to_uin: Some(target),\n            }),\n            message_chain,\n            ptt,\n        )\n        .await\n    }\n\n    pub async fn upload_friend_image(&self, target: i64, data: &[u8]) -> RQResult<FriendImage> {\n        let image_info = ImageInfo::try_new(data)?;\n        let image_store = self.get_off_pic_store(target, &image_info).await?;\n\n        let friend_image = match image_store {\n            OffPicUpResp::Exist { res_id, uuid } => image_info.into_friend_image(res_id, uuid),\n            OffPicUpResp::UploadRequired {\n                res_id,\n                uuid,\n                upload_key,\n                mut upload_addrs,\n            } => {\n                let addr = match self.highway_addrs.read().await.first() {\n                    Some(addr) => *addr,\n                    None => upload_addrs\n                        .pop()\n                        .ok_or(RQError::EmptyField(\"upload_addrs\"))?,\n                };\n                self.highway_upload_bdh(\n                    addr.into(),\n                    BdhInput {\n                        command_id: 1,\n                        ticket: upload_key,\n                        ext: vec![],\n                        encrypt: false,\n                        chunk_size: 256 * 1024,\n                        send_echo: true,\n                    },\n                    data,\n                )\n                .await?;\n                image_info.into_friend_image(res_id, uuid)\n            }\n        };\n        Ok(friend_image)\n    }\n\n    pub async fn get_off_pic_store(\n        &self,\n        target: i64,\n        image_info: &ImageInfo,\n    ) -> RQResult<OffPicUpResp> {\n        let req = self.engine.read().await.build_off_pic_up_packet(\n            target,\n            image_info.filename.clone(),\n            image_info.md5.clone(),\n            image_info.size as u64,\n            image_info.width,\n            image_info.height,\n            image_info.image_type as u32,\n        );\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_off_pic_up_response(resp.body)\n    }\n\n    /// 分享好友音乐\n    pub async fn send_friend_music_share(\n        &self,\n        uin: i64,\n        music_share: MusicShare,\n        music_version: MusicVersion,\n    ) -> RQResult<()> {\n        let req = self.engine.read().await.build_share_music_request_packet(\n            ShareTarget::Friend(uin),\n            music_share,\n            music_version,\n        );\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 分享链接\n    pub async fn send_friend_link_share(&self, uin: i64, link_share: LinkShare) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_share_link_request_packet(ShareTarget::Friend(uin), link_share);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    // 撤回好友消息\n    pub async fn recall_friend_message(\n        &self,\n        uin: i64,\n        msg_time: i64,\n        seqs: Vec<i32>,\n        rands: Vec<i32>,\n    ) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_friend_recall_packet(uin, msg_time, seqs, rands);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    pub async fn upload_friend_audio(\n        &self,\n        target: i64,\n        data: &[u8],\n        audio_duration: Duration,\n    ) -> RQResult<FriendAudio> {\n        let md5 = md5::compute(data).to_vec();\n        let size = data.len();\n        let ext = self.engine.read().await.build_friend_try_up_ptt_req(\n            target,\n            md5.clone(),\n            size as i64,\n            size as i32,\n        );\n        let addr = self\n            .highway_addrs\n            .read()\n            .await\n            .first()\n            .copied()\n            .ok_or(RQError::EmptyField(\"highway_addrs\"))?;\n        let ticket = self\n            .highway_session\n            .read()\n            .await\n            .sig_session\n            .clone()\n            .to_vec();\n        let resp = self\n            .highway_upload_bdh(\n                addr.into(),\n                BdhInput {\n                    command_id: 26,\n                    ticket,\n                    ext: ext.to_vec(),\n                    encrypt: false,\n                    chunk_size: 256 * 1024,\n                    send_echo: true,\n                },\n                data,\n            )\n            .await?;\n        let uuid = self\n            .engine\n            .read()\n            .await\n            .decode_friend_try_up_ptt_resp(resp)?;\n        Ok(FriendAudio(pb::msg::Ptt {\n            file_type: Some(4),\n            src_uin: Some(self.uin().await),\n            file_uuid: Some(uuid),\n            file_name: Some(format!(\"{}.amr\", encode_hex(&md5))),\n            file_md5: Some(md5),\n            file_size: Some(size as i32),\n            reserve: Some({\n                let mut w = Vec::new();\n                w.put_u8(3); // tlv count\n                {\n                    w.put_u8(8);\n                    w.put_u16(4);\n                    w.put_u32(1); // codec\n                }\n                {\n                    w.put_u8(9);\n                    w.put_u16(4);\n                    w.put_u32(audio_duration.as_secs() as u32); // voiceLength\n                }\n                {\n                    w.put_u8(10);\n                    w.put_u16(6);\n                    w.put_slice(&[0x08, 0x00, 0x28, 0x00, 0x38, 0x00]); // change_voice+redpack_type+autototext_voice\n                }\n                w\n            }),\n            bool_valid: Some(true),\n            ..Default::default()\n        }))\n    }\n\n    pub async fn get_friend_audio_url(\n        &self,\n        sender_uin: i64,\n        audio: FriendAudio,\n    ) -> RQResult<String> {\n        let req = self.engine.read().await.build_c2c_ptt_down_req(\n            sender_uin,\n            audio.0.file_uuid.ok_or(RQError::EmptyField(\"file_uuid\"))?,\n        );\n        let resp = self.send_and_wait(req).await?;\n        self.engine.read().await.decode_c2c_ptt_down(resp.body)\n    }\n\n    /// 标记私聊消息已读 TODO 待测试\n    pub async fn mark_friend_message_readed(&self, uin: i64, time: i64) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_friend_msg_readed_packet(uin, time);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 获取好友个性签名\n    pub async fn get_friend_rich_sig(&self, user_ids: Vec<i64>) -> RQResult<Vec<RichSigInfo>> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_get_rich_sig_request_packet(user_ids);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_get_rich_sig_response_packet(resp.body)\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/api/group.rs",
    "content": "use std::collections::HashMap;\nuse std::time::{Duration, UNIX_EPOCH};\n\nuse bytes::Bytes;\nuse cached::Cached;\nuse prost::Message;\n\nuse ricq_core::command::common::PbToBytes;\nuse ricq_core::command::img_store::GroupImageStoreResp;\nuse ricq_core::command::multi_msg::gen_forward_preview;\nuse ricq_core::command::{friendlist::*, oidb_svc::*, profile_service::*};\nuse ricq_core::common::group_code2uin;\nuse ricq_core::hex::encode_hex;\nuse ricq_core::highway::BdhInput;\nuse ricq_core::msg::elem::{Anonymous, GroupImage, RichMsg, VideoFile};\nuse ricq_core::msg::MessageChain;\nuse ricq_core::pb;\nuse ricq_core::pb::short_video::ShortVideoUploadRsp;\nuse ricq_core::structs::{ForwardMessage, GroupFileCount, GroupFileList, MessageNode};\nuse ricq_core::structs::{GroupAudio, GroupMemberPermission};\nuse ricq_core::structs::{GroupInfo, GroupMemberInfo, MessageReceipt};\n\nuse crate::structs::ImageInfo;\nuse crate::{RQError, RQResult};\n\nimpl super::super::Client {\n    /// 获取进群申请信息\n    async fn get_group_system_messages(&self, suspicious: bool) -> RQResult<GroupSystemMessages> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_system_msg_new_group_packet(suspicious);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_system_msg_group_packet(resp.body)\n    }\n\n    /// 获取所有进群请求\n    pub async fn get_all_group_system_messages(&self) -> RQResult<GroupSystemMessages> {\n        let mut resp = self.get_group_system_messages(false).await?;\n        let risk_resp = self.get_group_system_messages(true).await?;\n        resp.join_group_requests\n            .extend(risk_resp.join_group_requests);\n        resp.self_invited.extend(risk_resp.self_invited);\n        Ok(resp)\n    }\n\n    /// 处理加群申请\n    #[allow(clippy::too_many_arguments)]\n    pub async fn solve_group_system_message(\n        &self,\n        msg_seq: i64,\n        req_uin: i64,\n        group_code: i64,\n        suspicious: bool,\n        is_invite: bool,\n        accept: bool,\n        block: bool,\n        reason: String,\n    ) -> RQResult<()> {\n        let pkt = self\n            .engine\n            .read()\n            .await\n            .build_system_msg_group_action_packet(\n                msg_seq,\n                req_uin,\n                group_code,\n                if suspicious { 2 } else { 1 },\n                is_invite,\n                accept,\n                block,\n                reason,\n            );\n        self.send_and_wait(pkt).await?;\n\n        Ok(())\n    }\n\n    /// 获取群列表\n    /// 第一个参数offset，从0开始；第二个参数count，150，另外两个都是0\n    pub async fn _get_group_list(&self, vec_cookie: &[u8]) -> RQResult<GroupListResponse> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_list_request_packet(vec_cookie);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_list_response(resp.body)\n    }\n\n    /// 发送群消息\n    pub async fn send_group_message(\n        &self,\n        group_code: i64,\n        message_chain: MessageChain,\n    ) -> RQResult<MessageReceipt> {\n        self._send_group_message(group_code, message_chain.into(), None)\n            .await\n    }\n\n    /// 发送群语音\n    pub async fn send_group_audio(\n        &self,\n        group_code: i64,\n        group_audio: GroupAudio,\n    ) -> RQResult<MessageReceipt> {\n        self._send_group_message(group_code, vec![], Some(group_audio.0))\n            .await\n    }\n\n    async fn _send_group_message(\n        &self,\n        group_code: i64,\n        elems: Vec<pb::msg::Elem>,\n        ptt: Option<pb::msg::Ptt>,\n    ) -> RQResult<MessageReceipt> {\n        let ran = (rand::random::<u32>() >> 1) as i32;\n        let (tx, rx) = tokio::sync::oneshot::channel();\n        {\n            self.receipt_waiters.lock().await.cache_set(ran, tx);\n        }\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_sending_packet(group_code, elems, ptt, ran, 1, 0, 0, false);\n        let _ = self.send_and_wait(req).await?;\n        let mut receipt = MessageReceipt {\n            seqs: vec![0],\n            rands: vec![ran],\n            time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i64,\n        };\n        match tokio::time::timeout(Duration::from_secs(5), rx).await {\n            Ok(Ok(seq)) => {\n                if let Some(s) = receipt.seqs.first_mut() {\n                    *s = seq;\n                }\n            }\n            Ok(Err(_)) => {} //todo\n            Err(_) => {}\n        }\n        Ok(receipt)\n    }\n\n    /// 发送群成员临时消息\n    pub async fn send_group_temp_message(\n        &self,\n        group_code: i64,\n        user_uin: i64,\n        message_chain: MessageChain,\n    ) -> RQResult<MessageReceipt> {\n        self.send_message(\n            pb::msg::routing_head::RoutingHead::GrpTmp(pb::msg::GrpTmp {\n                group_uin: Some(group_code2uin(group_code)),\n                to_uin: Some(user_uin),\n            }),\n            message_chain,\n            None,\n        )\n        .await\n    }\n\n    /// 获取群成员信息\n    pub async fn get_group_member_info(\n        &self,\n        group_code: i64,\n        uin: i64,\n    ) -> RQResult<GroupMemberInfo> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_member_info_request_packet(group_code, uin);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_member_info_response(resp.body)\n    }\n\n    /// 批量获取群信息\n    pub async fn get_group_infos(&self, group_codes: Vec<i64>) -> RQResult<Vec<GroupInfo>> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_info_request_packet(group_codes);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_info_response(resp.body)\n    }\n\n    /// 获取群信息\n    pub async fn get_group_info(&self, group_code: i64) -> RQResult<Option<GroupInfo>> {\n        Ok(self.get_group_infos(vec![group_code]).await?.pop())\n    }\n\n    /// 刷新群列表\n    pub async fn get_group_list(&self) -> RQResult<Vec<GroupInfo>> {\n        // 获取群列表\n        let mut vec_cookie = Bytes::new();\n        let mut groups = Vec::new();\n        loop {\n            let resp = self._get_group_list(&vec_cookie).await?;\n            vec_cookie = resp.vec_cookie;\n            for g in resp.groups {\n                groups.push(g);\n            }\n            if vec_cookie.is_empty() {\n                break;\n            }\n        }\n        Ok(groups)\n    }\n\n    /// 获取群成员列表 (low level api)\n    async fn _get_group_member_list(\n        &self,\n        group_code: i64,\n        next_uin: i64,\n        group_owner_uin: i64,\n    ) -> RQResult<GroupMemberListResponse> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_member_list_request_packet(group_code, next_uin);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_member_list_response(resp.body, group_owner_uin)\n    }\n\n    /// 获取群成员列表\n    pub async fn get_group_member_list(\n        &self,\n        group_code: i64,\n        group_owner_uin: i64,\n    ) -> RQResult<Vec<GroupMemberInfo>> {\n        let mut next_uin = 0;\n        let mut list = Vec::new();\n        loop {\n            let mut resp = self\n                ._get_group_member_list(group_code, next_uin, group_owner_uin)\n                .await?;\n            if resp.list.is_empty() {\n                return Err(RQError::EmptyField(\"GroupMemberListResponse.list\"));\n            }\n            for m in resp.list.iter_mut() {\n                m.group_code = group_code;\n            }\n            list.append(&mut resp.list);\n            next_uin = resp.next_uin;\n            if next_uin == 0 {\n                break;\n            }\n        }\n        Ok(list)\n    }\n\n    /// 标记群消息已读\n    pub async fn mark_group_message_readed(&self, group_code: i64, seq: i32) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_msg_readed_packet(group_code, seq);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 群禁言 (解除禁言 duration=0)\n    pub async fn group_mute(\n        &self,\n        group_code: i64,\n        member_uin: i64,\n        duration: std::time::Duration,\n    ) -> RQResult<()> {\n        let req = self.engine.read().await.build_group_mute_packet(\n            group_code,\n            member_uin,\n            duration.as_secs() as u32,\n        );\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 全员禁言\n    pub async fn group_mute_all(&self, group_code: i64, mute: bool) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_mute_all_packet(group_code, mute);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 修改群名称\n    pub async fn update_group_name(&self, group_code: i64, name: String) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_name_update_packet(group_code, name);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 设置群公告\n    pub async fn update_group_memo(&self, group_code: i64, memo: String) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_memo_update_packet(group_code, memo);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 设置群管理员\n    ///\n    /// flag: true 设置管理员 false 取消管理员\n    pub async fn group_set_admin(&self, group_code: i64, member: i64, flag: bool) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_admin_set_packet(group_code, member, flag);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 群戳一戳\n    pub async fn group_poke(&self, group_code: i64, target: i64) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_poke_packet(group_code, target);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 群踢人\n    pub async fn group_kick(\n        &self,\n        group_code: i64,\n        member_uins: Vec<i64>,\n        kick_msg: &str,\n        block: bool,\n    ) -> RQResult<()> {\n        let req = self.engine.read().await.build_group_kick_packet(\n            group_code,\n            member_uins,\n            kick_msg,\n            block,\n        );\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    pub async fn group_invite(&self, group_code: i64, uin: i64) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_invite_packet(group_code, uin);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    pub async fn group_quit(&self, group_code: i64) -> RQResult<()> {\n        let req = self.engine.read().await.build_quit_group_packet(group_code);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 获取群 @全体成员 剩余次数\n    pub async fn group_at_all_remain(&self, group_code: i64) -> RQResult<GroupAtAllRemainInfo> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_at_all_remain_request_packet(group_code);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_at_all_remain_response(resp.body)\n    }\n\n    /// 设置群头衔\n    pub async fn group_edit_special_title(\n        &self,\n        group_code: i64,\n        member_uin: i64,\n        new_title: String,\n    ) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_edit_special_title_packet(group_code, member_uin, new_title);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 获取自己的匿名信息（用于发送群消息）\n    pub async fn get_anony_info(&self, group_code: i64) -> RQResult<Option<Anonymous>> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_get_anony_info_request(group_code);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_get_anony_info_response(resp.body)\n    }\n\n    /// 分享群音乐\n    pub async fn send_group_music_share(\n        &self,\n        group_code: i64,\n        music_share: MusicShare,\n        music_version: MusicVersion,\n    ) -> RQResult<()> {\n        let req = self.engine.read().await.build_share_music_request_packet(\n            ShareTarget::Group(group_code),\n            music_share,\n            music_version,\n        );\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 分享链接\n    pub async fn send_group_link_share(\n        &self,\n        group_code: i64,\n        link_share: LinkShare,\n    ) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_share_link_request_packet(ShareTarget::Group(group_code), link_share);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 修改群名片\n    pub async fn edit_group_member_card(\n        &self,\n        group_code: i64,\n        member_uin: i64,\n        card: String,\n    ) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_edit_group_tag_packet(group_code, member_uin, card);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 撤回群消息\n    pub async fn recall_group_message(\n        &self,\n        group_code: i64,\n        seqs: Vec<i32>,\n        rands: Vec<i32>,\n    ) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_recall_packet(group_code, seqs, rands);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    // 用 highway 上传群图片之前调用，获取 upload_key\n    pub async fn get_group_image_store(\n        &self,\n        group_code: i64,\n        image_info: &ImageInfo,\n    ) -> RQResult<GroupImageStoreResp> {\n        let req = self.engine.read().await.build_group_image_store_packet(\n            group_code,\n            image_info.filename.clone(),\n            image_info.md5.clone(),\n            image_info.size as u64,\n            image_info.width,\n            image_info.height,\n            image_info.image_type as u32,\n        );\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_image_store_response(resp.body)\n    }\n\n    /// 上传群图片\n    pub async fn upload_group_image(&self, group_code: i64, data: &[u8]) -> RQResult<GroupImage> {\n        let image_info = ImageInfo::try_new(data)?;\n\n        let image_store = self.get_group_image_store(group_code, &image_info).await?;\n        let signature = self.highway_session.read().await.session_key.to_vec();\n        let group_image = match image_store {\n            GroupImageStoreResp::Exist { file_id, addrs } => image_info.into_group_image(\n                file_id,\n                addrs.first().copied().unwrap_or_default(),\n                signature,\n            ),\n            GroupImageStoreResp::NotExist {\n                file_id,\n                upload_key,\n                mut upload_addrs,\n            } => {\n                let addr = match self.highway_addrs.read().await.first() {\n                    Some(addr) => *addr,\n                    None => upload_addrs\n                        .pop()\n                        .ok_or(RQError::EmptyField(\"upload_addrs\"))?,\n                };\n                self.highway_upload_bdh(\n                    addr.into(),\n                    BdhInput {\n                        command_id: 2,\n                        ticket: upload_key,\n                        ext: vec![],\n                        encrypt: false,\n                        chunk_size: 256 * 1024,\n                        send_echo: true,\n                    },\n                    data,\n                )\n                .await?;\n                image_info.into_group_image(file_id, addr, signature)\n            }\n        };\n        Ok(group_image)\n    }\n\n    /// 上传群音频 codec: 0-amr, 1-silk\n    pub async fn upload_group_audio(\n        &self,\n        group_code: i64,\n        data: &[u8],\n        codec: u32,\n    ) -> RQResult<GroupAudio> {\n        let md5 = md5::compute(data).to_vec();\n        let size = data.len();\n        let ext = self.engine.read().await.build_group_try_up_ptt_req(\n            group_code,\n            md5.clone(),\n            size as u64,\n            codec,\n            size as u32,\n        );\n        let addr = self\n            .highway_addrs\n            .read()\n            .await\n            .first()\n            .copied()\n            .ok_or(RQError::EmptyField(\"highway_addrs\"))?;\n        let ticket = self\n            .highway_session\n            .read()\n            .await\n            .sig_session\n            .clone()\n            .to_vec();\n        let resp = self\n            .highway_upload_bdh(\n                addr.into(),\n                BdhInput {\n                    command_id: 29,\n                    ticket,\n                    ext: ext.to_vec(),\n                    encrypt: false,\n                    chunk_size: 256 * 1024,\n                    send_echo: true,\n                },\n                data,\n            )\n            .await?;\n        let file_key = self\n            .engine\n            .read()\n            .await\n            .decode_group_try_up_ptt_resp(resp)?;\n        Ok(GroupAudio(pb::msg::Ptt {\n            file_type: Some(4),\n            src_uin: Some(self.uin().await),\n            file_name: Some(format!(\"{}.amr\", encode_hex(&md5))),\n            file_md5: Some(md5),\n            file_size: Some(size as i32),\n            bool_valid: Some(true),\n            pb_reserve: Some(vec![8, 0, 40, 0, 56, 0]),\n            group_file_key: Some(file_key),\n            ..Default::default()\n        }))\n    }\n\n    pub async fn get_group_audio_url(\n        &self,\n        group_code: i64,\n        audio: GroupAudio,\n    ) -> RQResult<String> {\n        let req = self.engine.read().await.build_group_ptt_down_req(\n            group_code,\n            audio.0.file_md5.ok_or(RQError::EmptyField(\"file_md5\"))?,\n        );\n        let resp = self.send_and_wait(req).await?;\n        self.engine.read().await.decode_group_ptt_down(resp.body)\n    }\n\n    // 用 highway 上传群视频之前调用，获取 upload_key\n    pub async fn get_group_short_video_store(\n        &self,\n        short_video_upload_req: pb::short_video::ShortVideoUploadReq,\n    ) -> RQResult<ShortVideoUploadRsp> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_video_store_packet(short_video_upload_req);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_video_store_response(resp.body)\n    }\n\n    /// 上传群短视频 参数：群号，视频数据，封面数据\n    /// TODO 未来可能会改成输入 std::io::Read\n    pub async fn upload_group_short_video(\n        &self,\n        group_code: i64,\n        video_data: &[u8],\n        thumb_data: &[u8],\n    ) -> RQResult<VideoFile> {\n        let video_md5 = md5::compute(video_data).to_vec();\n        let thumb_md5 = md5::compute(thumb_data).to_vec();\n        let video_size = video_data.len();\n        let thumb_size = thumb_data.len();\n        let short_video_up_req = self.engine.read().await.build_short_video_up_req(\n            group_code,\n            video_md5.clone(),\n            thumb_md5.clone(),\n            video_size as i64,\n            thumb_size as i64,\n        );\n        let ext = short_video_up_req.to_bytes().to_vec();\n\n        let video_store = self.get_group_short_video_store(short_video_up_req).await?;\n\n        if video_store.file_exists == 1 {\n            return Ok(VideoFile {\n                name: format!(\"{}.mp4\", encode_hex(&video_md5)),\n                uuid: video_store.file_id,\n                size: video_size as i32,\n                thumb_size: thumb_size as i32,\n                md5: video_md5,\n                thumb_md5,\n            });\n        }\n\n        let addr = self\n            .highway_addrs\n            .read()\n            .await\n            .first()\n            .copied()\n            .ok_or(RQError::EmptyField(\"highway_addrs\"))?;\n\n        if self.highway_session.read().await.session_key.is_empty() {\n            return Err(RQError::EmptyField(\"highway_session_key\"));\n        }\n        let ticket = self.highway_session.read().await.sig_session.to_vec();\n        let mut data = Vec::with_capacity(thumb_size + video_size);\n        data.copy_from_slice(thumb_data);\n        data[thumb_size..].copy_from_slice(video_data);\n\n        let rsp = self\n            .highway_upload_bdh(\n                addr.into(),\n                BdhInput {\n                    command_id: 25,\n                    ticket,\n                    ext,\n                    encrypt: true,\n                    chunk_size: 256 * 1024,\n                    send_echo: true,\n                },\n                &data,\n            )\n            .await?;\n        let rsp = pb::short_video::ShortVideoUploadRsp::decode(&*rsp)\n            .map_err(|_| RQError::Decode(\"ShortVideoUploadRsp\".into()))?;\n        Ok(VideoFile {\n            name: format!(\"{}.mp4\", encode_hex(&video_md5)),\n            uuid: rsp.file_id,\n            size: video_size as i32,\n            thumb_size: thumb_size as i32,\n            md5: video_md5,\n            thumb_md5,\n        })\n    }\n\n    /// 设置群精华消息\n    pub async fn operate_group_essence(\n        &self,\n        group_code: i64,\n        msg_seq: i32,\n        msg_rand: i32,\n        flag: bool,\n    ) -> RQResult<pb::oidb::EacRspBody> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_essence_msg_operate_packet(group_code, msg_seq, msg_rand, flag);\n        let resp = self.send_and_wait(req).await?;\n        let decode = self\n            .engine\n            .read()\n            .await\n            .decode_essence_msg_response(resp.body)?;\n        Ok(decode)\n    }\n\n    /// 发送群消息\n    /// 仅在多张图片时需要，发送文字不需要\n    pub async fn send_group_long_message(\n        &self,\n        group_code: i64,\n        message_chain: MessageChain,\n    ) -> RQResult<MessageReceipt> {\n        let brief = \"[图片][图片][图片]\"; // TODO brief\n        let res_id = self\n            .upload_msgs(\n                group_code,\n                vec![MessageNode {\n                    sender_id: self.uin().await,\n                    time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i32,\n                    sender_name: self.account_info.read().await.nickname.clone(),\n                    elements: message_chain,\n                }\n                .into()],\n                true,\n            )\n            .await?;\n        let template=format!(\n            \"<?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>\",\n            brief,\n            res_id,\n            UNIX_EPOCH.elapsed().unwrap().as_millis(),\n            brief);\n        let mut chain = MessageChain::default();\n        chain.push(RichMsg {\n            service_id: 35,\n            template1: template,\n        });\n        chain.0.extend(vec![\n            pb::msg::elem::Elem::Text(pb::msg::Text {\n                str: Some(\"你的QQ暂不支持查看[转发多条消息]，请期待后续版本。\".into()),\n                ..Default::default()\n            }),\n            pb::msg::elem::Elem::GeneralFlags(pb::msg::GeneralFlags {\n                long_text_flag: Some(1),\n                long_text_resid: Some(res_id),\n                pendant_id: Some(0),\n                pb_reserve: Some(vec![0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00]), // TODO 15=73255?\n                ..Default::default()\n            }),\n        ]);\n        self._send_group_message(group_code, chain.into(), None)\n            .await\n    }\n\n    /// 发送转发消息\n    pub async fn send_group_forward_message(\n        &self,\n        group_code: i64,\n        msgs: Vec<ForwardMessage>,\n    ) -> RQResult<MessageReceipt> {\n        let t_sum = msgs.len();\n        let preview = gen_forward_preview(&msgs);\n        let res_id = self.upload_msgs(group_code, msgs, false).await?;\n        // TODO friend template?\n        let template = format!(\n            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>\"##,\n            res_id,\n            UNIX_EPOCH.elapsed().unwrap().as_millis(), // TODO m_filename?\n            t_sum,\n            preview,\n            t_sum\n        );\n        let mut chain = MessageChain::default();\n        chain.push(RichMsg {\n            service_id: 35,\n            template1: template,\n        });\n        chain\n            .0\n            .push(pb::msg::elem::Elem::GeneralFlags(pb::msg::GeneralFlags {\n                pendant_id: Some(0),\n                pb_reserve: Some(vec![0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00]),\n                ..Default::default()\n            }));\n        self._send_group_message(group_code, chain.into(), None)\n            .await\n    }\n\n    /// 获取群主/管理员列表\n    pub async fn get_group_admin_list(\n        &self,\n        group_code: i64,\n    ) -> RQResult<HashMap<i64, GroupMemberPermission>> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_get_group_admin_list_request_packet(group_code as u64);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_get_group_admin_list_response(resp.body)\n    }\n\n    /// 群聊打卡\n    pub async fn group_sign_in(&self, group_code: i64) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_sign_in_packet(group_code);\n        self.send_and_wait(req).await?;\n        Ok(())\n    }\n    // 获取群文件列表\n    pub async fn get_group_file_list(\n        &self,\n        group_code: u64,\n        folder_id: &str,\n        start_index: u32,\n    ) -> RQResult<GroupFileList> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_file_list_request_packet(group_code, folder_id.into(), start_index);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_file_list_response(resp.body)\n    }\n\n    /// 获取群文件总数\n    pub async fn get_group_files_count(&self, group_code: u64) -> RQResult<GroupFileCount> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_file_count_request_packet(group_code);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_file_count_response(resp.body)\n    }\n    /// 获取文件下载链接\n    /// # Examples\n    /// ```\n    /// # async fn test(client: &ricq::Client) {\n    /// let group_code: i64 = 123456789;\n    /// let file_list = client.get_group_file_list(group_code.try_into().unwrap(), \"/\", 0).await.unwrap();\n    /// for item_info in file_list.items {\n    ///     let url = client\n    ///         .get_group_file_download(\n    ///             group_code,\n    ///             &item_info.file_info.file_id,\n    ///             item_info.file_info.bus_id,\n    ///             &item_info.file_info.file_name,\n    ///         )\n    ///         .await;\n    ///     println!(\"{:?}\", url);\n    /// }\n    /// # }\n    ///```\n    pub async fn get_group_file_download(\n        &self,\n        group_code: i64,\n        file_id: &str,\n        bus_id: u32,\n        file_name: &str,\n    ) -> RQResult<String> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_group_file_download_request_packet(group_code, file_id.into(), bus_id as i32);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_group_file_download_response(resp.body, file_name)\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/api/login.rs",
    "content": "use std::sync::atomic::Ordering;\n\nuse crate::jce::SvcRespRegister;\nuse crate::qsign::QSignClient;\nuse crate::{RQError, RQResult};\nuse ricq_core::command::wtlogin::*;\nuse ricq_core::hex::decode_hex;\nuse ricq_core::token::Token;\n\n/// 登录相关\nimpl super::super::Client {\n    /// 二维码登录 - 获取二维码\n    pub async fn fetch_qrcode(&self) -> RQResult<QRCodeState> {\n        let req = self.engine.read().await.build_qrcode_fetch_request_packet();\n        let resp = self.send_and_wait(req).await?;\n        let resp = self\n            .engine\n            .read()\n            .await\n            .decode_trans_emp_response(resp.body)?;\n        self.process_trans_emp_response(&resp).await;\n        Ok(resp)\n    }\n\n    /// 二维码登录 - 查询二维码状态\n    pub async fn query_qrcode_result(&self, sig: &[u8]) -> RQResult<QRCodeState> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_qrcode_result_query_request_packet(sig);\n        let resp = self.send_and_wait(req).await?;\n        let resp = self\n            .engine\n            .read()\n            .await\n            .decode_trans_emp_response(resp.body)?;\n        self.process_trans_emp_response(&resp).await;\n        Ok(resp)\n    }\n\n    /// 二维码登录 - 登录 ( 可能还需要 device_lock_login )\n    pub async fn qrcode_login(\n        &self,\n        tmp_pwd: &[u8],\n        tmp_no_pic_sig: &[u8],\n        tgt_qr: &[u8],\n    ) -> RQResult<LoginResponse> {\n        let req =\n            self.engine\n                .read()\n                .await\n                .build_qrcode_login_packet(tmp_pwd, tmp_no_pic_sig, tgt_qr);\n        let resp = self.send_and_wait(req).await?;\n        let resp = self.engine.read().await.decode_login_response(resp.body)?;\n        self.process_login_response(&resp).await;\n        Ok(resp)\n    }\n\n    pub async fn sign(&self, data: &str) -> RQResult<Vec<u8>> {\n        let uin = self.uin().await;\n        let engine = self.engine.read().await;\n        let sub_cmd = u8::from_str_radix(&data[4..], 16).unwrap();\n        let salt = QSignClient::calc_salt(\n            uin as u64,\n            &engine.transport.sig.guid,\n            &engine.transport.version.sdk_version,\n            sub_cmd as u32,\n        );\n        let resp = self\n            .qsign_client\n            .custom_energy(\n                uin,\n                data,\n                &salt,\n                &engine.transport.sig.guid,\n                &engine.transport.device.android_id,\n            )\n            .await\n            .map_err(|e| RQError::Other(e.to_string()))?;\n        if resp.code != 0 {\n            return Err(RQError::Other(format!(\"failed to energy {}\", resp.msg)));\n        }\n        decode_hex(&resp.data)\n            .map_err(|err| RQError::Other(format!(\"failed to decode hex: {}\", err)))\n    }\n\n    /// 密码登录 - 提交密码md5\n    pub async fn password_md5_login(\n        &self,\n        uin: i64,\n        password_md5: &[u8],\n    ) -> RQResult<LoginResponse> {\n        self.engine.read().await.uin.store(uin, Ordering::Relaxed);\n        let sign = self.sign(\"810_9\").await?;\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_login_packet(password_md5, &sign, true);\n        let resp = self.send_and_wait(req).await?;\n        let resp = self.engine.read().await.decode_login_response(resp.body)?;\n        self.process_login_response(&resp).await;\n        Ok(resp)\n    }\n\n    pub async fn password_login(&self, uin: i64, password: &str) -> RQResult<LoginResponse> {\n        self.password_md5_login(uin, &md5::compute(password).to_vec())\n            .await\n    }\n\n    /// 密码登录 - 请求短信验证码\n    pub async fn request_sms(&self) -> RQResult<LoginResponse> {\n        let req = self.engine.read().await.build_sms_request_packet();\n        let resp = self.send_and_wait(req).await?;\n        let resp = self.engine.read().await.decode_login_response(resp.body)?;\n        self.process_login_response(&resp).await;\n        Ok(resp)\n    }\n\n    /// 密码登录 - 提交短信验证码\n    pub async fn submit_sms_code(&self, code: &str) -> RQResult<LoginResponse> {\n        let sign = self.sign(\"810_7\").await?;\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_sms_code_submit_packet(code.trim(), &sign);\n        let resp = self.send_and_wait(req).await?;\n        let resp = self.engine.read().await.decode_login_response(resp.body)?;\n        self.process_login_response(&resp).await;\n        Ok(resp)\n    }\n\n    /// 密码登录 - 提交滑块ticket\n    pub async fn submit_ticket(&self, ticket: &str) -> RQResult<LoginResponse> {\n        let sign = self.sign(\"810_2\").await?;\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_ticket_submit_packet(ticket, &sign);\n        let resp = self.send_and_wait(req).await?;\n        let resp = self.engine.read().await.decode_login_response(resp.body)?;\n        self.process_login_response(&resp).await;\n        Ok(resp)\n    }\n\n    /// 设备锁登录 - 二维码、密码登录都需要\n    pub async fn device_lock_login(&self) -> RQResult<LoginResponse> {\n        let req = self.engine.read().await.build_device_lock_login_packet();\n        let resp = self.send_and_wait(req).await?;\n        let resp = self.engine.read().await.decode_login_response(resp.body)?;\n        self.process_login_response(&resp).await;\n        Ok(resp)\n    }\n\n    /// token 登录\n    pub async fn token_login(&self, token: Token) -> RQResult<LoginResponse> {\n        self.load_token(token).await;\n        self.request_change_sig(None).await\n    }\n\n    /// 换 token，使用后需要重新 register\n    pub async fn request_change_sig(&self, main_sig_map: Option<u32>) -> RQResult<LoginResponse> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_request_change_sig_packet(main_sig_map);\n        let resp = self.send_and_wait(req).await?;\n        let resp = self\n            .engine\n            .read()\n            .await\n            .decode_exchange_emp_response(resp.body)?;\n        self.process_login_response(&resp).await;\n        Ok(resp)\n    }\n\n    /// 注册客户端，登录后必须注册\n    pub async fn register_client(&self) -> RQResult<SvcRespRegister> {\n        let req = self.engine.read().await.build_client_register_packet();\n        let resp = self.send_and_wait(req).await?;\n        let resp = self\n            .engine\n            .read()\n            .await\n            .decode_client_register_response(resp.body)?;\n        if !resp.result.is_empty() || resp.reply_code != 0 {\n            return Err(RQError::Other(resp.result + &resp.reply_code.to_string()));\n        }\n        self.online.store(true, Ordering::SeqCst);\n        Ok(resp)\n    }\n\n    pub async fn heartbeat(&self) -> RQResult<()> {\n        let req = self.engine.read().await.build_heartbeat_packet();\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    // 系统强制下线 response\n    pub(crate) async fn send_msg_offline_rsp(&self, uin: i64, seq_no: i64) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_msf_force_offline_rsp(uin, seq_no);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    pub(crate) async fn send_sid_ticket_expired_response(&self, seq: i32) -> RQResult<()> {\n        let rsp = self\n            .engine\n            .read()\n            .await\n            .build_sid_ticket_expired_response(seq);\n        self.send(rsp).await?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/api/mod.rs",
    "content": "use std::net::SocketAddr;\nuse std::sync::atomic::Ordering;\nuse std::time::UNIX_EPOCH;\n\nuse bytes::Bytes;\nuse cached::Cached;\n\nuse ricq_core::command::message_svc::MessageSyncResponse;\nuse ricq_core::command::oidb_svc::*;\nuse ricq_core::common::{group_code2uin, RQAddr};\nuse ricq_core::highway::BdhInput;\nuse ricq_core::msg::MessageChain;\nuse ricq_core::pb;\nuse ricq_core::structs::Status;\nuse ricq_core::structs::SummaryCardInfo;\nuse ricq_core::structs::{ForwardMessage, MessageReceipt};\n\nuse crate::jce::SvcDevLoginInfo;\nuse crate::{RQError, RQResult};\n\nmod friend;\nmod group;\nmod login;\n\n/// API\nimpl super::Client {\n    /// 设置在线状态 TODO net_type\n    pub async fn update_online_status<T>(&self, status: T) -> RQResult<()>\n    where\n        T: Into<Status>,\n    {\n        let status = status.into();\n        if let Some(ref custom_status) = status.custom_status {\n            if custom_status.wording.is_empty() || custom_status.wording.chars().count() > 4 {\n                return Err(RQError::Other(\"invalid wording length\".into()));\n            }\n        }\n        let req = self.engine.read().await.build_set_online_status_packet(\n            status.online_status,\n            status.ext_online_status,\n            status.custom_status,\n        );\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 修改签名\n    pub async fn update_signature(&self, signature: String) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_update_signature_packet(signature);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 修改个人资料\n    pub async fn update_profile_detail(&self, profile: ProfileDetailUpdate) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_update_profile_detail_packet(profile);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 刷新客户端状态\n    pub async fn refresh_status(&self) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_get_offline_msg_request_packet(self.last_message_time.load(Ordering::SeqCst));\n        let _resp = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    /// 获取通过安全验证的设备\n    pub async fn get_allowed_clients(&self) -> RQResult<Vec<SvcDevLoginInfo>> {\n        let req = self.engine.read().await.build_device_list_request_packet();\n        let resp = self.send_and_wait(req).await?;\n        self.engine.read().await.decode_dev_list_response(resp.body)\n    }\n\n    /// 文本翻译\n    pub async fn translate(\n        &self,\n        src_language: String,\n        dst_language: String,\n        src_text_list: Vec<String>,\n    ) -> RQResult<Vec<String>> {\n        let list_len = src_text_list.len();\n        let req = self.engine.read().await.build_translate_request_packet(\n            src_language,\n            dst_language,\n            src_text_list,\n        );\n        let resp = self.send_and_wait(req).await?;\n        let translations = self\n            .engine\n            .read()\n            .await\n            .decode_translate_response(resp.body)?;\n        if translations.len() != list_len {\n            return Err(RQError::Other(\"translate length error\".into()));\n        }\n        Ok(translations)\n    }\n\n    // source 0-自己 1-好友 2-群成员\n    // cookie source=1时 在 summary info 获取\n    pub async fn send_like(\n        &self,\n        uin: i64,\n        count: i32,\n        source: i32,\n        cookies: Bytes,\n    ) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_send_like_packet(uin, count, source, cookies);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    // TODO 待完善\n    // 图片 OCR\n    pub async fn image_ocr(\n        &self,\n        img_url: String,\n        md5: String,\n        size: i32,\n        wight: i32,\n        height: i32,\n    ) -> RQResult<OcrResponse> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_image_ocr_request_packet(img_url, md5, size, wight, height);\n        let resp = self.send_and_wait(req).await?;\n\n        let decode = self\n            .engine\n            .read()\n            .await\n            .decode_image_ocr_response(resp.body)?;\n        Ok(decode)\n    }\n\n    // 标记消息已收到，server 不再重复推送\n    pub async fn delete_message(&self, items: Vec<pb::MessageItem>) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_delete_message_request_packet(items);\n        let _ = self.send_and_wait(req).await?;\n        Ok(())\n    }\n\n    // 标记 online_push 已收到，server 不再重复推送\n    pub async fn delete_online_push(\n        &self,\n        uin: i64,\n        svrip: i32,\n        push_token: Bytes,\n        seq: u16,\n        del_msg: Vec<ricq_core::jce::PushMessageInfo>,\n    ) -> RQResult<()> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_delete_online_push_packet(uin, svrip, push_token, seq, del_msg);\n        self.send(req).await?;\n        Ok(())\n    }\n\n    // sync message\n    async fn sync_message(&self, sync_flag: i32) -> RQResult<MessageSyncResponse> {\n        let time = UNIX_EPOCH.elapsed().unwrap().as_secs() as i64;\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_get_message_request_packet(sync_flag, time);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_message_svc_packet(resp.body)\n    }\n\n    // 从服务端拉取通知\n    pub(crate) async fn sync_all_message(&self) -> RQResult<Vec<pb::msg::Message>> {\n        const SYNC_START: i32 = 0;\n        const _SYNC_CONTINUE: i32 = 1;\n        const SYNC_STOP: i32 = 2;\n\n        let mut sync_flag = SYNC_START;\n        let mut msgs = Vec::new();\n        loop {\n            let resp = match self.sync_message(sync_flag).await {\n                Ok(resp) => resp,\n                Err(_) => {\n                    tracing::warn!(\"failed to sync_message\");\n                    break;\n                }\n            };\n            if let Err(err) = self\n                .delete_message(\n                    resp.msgs\n                        .iter()\n                        .map(|m| {\n                            let head = m.head.as_ref().unwrap();\n                            pb::MessageItem {\n                                from_uin: head.from_uin(),\n                                to_uin: head.to_uin(),\n                                msg_type: head.msg_type(),\n                                msg_seq: head.msg_seq(),\n                                msg_uid: head.msg_uid(),\n                                ..Default::default()\n                            }\n                        })\n                        .collect(),\n                )\n                .await\n            {\n                tracing::warn!(\"failed to delete_message: {}\", err);\n                break;\n            }\n            match resp.msg_rsp_type {\n                0 => {\n                    let mut engine = self.engine.write().await;\n                    if let Some(sync_cookie) = resp.sync_cookie {\n                        engine.transport.sig.sync_cookie = Bytes::from(sync_cookie)\n                    }\n                    if let Some(pub_account_cookie) = resp.pub_account_cookie {\n                        engine.transport.sig.pub_account_cookie = Bytes::from(pub_account_cookie)\n                    }\n                }\n                1 => {\n                    let mut engine = self.engine.write().await;\n                    if let Some(sync_cookie) = resp.sync_cookie {\n                        engine.transport.sig.sync_cookie = Bytes::from(sync_cookie)\n                    }\n                }\n                2 => {\n                    let mut engine = self.engine.write().await;\n                    if let Some(pub_account_cookie) = resp.pub_account_cookie {\n                        engine.transport.sig.pub_account_cookie = Bytes::from(pub_account_cookie)\n                    }\n                }\n                _ => {}\n            }\n            msgs.extend(resp.msgs);\n            sync_flag = resp.sync_flag;\n            if sync_flag == SYNC_STOP {\n                break;\n            }\n        }\n        Ok(msgs)\n    }\n\n    // 获取名片信息\n    pub async fn get_summary_info(&self, uin: i64) -> RQResult<SummaryCardInfo> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_summary_card_request_packet(uin);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_summary_card_response(resp.body)\n    }\n\n    // 准备上传消息，获取 ukey, resid, ip, port\n    async fn multi_msg_apply_up(\n        &self,\n        dst_uin: i64,\n        data: &[u8],\n        is_long: bool,\n    ) -> RQResult<pb::multimsg::MultiMsgApplyUpRsp> {\n        let req = self.engine.read().await.build_multi_msg_apply_up_req(\n            data.len() as i64,\n            md5::compute(data).to_vec(),\n            if is_long { 1 } else { 2 },\n            dst_uin,\n        );\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_multi_msg_apply_up_resp(resp.body)\n    }\n\n    // 上传长消息、转发消息 私聊未测试\n    pub async fn upload_msgs(\n        &self,\n        group_code: i64,\n        msgs: Vec<ForwardMessage>,\n        is_long: bool,\n    ) -> RQResult<String> {\n        let data = self\n            .engine\n            .read()\n            .await\n            .calculate_validation_data(msgs, group_code);\n        let rsp = self\n            .multi_msg_apply_up(group_code2uin(group_code), &data, is_long)\n            .await?;\n        let resid = rsp.msg_resid;\n        if self.highway_session.read().await.session_key.is_empty() {\n            return Err(RQError::EmptyField(\"highway_session_key is empty\"));\n        }\n        let addrs: Vec<RQAddr> = rsp\n            .uint32_up_ip\n            .into_iter()\n            .zip(rsp.uint32_up_port)\n            .map(|(ip, port)| RQAddr(ip as u32, port as u16))\n            .collect();\n        let body =\n            self.engine\n                .read()\n                .await\n                .build_long_req(group_code2uin(group_code), data, rsp.msg_ukey);\n        for addr in addrs {\n            match self\n                .highway_upload_bdh(\n                    addr.into(),\n                    BdhInput {\n                        command_id: 27,\n                        ticket: rsp.msg_sig.clone(),\n                        chunk_size: 8192 * 8,\n                        ..Default::default()\n                    },\n                    &body,\n                )\n                .await\n            {\n                Ok(_) => return Ok(resid),\n                Err(_) => continue,\n            }\n        }\n        Err(RQError::Other(\"failed to upload long message\".into()))\n    }\n\n    // 获取转发消息下载地址和 key\n    async fn multi_msg_apply_down(\n        &self,\n        res_id: String,\n    ) -> RQResult<pb::multimsg::MultiMsgApplyDownRsp> {\n        let req = self\n            .engine\n            .read()\n            .await\n            .build_multi_msg_apply_down_req(res_id);\n        let resp = self.send_and_wait(req).await?;\n        self.engine\n            .read()\n            .await\n            .decode_multi_msg_apply_down_resp(resp.body)\n    }\n\n    pub async fn download_msgs(&self, res_id: String) -> RQResult<Vec<ForwardMessage>> {\n        let mut resp = self.multi_msg_apply_down(res_id).await?;\n        if resp.result != 0 {\n            return Err(RQError::Other(format!(\n                \"multi_msg_apply_down result {}\",\n                resp.result\n            )));\n        }\n        let prefix=if let Some(pb::multimsg::ExternMsg { channel_type }) = resp.msg_extern_info && channel_type == 2 {\n            \"https://ssl.htdata.qq.com\".into()\n        } else {\n            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));\n            format!(\"http://{addr}\")\n        };\n        let _url = format!(\n            \"{}{}\",\n            prefix,\n            String::from_utf8_lossy(&resp.thumb_down_para)\n        );\n        let _encrypt_key = resp.msg_key;\n        // TODO get data and decrypt\n        // TODO decoder -> LongRspBody\n        // TODO uncompress\n        // TODO link message, convert to Vec<ForwardMessage>\n        todo!()\n    }\n\n    /// 发送消息\n    pub async fn send_message(\n        &self,\n        routing_head: pb::msg::routing_head::RoutingHead,\n        message_chain: MessageChain,\n        ptt: Option<pb::msg::Ptt>,\n    ) -> RQResult<MessageReceipt> {\n        let time = UNIX_EPOCH.elapsed().unwrap().as_secs() as i64;\n        let seq = self.engine.read().await.next_friend_seq();\n        let ran = (rand::random::<u32>() >> 1) as i32;\n        let (tx, _) = tokio::sync::oneshot::channel();\n        {\n            self.receipt_waiters.lock().await.cache_set(ran, tx);\n        }\n        let req = self.engine.read().await.build_send_message_packet(\n            routing_head,\n            message_chain.into(),\n            ptt,\n            seq,\n            ran,\n            time,\n        );\n        self.send_and_wait(req).await?;\n        let receipt = MessageReceipt {\n            seqs: vec![seq],\n            rands: vec![ran],\n            time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i64,\n        };\n        // 除了群聊，都不需要等 receipt 的 seq\n        Ok(receipt)\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/event.rs",
    "content": "use std::sync::Arc;\n\nuse ricq_core::command::profile_service::{JoinGroupRequest, NewFriendRequest, SelfInvited};\nuse ricq_core::structs::{\n    DeleteFriend, FriendAudioMessage, FriendInfo, FriendMessageRecall, FriendPoke,\n    GroupAudioMessage, GroupDisband, GroupLeave, GroupMessageRecall, GroupMute, GroupNameUpdate,\n    GroupPoke, GroupTempMessage, MemberPermissionChange, NewMember,\n};\nuse ricq_core::{jce, RQResult};\n\nuse crate::client::NetworkStatus;\nuse crate::structs::{FriendMessage, GroupMessage};\nuse crate::Client;\n\n#[derive(Clone, derivative::Derivative)]\n#[derivative(Debug)]\npub struct EventWithClient<T> {\n    #[derivative(Debug = \"ignore\")]\n    pub client: Arc<Client>,\n    pub inner: T,\n}\n\npub type GroupMessageEvent = EventWithClient<GroupMessage>;\n\nimpl GroupMessageEvent {\n    pub async fn recall(&self) -> RQResult<()> {\n        // TODO check permission\n        self.client\n            .recall_group_message(\n                self.inner.group_code,\n                self.inner.seqs.clone(),\n                self.inner.rands.clone(),\n            )\n            .await\n    }\n}\n\npub type FriendMessageEvent = EventWithClient<FriendMessage>;\npub type GroupTempMessageEvent = EventWithClient<GroupTempMessage>;\npub type JoinGroupRequestEvent = EventWithClient<JoinGroupRequest>;\n\nimpl JoinGroupRequestEvent {\n    pub async fn accept(&self) -> RQResult<()> {\n        self.client\n            .solve_group_system_message(\n                self.inner.msg_seq,\n                self.inner.req_uin,\n                self.inner.group_code,\n                self.inner.suspicious,\n                self.inner.invitor_uin.is_some(),\n                true,\n                false,\n                \"\".into(),\n            )\n            .await\n    }\n\n    pub async fn reject(&self, reason: String, block: bool) -> RQResult<()> {\n        self.client\n            .solve_group_system_message(\n                self.inner.msg_seq,\n                self.inner.req_uin,\n                self.inner.group_code,\n                self.inner.suspicious,\n                self.inner.invitor_uin.is_some(),\n                false,\n                block,\n                reason,\n            )\n            .await\n    }\n}\n\npub type NewFriendRequestEvent = EventWithClient<NewFriendRequest>;\n\nimpl NewFriendRequestEvent {\n    pub async fn accept(&self) -> RQResult<()> {\n        self.client\n            .solve_friend_system_message(self.inner.msg_seq, self.inner.req_uin, true)\n            .await\n    }\n\n    pub async fn reject(&self) -> RQResult<()> {\n        self.client\n            .solve_friend_system_message(self.inner.msg_seq, self.inner.req_uin, false)\n            .await\n    }\n}\n\npub type NewMemberEvent = EventWithClient<NewMember>;\npub type GroupMuteEvent = EventWithClient<GroupMute>;\npub type FriendMessageRecallEvent = EventWithClient<FriendMessageRecall>;\npub type GroupMessageRecallEvent = EventWithClient<GroupMessageRecall>;\npub type NewFriendEvent = EventWithClient<FriendInfo>;\npub type GroupLeaveEvent = EventWithClient<GroupLeave>;\npub type GroupDisbandEvent = EventWithClient<GroupDisband>;\npub type FriendPokeEvent = EventWithClient<FriendPoke>;\npub type GroupPokeEvent = EventWithClient<GroupPoke>;\npub type GroupNameUpdateEvent = EventWithClient<GroupNameUpdate>;\npub type DeleteFriendEvent = EventWithClient<DeleteFriend>;\npub type MemberPermissionChangeEvent = EventWithClient<MemberPermissionChange>;\npub type SelfInvitedEvent = EventWithClient<SelfInvited>;\npub type GroupAudioMessageEvent = EventWithClient<GroupAudioMessage>;\n\nimpl GroupAudioMessageEvent {\n    pub async fn url(&self) -> RQResult<String> {\n        self.client\n            .get_group_audio_url(self.inner.group_code, self.inner.audio.clone())\n            .await\n    }\n}\n\npub type FriendAudioMessageEvent = EventWithClient<FriendAudioMessage>;\n\nimpl FriendAudioMessageEvent {\n    pub async fn url(&self) -> RQResult<String> {\n        self.client\n            .get_friend_audio_url(self.inner.from_uin, self.inner.audio.clone())\n            .await\n    }\n}\n\npub type KickedOfflineEvent = EventWithClient<jce::RequestPushForceOffline>;\npub type MSFOfflineEvent = EventWithClient<jce::RequestMSFForceOffline>;\n\n#[derive(Copy, Clone, Debug)]\n#[repr(u8)]\npub enum DisconnectReason {\n    /// 主动断开\n    Actively(NetworkStatus),\n    /// 网络原因\n    Network,\n}\n\nimpl DisconnectReason {\n    /// 客户端网络状态\n    pub fn status(&self) -> NetworkStatus {\n        match self {\n            Self::Actively(s) => *s,\n            Self::Network => NetworkStatus::NetworkOffline,\n        }\n    }\n}\n\npub type ClientDisconnect = EventWithClient<DisconnectReason>;\n\nimpl ClientDisconnect {\n    pub fn reason(&self) -> DisconnectReason {\n        self.inner\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/handler/mod.rs",
    "content": "use std::future::Future;\nuse std::pin::Pin;\n\nuse async_trait::async_trait;\nuse tokio::sync::{\n    broadcast::Sender as BroadcastSender,\n    mpsc::{Sender as MpscSender, UnboundedSender},\n    watch::Sender as WatchSender,\n};\n\nuse crate::client::event::*;\n\n/// 所有需要外发的数据的枚举打包\n#[derive(Clone, derivative::Derivative)]\n#[derivative(Debug)]\npub enum QEvent {\n    /// 登录成功事件\n    Login(i64),\n    /// 群消息\n    GroupMessage(GroupMessageEvent),\n    /// 群语音\n    GroupAudioMessage(GroupAudioMessageEvent),\n    /// 好友消息\n    FriendMessage(FriendMessageEvent),\n    /// 群语音\n    FriendAudioMessage(FriendAudioMessageEvent),\n    /// 群临时消息\n    GroupTempMessage(GroupTempMessageEvent),\n    /// 加群申请\n    GroupRequest(JoinGroupRequestEvent),\n    /// 加群申请\n    SelfInvited(SelfInvitedEvent),\n    /// 加好友申请\n    NewFriendRequest(NewFriendRequestEvent),\n    /// 新成员入群\n    NewMember(NewMemberEvent),\n    /// 成员被禁言\n    GroupMute(GroupMuteEvent),\n    /// 好友消息撤回\n    FriendMessageRecall(FriendMessageRecallEvent),\n    /// 群消息撤回\n    GroupMessageRecall(GroupMessageRecallEvent),\n    /// 新好友\n    NewFriend(NewFriendEvent),\n    /// 退群/被踢\n    GroupLeave(GroupLeaveEvent),\n    /// 群解散\n    GroupDisband(GroupDisbandEvent),\n    /// 好友戳一戳\n    FriendPoke(FriendPokeEvent),\n    /// 群成员戳一戳\n    GroupPoke(GroupPokeEvent),\n    /// 群名称修改\n    GroupNameUpdate(GroupNameUpdateEvent),\n    /// 好友删除\n    DeleteFriend(DeleteFriendEvent),\n    /// 群成员权限变更\n    MemberPermissionChange(MemberPermissionChangeEvent),\n    /// 被其他客户端踢下线\n    /// 不能用于掉线重连，掉线重连以 start 返回为准\n    KickedOffline(KickedOfflineEvent),\n    /// 服务端强制下线\n    /// 不能用于掉线重连，掉线重连以 start 返回为准\n    MSFOffline(MSFOfflineEvent),\n    /// 网络原因/客户端主动掉线\n    /// 可用于掉线重连\n    ClientDisconnect(ClientDisconnect),\n}\n\n/// 处理外发数据的接口\n///\n/// 同时，所有 `async fn(QEvent)` 都已自动实现 `Handler`。\n///\n/// # Examples\n///\n/// 你可以为自己的 struct 实现 Handler：\n///\n/// ```ignore\n/// struct MyHandler;\n/// impl Handler for MyHandler { ... }\n/// ```\n///\n/// 或者只定义单个事件处理函数，更简洁：\n///\n/// ```\n/// # use ricq::{Client, Device, Protocol, handler::QEvent};\n/// # fn test(device: Device) {\n/// async fn on_event(e: QEvent) {\n///     dbg!(e);\n/// }\n/// let client = Client::new(\n///     device,\n///     Protocol::MacOS.into(),\n///     on_event as fn(_) -> _,\n/// );\n/// # }\n/// ```\n#[async_trait]\npub trait Handler: Sync {\n    async fn handle(&self, event: QEvent);\n}\n\n// 这里还有一种 Fn(QEvent) -> Fut 的写法，但是会与 PartlyHandler 冲突\nimpl<Fut> Handler for fn(QEvent) -> Fut\nwhere\n    Fut: Future<Output = ()> + Send,\n{\n    fn handle<'a: 'b, 'b>(&'a self, e: QEvent) -> Pin<Box<dyn Future<Output = ()> + Send + 'b>> {\n        Box::pin(async move { self(e).await })\n    }\n}\n\n/// 一个默认 Handler，只是把信息打印出来\npub struct DefaultHandler;\n\n#[async_trait]\nimpl Handler for DefaultHandler {\n    async fn handle(&self, e: QEvent) {\n        match e {\n            QEvent::GroupMessage(m) => {\n                tracing::info!(\n                    \"MESSAGE (GROUP={}): {}\",\n                    m.inner.group_code,\n                    m.inner.elements\n                )\n            }\n            QEvent::FriendMessage(m) => {\n                tracing::info!(\n                    \"MESSAGE (FRIEND={}): {}\",\n                    m.inner.from_uin,\n                    m.inner.elements\n                )\n            }\n            QEvent::GroupTempMessage(m) => {\n                tracing::info!(\"MESSAGE (TEMP={}): {}\", m.inner.from_uin, m.inner.elements)\n            }\n            QEvent::GroupRequest(m) => {\n                tracing::info!(\n                    \"REQUEST (GROUP={}, UIN={}): {}\",\n                    m.inner.group_code,\n                    m.inner.req_uin,\n                    m.inner.message\n                )\n            }\n            QEvent::NewFriendRequest(m) => {\n                tracing::info!(\"REQUEST (UIN={}): {}\", m.inner.req_uin, m.inner.message)\n            }\n            _ => tracing::info!(\"{:?}\", e),\n        }\n    }\n}\n\n#[async_trait]\nimpl Handler for BroadcastSender<QEvent> {\n    async fn handle(&self, msg: QEvent) {\n        self.send(msg).ok();\n    }\n}\n\n#[async_trait]\nimpl Handler for MpscSender<QEvent> {\n    async fn handle(&self, msg: QEvent) {\n        self.send(msg).await.ok();\n    }\n}\n\n#[async_trait]\nimpl Handler for UnboundedSender<QEvent> {\n    async fn handle(&self, msg: QEvent) {\n        self.send(msg).ok();\n    }\n}\n\n#[async_trait]\nimpl Handler for WatchSender<QEvent> {\n    async fn handle(&self, msg: QEvent) {\n        self.send(msg).ok();\n    }\n}\n\n#[async_trait]\npub trait PartlyHandler: Sync {\n    async fn handle_login(&self, _: i64) {}\n    async fn handle_group_message(&self, _event: GroupMessageEvent) {}\n    async fn handle_group_audio(&self, _event: GroupAudioMessageEvent) {}\n    async fn handle_friend_message(&self, _event: FriendMessageEvent) {}\n    async fn handle_friend_audio(&self, _event: FriendAudioMessageEvent) {}\n    async fn handle_group_temp_message(&self, _event: GroupTempMessageEvent) {}\n    async fn handle_group_request(&self, _event: JoinGroupRequestEvent) {}\n    async fn handle_self_invited(&self, _event: SelfInvitedEvent) {}\n    async fn handle_friend_request(&self, _event: NewFriendRequestEvent) {}\n    async fn handle_new_member(&self, _event: NewMemberEvent) {}\n    async fn handle_group_mute(&self, _event: GroupMuteEvent) {}\n    async fn handle_friend_message_recall(&self, _event: FriendMessageRecallEvent) {}\n    async fn handle_group_message_recall(&self, _event: GroupMessageRecallEvent) {}\n    async fn handle_new_friend(&self, _event: NewFriendEvent) {}\n    async fn handle_group_leave(&self, _event: GroupLeaveEvent) {}\n    async fn handle_group_disband(&self, _event: GroupDisbandEvent) {}\n    async fn handle_friend_poke(&self, _event: FriendPokeEvent) {}\n    async fn handle_group_poke(&self, _event: GroupPokeEvent) {}\n    async fn handle_group_name_update(&self, _event: GroupNameUpdateEvent) {}\n    async fn handle_delete_friend(&self, _event: DeleteFriendEvent) {}\n    async fn handle_member_permission_change(&self, _event: MemberPermissionChangeEvent) {}\n    async fn handle_kicked_offline(&self, _event: KickedOfflineEvent) {}\n    async fn handle_msf_offline(&self, _event: MSFOfflineEvent) {}\n    async fn handle_client_disconnect(&self, _event: ClientDisconnect) {}\n}\n\n#[async_trait]\nimpl<PH> Handler for PH\nwhere\n    PH: PartlyHandler,\n{\n    async fn handle(&self, event: QEvent) {\n        match event {\n            QEvent::Login(uin) => self.handle_login(uin).await,\n            QEvent::GroupMessage(m) => self.handle_group_message(m).await,\n            QEvent::GroupAudioMessage(m) => self.handle_group_audio(m).await,\n            QEvent::FriendMessage(m) => self.handle_friend_message(m).await,\n            QEvent::FriendAudioMessage(m) => self.handle_friend_audio(m).await,\n            QEvent::GroupTempMessage(m) => self.handle_group_temp_message(m).await,\n            QEvent::GroupRequest(m) => self.handle_group_request(m).await,\n            QEvent::SelfInvited(m) => self.handle_self_invited(m).await,\n            QEvent::NewFriendRequest(m) => self.handle_friend_request(m).await,\n            QEvent::NewMember(m) => self.handle_new_member(m).await,\n            QEvent::GroupMute(m) => self.handle_group_mute(m).await,\n            QEvent::FriendMessageRecall(m) => self.handle_friend_message_recall(m).await,\n            QEvent::GroupMessageRecall(m) => self.handle_group_message_recall(m).await,\n            QEvent::NewFriend(m) => self.handle_new_friend(m).await,\n            QEvent::GroupLeave(m) => self.handle_group_leave(m).await,\n            QEvent::GroupDisband(m) => self.handle_group_disband(m).await,\n            QEvent::FriendPoke(m) => self.handle_friend_poke(m).await,\n            QEvent::GroupPoke(m) => self.handle_group_poke(m).await,\n            QEvent::GroupNameUpdate(m) => self.handle_group_name_update(m).await,\n            QEvent::DeleteFriend(m) => self.handle_delete_friend(m).await,\n            QEvent::MemberPermissionChange(m) => self.handle_member_permission_change(m).await,\n            QEvent::KickedOffline(m) => self.handle_kicked_offline(m).await,\n            QEvent::MSFOffline(m) => self.handle_msf_offline(m).await,\n            QEvent::ClientDisconnect(m) => self.handle_client_disconnect(m).await,\n        }\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/highway/codec.rs",
    "content": "use bytes::{Buf, BufMut, BytesMut};\nuse tokio_util::codec::{Decoder, Encoder};\n\nuse ricq_core::RQError;\n\nuse crate::client::highway::HighwayFrame;\n\npub struct HighwayCodec;\n\nimpl Encoder<HighwayFrame> for HighwayCodec {\n    type Error = RQError;\n\n    fn encode(&mut self, item: HighwayFrame, dst: &mut BytesMut) -> Result<(), Self::Error> {\n        dst.put_u8(40);\n        dst.put_u32(item.head.len() as u32);\n        dst.put_u32(item.body.len() as u32);\n        dst.put_slice(&item.head);\n        dst.put_slice(&item.body);\n        dst.put_u8(41);\n        Ok(())\n    }\n}\n\nimpl Decoder for HighwayCodec {\n    type Item = HighwayFrame;\n    type Error = RQError;\n\n    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {\n        if src.len() < 10 {\n            return Ok(None);\n        }\n        src.get_u8();\n        let head_length = src.get_u32() as usize;\n        let body_length = src.get_u32() as usize;\n        if head_length + body_length + 1 > src.remaining() {\n            return Ok(None);\n        }\n        let head = src.copy_to_bytes(head_length);\n        let body = src.copy_to_bytes(body_length);\n        src.get_u8();\n        Ok(Some(Self::Item { head, body }))\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/highway/mod.rs",
    "content": "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",
    "content": "use std::net::SocketAddr;\nuse std::time::Duration;\n\nuse bytes::Bytes;\nuse futures_util::{SinkExt, StreamExt};\nuse tokio::net::TcpStream;\nuse tokio_util::codec::Framed;\n\nuse ricq_core::command::common::PbToBytes;\nuse ricq_core::crypto::qqtea_encrypt;\nuse ricq_core::highway::BdhInput;\nuse ricq_core::{pb, RQError, RQResult};\n\nuse crate::client::highway::codec::HighwayCodec;\nuse crate::client::highway::HighwayFrame;\nuse crate::client::tcp::tcp_connect_timeout;\nuse crate::Client;\n\nimpl Client {\n    pub async fn highway_upload_bdh(\n        &self,\n        addr: SocketAddr,\n        mut input: BdhInput,\n        data: &[u8],\n    ) -> RQResult<Bytes> {\n        if input.encrypt {\n            let session_key = self.highway_session.read().await.session_key.clone();\n            input.ext = qqtea_encrypt(&input.ext, &session_key)\n        }\n        let stream = tcp_connect_timeout(addr, Duration::from_secs(5))\n            .await\n            .map_err(RQError::IO)?;\n        let mut stream = Framed::new(stream, HighwayCodec);\n        // send heartbeat\n        let sum = md5::compute(data).to_vec();\n        let length = data.len();\n\n        if input.send_echo {\n            stream\n                .send(HighwayFrame {\n                    head: self.highway_session.read().await.build_heartbreak(),\n                    body: Bytes::new(),\n                })\n                .await?;\n            let _ = read_response(&mut stream).await?;\n        }\n        let mut ticket = input.ticket;\n        let mut rsp_ext = Bytes::new();\n        let data = Bytes::copy_from_slice(data);\n        let len = data.len();\n        let chunk_size = input.chunk_size;\n\n        for i in (0..len).step_by(chunk_size) {\n            let min = std::cmp::min(i + chunk_size, len);\n            let chunk = data.slice(i..min);\n            let head = pb::ReqDataHighwayHead {\n                msg_basehead: Some(self.highway_session.read().await.build_basehead(\n                    \"PicUp.DataUp\".into(),\n                    4096,\n                    input.command_id,\n                    2052,\n                )),\n                msg_seghead: Some(self.highway_session.read().await.build_seghead(\n                    length as i64,\n                    i as i64,\n                    &chunk,\n                    ticket.clone(),\n                    sum.clone(),\n                )),\n                req_extendinfo: input.ext.clone(),\n                ..Default::default()\n            };\n            stream\n                .send(HighwayFrame {\n                    head: head.to_bytes(),\n                    body: chunk,\n                })\n                .await?;\n            let resp = read_response(&mut stream).await?;\n            let rsp_head = self\n                .highway_session\n                .read()\n                .await\n                .decode_rsp_head(resp.head)?;\n            if rsp_head.error_code != 0 {\n                return Err(RQError::Other(format!(\n                    \"error_code = {}\",\n                    rsp_head.error_code\n                )));\n            }\n            if !rsp_head.rsp_extendinfo.is_empty() {\n                rsp_ext = Bytes::from(rsp_head.rsp_extendinfo)\n            }\n            if let Some(h) = rsp_head.msg_seghead {\n                if !h.serviceticket.is_empty() {\n                    ticket = h.serviceticket\n                }\n            }\n        }\n\n        Ok(rsp_ext)\n    }\n}\n\nasync fn read_response(stream: &mut Framed<TcpStream, HighwayCodec>) -> RQResult<HighwayFrame> {\n    loop {\n        if let Some(resp) = stream.next().await {\n            return resp;\n        }\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/mod.rs",
    "content": "use bytes::Bytes;\nuse std::collections::HashMap;\nuse std::sync::atomic::{AtomicBool, AtomicI64, AtomicU8, Ordering};\nuse std::sync::Arc;\nuse std::time::UNIX_EPOCH;\n\nuse cached::Cached;\nuse futures_util::StreamExt;\nuse tokio::sync::{broadcast, RwLock};\nuse tokio::sync::{oneshot, Mutex};\nuse tokio::time::{sleep, Duration};\n\npub use net::{Connector, DefaultConnector};\nuse ricq_core::command::common::PbToBytes;\nuse ricq_core::command::online_push::GroupMessagePart;\nuse ricq_core::command::profile_service::GroupSystemMessages;\nuse ricq_core::common::RQAddr;\nuse ricq_core::hex::decode_hex;\nuse ricq_core::protocol::version::Version;\nuse ricq_core::protocol::{device::Device, packet::Packet};\nuse ricq_core::structs::{AccountInfo, AddressInfo, OtherClientInfo};\nuse ricq_core::Engine;\npub use ricq_core::Token;\n\nuse crate::qsign::{QSignClient, QSignResponse, RequestCallback, SignData};\nuse crate::{RQError, RQResult};\n\nmod api;\npub mod event;\npub mod handler;\nmod highway;\npub(crate) mod net;\nmod processor;\npub mod qimei;\nmod tcp;\n\nconst SIGN_COMMANDS: &str = r#\"ConnAuthSvr.fast_qq_login\nConnAuthSvr.sdk_auth_api\nConnAuthSvr.sdk_auth_api_emp\nFeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoBarrage\nFeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoComment\nFeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoFollow\nFeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoLike\nFeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoPush\nFeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoReply\nFeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.PublishFeed\nFeedCloudSvr.trpc.videocircle.circleprofile.CircleProfile.SetProfile\nfriendlist.addFriend\nfriendlist.AddFriendReq\nfriendlist.ModifyGroupInfoReq\nMessageSvc.PbSendMsg\nMsgProxy.SendMsg\nOidbSvc.0x4ff_9\nOidbSvc.0x4ff_9_IMCore\nOidbSvc.0x56c_6\nOidbSvc.0x6d9_4\nOidbSvc.0x758\nOidbSvc.0x758_0\nOidbSvc.0x758_1\nOidbSvc.0x88d_0\nOidbSvc.0x89a_0\nOidbSvc.0x89b_1\nOidbSvc.0x8a1_0\nOidbSvc.0x8a1_7\nOidbSvc.0x8ba\nOidbSvc.0x9fa\nOidbSvc.oidb_0x758\nOidbSvcTrpcTcp.0x101e_1\nOidbSvcTrpcTcp.0x101e_2\nOidbSvcTrpcTcp.0x1100_1\nOidbSvcTrpcTcp.0x1105_1\nOidbSvcTrpcTcp.0x1107_1\nOidbSvcTrpcTcp.0x55f_0\nOidbSvcTrpcTcp.0x6d9_4\nOidbSvcTrpcTcp.0xf55_1\nOidbSvcTrpcTcp.0xf57_1\nOidbSvcTrpcTcp.0xf57_106\nOidbSvcTrpcTcp.0xf57_9\nOidbSvcTrpcTcp.0xf65_1\nOidbSvcTrpcTcp.0xf65_10 \nOidbSvcTrpcTcp.0xf67_1\nOidbSvcTrpcTcp.0xf67_5\nOidbSvcTrpcTcp.0xf6e_1\nOidbSvcTrpcTcp.0xf88_1\nOidbSvcTrpcTcp.0xf89_1\nOidbSvcTrpcTcp.0xfa5_1\nProfileService.getGroupInfoReq\nProfileService.GroupMngReq\nQChannelSvr.trpc.qchannel.commwriter.ComWriter.DoComment\nQChannelSvr.trpc.qchannel.commwriter.ComWriter.DoReply\nQChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed\nqidianservice.135\nqidianservice.207\nqidianservice.269\nqidianservice.290\nSQQzoneSvc.addComment\nSQQzoneSvc.addReply\nSQQzoneSvc.forward\nSQQzoneSvc.like\nSQQzoneSvc.publishmood\nSQQzoneSvc.shuoshuo\ntrpc.group_pro.msgproxy.sendmsg\ntrpc.login.ecdh.EcdhService.SsoNTLoginPasswordLoginUnusualDevice\ntrpc.o3.ecdh_access.EcdhAccess.SsoEstablishShareKey\ntrpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Access\ntrpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Establish\ntrpc.o3.ecdh_access.EcdhAccess.SsoSecureAccess\ntrpc.o3.report.Report.SsoReport\ntrpc.passwd.manager.PasswdManager.SetPasswd\ntrpc.passwd.manager.PasswdManager.VerifyPasswd\ntrpc.qlive.relationchain_svr.RelationchainSvr.Follow\ntrpc.qlive.word_svr.WordSvr.NewPublicChat\ntrpc.qqhb.qqhb_proxy.Handler.sso_handle\ntrpc.springfestival.redpacket.LuckyBag.SsoSubmitGrade\nwtlogin.device_lock\nwtlogin.exchange_emp\nwtlogin.login\nwtlogin.name2uin\nwtlogin.qrlogin\nwtlogin.register\nwtlogin.trans_emp\nwtlogin_device.login\nwtlogin_device.tran_sim_emp\"#;\n\npub struct Client {\n    /// QEvent Handler 调用 handle 方法外发 QEvent\n    handler: Box<dyn handler::Handler + Sync + Send + 'static>,\n    pub engine: RwLock<Engine>,\n\n    // 状态相关\n    /// 网络状态\n    status: AtomicU8,\n    /// 停止网络信号 Sender\n    disconnect_signal: broadcast::Sender<()>,\n    /// 是否在线\n    pub online: AtomicBool,\n    /// 心跳包是否已启用\n    pub heartbeat_enabled: AtomicBool,\n\n    // 包相关\n    /// 外发包 Sender\n    out_pkt_sender: net::OutPktSender,\n    /// send_and_wait WaitMap\n    packet_promises: RwLock<HashMap<i32, oneshot::Sender<Packet>>>,\n    /// 当前客户端发送消息后使用 cache 避免上报自身消息事件\n    receipt_waiters: Mutex<cached::TimedCache<i32, oneshot::Sender<i32>>>,\n\n    // account info\n    pub account_info: RwLock<AccountInfo>,\n\n    // address\n    pub address: RwLock<AddressInfo>,\n    /// 其他同时在线客户端\n    pub online_clients: RwLock<Vec<OtherClientInfo>>,\n\n    // statics\n    pub last_message_time: AtomicI64,\n    /// 调用 new 方法时的时间戳\n    pub start_time: i32,\n\n    /// 群消息 builder 寄存 <div_seq, parts> : parts is sorted by pkg_index\n    group_message_builder: RwLock<cached::TimedCache<i32, Vec<GroupMessagePart>>>,\n    /// 每个 28 Byte\n    c2c_cache: RwLock<cached::TimedCache<(i64, i64, i32, i64), ()>>,\n    push_req_cache: RwLock<cached::TimedCache<(i16, i64), ()>>,\n    push_trans_cache: RwLock<cached::TimedCache<(i32, i64), ()>>,\n    group_sys_message_cache: RwLock<GroupSystemMessages>,\n\n    pub highway_session: RwLock<ricq_core::highway::Session>,\n    pub highway_addrs: RwLock<Vec<RQAddr>>,\n\n    packet_handler: RwLock<HashMap<String, broadcast::Sender<Packet>>>,\n    pub qsign_client: Arc<QSignClient>,\n}\n\nimpl super::Client {\n    /// 新建 Clinet\n    ///\n    /// **Notice: 该方法仅新建 Client 需要调用 start 方法连接到服务器**\n    pub fn new<H>(\n        device: Device,\n        version: Version,\n        qsign_client: Arc<QSignClient>,\n        handler: H,\n    ) -> Client\n    where\n        H: crate::client::handler::Handler + 'static + Sync + Send,\n    {\n        let (out_pkt_sender, _) = tokio::sync::broadcast::channel(1024);\n        let (disconnect_signal, _) = tokio::sync::broadcast::channel(8);\n\n        Client {\n            handler: Box::new(handler),\n            engine: RwLock::new(Engine::new(device, version)),\n            status: AtomicU8::new(NetworkStatus::Unknown as u8),\n            heartbeat_enabled: AtomicBool::new(false),\n            online: AtomicBool::new(false),\n            out_pkt_sender,\n            disconnect_signal,\n            // out_going_packet_session_id: RwLock::new(Bytes::from_static(&[0x02, 0xb0, 0x5b, 0x8b])),\n            packet_promises: Default::default(),\n            receipt_waiters: Mutex::new(cached::TimedCache::with_lifespan(60)),\n            account_info: Default::default(),\n            address: Default::default(),\n            online_clients: Default::default(),\n            last_message_time: Default::default(),\n            start_time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i32,\n            group_message_builder: RwLock::new(cached::TimedCache::with_lifespan(600)),\n            c2c_cache: RwLock::new(cached::TimedCache::with_lifespan(3600)),\n            push_req_cache: RwLock::new(cached::TimedCache::with_lifespan(30)),\n            push_trans_cache: RwLock::new(cached::TimedCache::with_lifespan(15)),\n            group_sys_message_cache: RwLock::new(Default::default()),\n            highway_session: RwLock::new(Default::default()),\n            highway_addrs: RwLock::new(Default::default()),\n            packet_handler: Default::default(),\n            qsign_client,\n        }\n    }\n\n    /// 新建 Clinet\n    ///\n    /// **Notice: 该方法仅新建 Client 需要调用 start 方法连接到服务器**\n    pub fn new_with_config<H>(\n        config: crate::Config,\n        qsign_client: Arc<QSignClient>,\n        handler: H,\n    ) -> Self\n    where\n        H: crate::client::handler::Handler + 'static + Sync + Send,\n    {\n        Self::new(config.device, config.version, qsign_client, handler)\n    }\n\n    /// 获取当前 Client uin\n    pub async fn uin(&self) -> i64 {\n        self.engine.read().await.uin.load(Ordering::Relaxed)\n    }\n\n    pub async fn sign_packet(&self, pkt: &mut Packet) -> RQResult<QSignResponse<SignData>> {\n        if !SIGN_COMMANDS.contains(&pkt.command_name) {\n            return Ok(Default::default());\n        }\n        let engine = self.engine.read().await;\n        let resp = self\n            .qsign_client\n            .sign(\n                pkt.uin,\n                engine.transport.version.qua,\n                &pkt.command_name,\n                pkt.seq_id,\n                &pkt.body,\n                &engine\n                    .transport\n                    .device\n                    .qimei\n                    .as_ref()\n                    .map(|qimei| qimei.q36.as_str())\n                    .unwrap_or_default(),\n                &engine.transport.device.android_id,\n                &engine.transport.sig.guid,\n            )\n            .await\n            .map_err(|err| RQError::Other(format!(\"failed to sign packet: {err}\")))?;\n        if resp.code != 0 {\n            return Err(RQError::Other(format!(\n                \"failed to sign packet, msg: {}\",\n                resp.msg\n            )));\n        }\n        let sign = ricq_core::pb::SsoReserveField {\n            flag: 0,\n            qimei: engine\n                .transport\n                .device\n                .qimei\n                .clone()\n                .unwrap_or_default()\n                .q16,\n            newconn_flag: 0,\n            uid: pkt.uin.to_string(),\n            imsi: 0,\n            network_type: 1,\n            ip_stack_type: 1,\n            message_type: 0,\n            sec_info: Some(ricq_core::pb::SsoSecureInfo {\n                sec_sig: decode_hex(&resp.data.sign).unwrap_or_default(),\n                sec_device_token: decode_hex(&resp.data.token).unwrap_or_default(),\n                sec_extra: decode_hex(&resp.data.extra).unwrap_or_default(),\n            }),\n            sso_ip_origin: 0,\n        }\n        .to_bytes();\n        pkt.sign = Some(sign);\n        Ok(resp)\n    }\n\n    pub async fn process_sign_callback(&self, callbacks: Vec<RequestCallback>) {\n        let callbacks: Vec<(i64, Packet)> = {\n            let engine = self.engine.read().await;\n            callbacks\n                .into_iter()\n                .map(|cb| {\n                    (\n                        cb.callback_id,\n                        engine.uni_packet(\n                            &cb.cmd,\n                            Bytes::from(decode_hex(&cb.body).unwrap_or_default()),\n                        ),\n                    )\n                })\n                .collect()\n        };\n        let _: Vec<_> = futures_util::stream::iter(callbacks)\n            .map(|(id, pkt)| async move {\n                let uin = pkt.uin;\n                let cmd = pkt.command_name.clone();\n                let resp = self.send_and_wait(pkt).await;\n                if let Err(ref err) = resp {\n                    tracing::error!(\n                        \"failed to process sign callback, id: {id}, cmd: {cmd}, err: {err}\"\n                    )\n                }\n                let resp = resp.unwrap_or_default();\n                if let Err(err) = self.qsign_client.submit(uin, &cmd, id, &resp.body).await {\n                    tracing::error!(\"failed to submit sign callback, err: {err}\")\n                }\n            })\n            .buffered(10)\n            .collect()\n            .await;\n    }\n\n    /// 向服务器发包\n    pub async fn send(&self, pkt: Packet) -> RQResult<usize> {\n        tracing::trace!(\"sending pkt {}-{},\", pkt.command_name, pkt.seq_id);\n        let data = self.engine.read().await.transport.encode_packet(pkt);\n        self.out_pkt_sender\n            .send(data)\n            .map_err(|_| RQError::Other(\"failed to send out_pkt\".into()))\n    }\n\n    /// 向服务器发包并等待接收返回的包，15 秒后超时返回 `Err(RQError::Timeout)`\n    #[async_recursion::async_recursion]\n    pub async fn send_and_wait(&self, mut pkt: Packet) -> RQResult<Packet> {\n        let callbacks = self.sign_packet(&mut pkt).await;\n        if let Err(ref err) = callbacks {\n            tracing::error!(\"failed to sign packet, err: {err}\");\n        }\n        let callbacks = callbacks.unwrap_or_default().data.request_callback;\n        let callback_future = self.process_sign_callback(callbacks);\n\n        tracing::trace!(\"send_and_waitting pkt {}-{},\", pkt.command_name, pkt.seq_id);\n        let seq = pkt.seq_id;\n        let expect = pkt.command_name.clone();\n        let data = self.engine.read().await.transport.encode_packet(pkt);\n        let (sender, receiver) = oneshot::channel();\n        {\n            let mut packet_promises = self.packet_promises.write().await;\n            packet_promises.insert(seq, sender);\n        }\n        if self.out_pkt_sender.send(data).is_err() {\n            let mut packet_promises = self.packet_promises.write().await;\n            packet_promises.remove(&seq);\n            return Err(RQError::Network);\n        }\n        let packet_future = tokio::time::timeout(std::time::Duration::from_secs(15), receiver);\n\n        let (resp, _) = tokio::join!(packet_future, callback_future);\n        match resp {\n            Ok(p) => p.unwrap().check_command_name(&expect),\n            Err(_) => {\n                tracing::trace!(\"waiting pkt {}-{} timeout\", expect, seq);\n                self.packet_promises.write().await.remove(&seq);\n                Err(RQError::Timeout)\n            }\n        }\n    }\n\n    /// 向服务器发送心跳包，并自动注册客户端\n    ///\n    /// 该方法会阻塞当前协程，通常 spawn 使用\n    pub async fn do_heartbeat(&self) {\n        self.heartbeat_enabled.store(true, Ordering::SeqCst);\n        let mut times = 0;\n        while self.online.load(Ordering::SeqCst) {\n            sleep(Duration::from_secs(30)).await;\n            if self.heartbeat().await.is_ok() {\n                times += 1;\n                if times >= 7 {\n                    if self.register_client().await.is_err() {\n                        break;\n                    }\n                    times = 0;\n                }\n            }\n        }\n        self.heartbeat_enabled.store(false, Ordering::SeqCst);\n    }\n\n    /// 生成 token\n    pub async fn gen_token(&self) -> Token {\n        self.engine.read().await.gen_token()\n    }\n\n    /// 从 token 恢复\n    pub async fn load_token(&self, token: Token) {\n        self.engine.write().await.load_token(token)\n    }\n\n    pub async fn device(&self) -> Device {\n        self.engine.read().await.transport.device.clone()\n    }\n\n    pub async fn version(&self) -> Version {\n        self.engine.read().await.transport.version.clone()\n    }\n\n    pub async fn get_highway_session_key(&self) -> Vec<u8> {\n        self.highway_session.read().await.session_key.to_vec()\n    }\n\n    /// 监听指定 command 数据包\n    pub async fn listen_command<S: ToString>(&self, command: S) -> broadcast::Receiver<Packet> {\n        self.packet_handler\n            .write()\n            .await\n            .cache_get_or_set_with(command.to_string(), || broadcast::channel(10).0)\n            .subscribe()\n    }\n}\n\nimpl Drop for Client {\n    fn drop(&mut self) {\n        self.stop(NetworkStatus::Drop);\n    }\n}\n\n#[derive(Copy, Clone, Debug)]\n#[repr(u8)]\npub enum NetworkStatus {\n    // 未启动\n    Unknown = 0,\n    // 运行中\n    Running = 1,\n    // 用户手动停止\n    Stop = 2,\n    // 内存释放\n    Drop = 3,\n    // 网络原因掉线\n    NetworkOffline = 4,\n    // 其他客户端踢下线\n    KickedOffline = 5,\n    // 服务端强制下线\n    MsfOffline = 6,\n}\n"
  },
  {
    "path": "ricq/src/client/net.rs",
    "content": "use std::net::SocketAddr;\nuse std::sync::atomic::Ordering;\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse crate::client::event::{ClientDisconnect, DisconnectReason};\nuse async_trait::async_trait;\nuse bytes::Bytes;\nuse futures_util::{SinkExt, StreamExt};\nuse tokio::io::{self, AsyncRead, AsyncWrite};\nuse tokio::net::TcpStream;\nuse tokio::sync::broadcast;\nuse tokio_util::codec::LengthDelimitedCodec;\n\nuse crate::client::tcp::tcp_connect_fastest;\nuse crate::client::NetworkStatus;\nuse crate::handler::QEvent;\n\nuse super::Client;\n\npub type OutPktSender = broadcast::Sender<Bytes>;\n\n#[async_trait]\npub trait Connector<T: AsyncRead + AsyncWrite> {\n    async fn connect(&self, client: &Client) -> io::Result<T>;\n}\n\npub struct DefaultConnector;\n\n#[async_trait]\nimpl Connector<TcpStream> for DefaultConnector {\n    async fn connect(&self, client: &Client) -> io::Result<TcpStream> {\n        tcp_connect_fastest(client.get_address_list().await, Duration::from_secs(5)).await\n    }\n}\n\nimpl crate::Client {\n    /// 获取服务器地址\n    pub async fn get_address_list(&self) -> Vec<SocketAddr> {\n        const BUILD_IN: [([u8; 4], u16); 6] = [\n            ([42, 81, 172, 81], 80),\n            ([114, 221, 148, 59], 14000),\n            ([42, 81, 172, 147], 443),\n            ([125, 94, 60, 146], 80),\n            ([114, 221, 144, 215], 80),\n            ([42, 81, 172, 22], 80),\n        ];\n        let mut addrs: Vec<_> = BUILD_IN.into_iter().map(SocketAddr::from).collect();\n        if let Ok(res) = tokio::net::lookup_host((\"msfwifi.3g.qq.com\", 8080)).await {\n            addrs.extend(res);\n        }\n        // TODO: src/client/processor/config_push_svc.rs\n        addrs\n    }\n\n    /// 获取网络状态\n    pub fn get_status(&self) -> u8 {\n        self.status.load(Ordering::Relaxed)\n    }\n\n    /// 开始处理流数据，阻塞当前 Task。该方法返回即为断线。\n    ///\n    /// **Notice: 该方法仅开始处理包，需要手动登录并开始心跳包**\n    pub async fn start(self: &Arc<Self>, stream: impl AsyncRead + AsyncWrite) {\n        self.status\n            .store(NetworkStatus::Running as u8, Ordering::Relaxed);\n        self.net_loop(stream).await; // 阻塞到断开\n        self.disconnect();\n        self.online.store(false, Ordering::Relaxed);\n\n        match self.status.compare_exchange(\n            NetworkStatus::Running as u8,\n            NetworkStatus::NetworkOffline as u8,\n            Ordering::Relaxed,\n            Ordering::Relaxed,\n        ) {\n            Ok(_) => {\n                self.handler\n                    .handle(QEvent::ClientDisconnect(ClientDisconnect {\n                        client: Arc::clone(self),\n                        inner: DisconnectReason::Network,\n                    }))\n                    .await;\n            }\n            Err(status) => {\n                self.handler\n                    .handle(QEvent::ClientDisconnect(ClientDisconnect {\n                        client: Arc::clone(self),\n                        inner: {\n                            let network = match status {\n                                0 => NetworkStatus::Unknown,\n                                1 => NetworkStatus::Running,\n                                2 => NetworkStatus::Stop,\n                                3 => NetworkStatus::Drop,\n                                5 => NetworkStatus::KickedOffline,\n                                6 => NetworkStatus::MsfOffline,\n                                _ => NetworkStatus::Unknown,\n                            };\n\n                            DisconnectReason::Actively(network)\n                        },\n                    }))\n                    .await;\n            }\n        }\n    }\n\n    pub fn stop(&self, status: NetworkStatus) {\n        self.disconnect();\n        self.status.store(status as u8, Ordering::Relaxed);\n        self.online.store(false, Ordering::Relaxed);\n    }\n\n    fn disconnect(&self) {\n        // TODO dispatch disconnect event\n        // don't unwrap (Err means there is no receiver.)\n        self.disconnect_signal.send(()).ok();\n    }\n\n    async fn net_loop(self: &Arc<Client>, stream: impl AsyncRead + AsyncWrite) {\n        let (mut write_half, mut read_half) = LengthDelimitedCodec::builder()\n            .length_field_length(4)\n            .length_adjustment(-4)\n            .new_framed(stream)\n            .split();\n        // 外发包 Channel Receiver\n        let mut rx = self.out_pkt_sender.subscribe();\n        let mut disconnect_signal = self.disconnect_signal.subscribe();\n        loop {\n            tokio::select! {\n                input = read_half.next() => {\n                    if let Some(Ok(mut input)) = input {\n                        if let Ok(pkt) = self.engine.read().await.transport.decode_packet(&mut input) {\n                            self.process_income_packet(pkt).await;\n                        } else {\n                            self.status.store(NetworkStatus::MsfOffline as u8, Ordering::Relaxed);\n                            break;\n                        }\n                    } else {\n                        break;\n                    }\n                }\n                output = rx.recv() => {\n                    if let Ok(output) = output && write_half.send(output).await.is_err() {\n                        break;\n                    }\n                }\n                _ = disconnect_signal.recv() => {\n                    break;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/c2c/friend_msg.rs",
    "content": "use cached::Cached;\nuse std::sync::Arc;\n\nuse ricq_core::msg::MessageChain;\nuse ricq_core::structs::{FriendAudio, FriendAudioMessage, FriendMessage};\nuse ricq_core::{pb, RQResult};\n\nuse crate::client::event::{FriendAudioMessageEvent, FriendMessageEvent};\nuse crate::handler::QEvent;\nuse crate::Client;\n\nimpl Client {\n    pub(crate) async fn process_friend_message(\n        self: &Arc<Self>,\n        mut msg: pb::msg::Message,\n    ) -> RQResult<()> {\n        fn take_ptt(msg: &mut pb::msg::Message) -> Option<pb::msg::Ptt> {\n            msg.body.as_mut()?.rich_text.as_mut()?.ptt.take()\n        }\n        if let Some(ptt) = take_ptt(&mut msg) {\n            // TODO self friend audio\n            self.handler\n                .handle(QEvent::FriendAudioMessage(FriendAudioMessageEvent {\n                    client: self.clone(),\n                    inner: parse_friend_audio_message(msg, ptt)?,\n                }))\n                .await;\n            return Ok(());\n        }\n\n        let message = parse_friend_message(msg)?;\n        if message.from_uin == self.uin().await {\n            if let Some(tx) = self\n                .receipt_waiters\n                .lock()\n                .await\n                .cache_remove(&message.rands.first().cloned().unwrap_or_default())\n            {\n                let _ = tx.send(message.seqs.first().cloned().unwrap_or_default());\n                return Ok(());\n            }\n        }\n        self.handler\n            .handle(QEvent::FriendMessage(FriendMessageEvent {\n                client: self.clone(),\n                inner: message,\n            }))\n            .await;\n        Ok(())\n    }\n}\n\npub fn parse_friend_message(msg: pb::msg::Message) -> RQResult<FriendMessage> {\n    let head = msg.head.unwrap();\n    Ok(FriendMessage {\n        seqs: vec![head.msg_seq()],\n        target: head.to_uin.unwrap(),\n        time: head.msg_time.unwrap(),\n        from_uin: head.from_uin.unwrap_or_default(),\n        from_nick: head.from_nick.unwrap_or_default(),\n        rands: vec![\n            if let Some(attr) = &msg.body.as_ref().unwrap().rich_text.as_ref().unwrap().attr {\n                attr.random()\n            } else {\n                0\n            },\n        ],\n        elements: MessageChain::from(msg.body.unwrap().rich_text.unwrap().elems), // todo ptt_store\n    })\n}\n\npub fn parse_friend_audio_message(\n    msg: pb::msg::Message,\n    ptt: pb::msg::Ptt,\n) -> RQResult<FriendAudioMessage> {\n    let head = msg.head.unwrap();\n    Ok(FriendAudioMessage {\n        seqs: vec![head.msg_seq()],\n        target: head.to_uin.unwrap(),\n        time: head.msg_time.unwrap(),\n        from_uin: head.from_uin.unwrap_or_default(),\n        from_nick: head.from_nick.unwrap_or_default(),\n        rands: vec![\n            if let Some(attr) = &msg.body.as_ref().unwrap().rich_text.as_ref().unwrap().attr {\n                attr.random()\n            } else {\n                0\n            },\n        ],\n        audio: FriendAudio(ptt),\n    })\n}\n"
  },
  {
    "path": "ricq/src/client/processor/c2c/friend_system_msg.rs",
    "content": "use crate::client::event::NewFriendRequestEvent;\nuse crate::handler::QEvent;\nuse crate::Client;\nuse ricq_core::command::profile_service::FriendSystemMessages;\nuse std::sync::Arc;\n\nimpl Client {\n    pub(crate) async fn process_friend_system_messages(\n        self: &Arc<Self>,\n        msgs: FriendSystemMessages,\n    ) {\n        for request in msgs.requests {\n            self.handler\n                .handle(QEvent::NewFriendRequest(NewFriendRequestEvent {\n                    client: self.clone(),\n                    inner: request,\n                }))\n                .await;\n        }\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/c2c/group_system_msg.rs",
    "content": "use std::sync::Arc;\n\nuse ricq_core::command::profile_service::GroupSystemMessages;\n\nuse crate::client::event::{JoinGroupRequestEvent, SelfInvitedEvent};\nuse crate::handler::QEvent;\nuse crate::Client;\n\nimpl Client {\n    pub(crate) async fn process_group_system_messages(self: &Arc<Self>, msgs: GroupSystemMessages) {\n        for request in msgs.self_invited.clone() {\n            if self\n                .self_invited_exists(request.msg_seq, request.msg_time)\n                .await\n            {\n                continue;\n            }\n            self.handler\n                .handle(QEvent::SelfInvited(SelfInvitedEvent {\n                    client: self.clone(),\n                    inner: request,\n                }))\n                .await;\n        }\n        for request in msgs.join_group_requests.clone() {\n            if self\n                .join_group_request_exists(request.msg_seq, request.msg_time)\n                .await\n            {\n                continue;\n            }\n            self.handler\n                .handle(QEvent::GroupRequest(JoinGroupRequestEvent {\n                    client: self.clone(),\n                    inner: request,\n                }))\n                .await;\n        }\n        let mut cache = self.group_sys_message_cache.write().await;\n        *cache = msgs\n    }\n\n    async fn self_invited_exists(&self, msg_seq: i64, msg_time: i64) -> bool {\n        if self.start_time > msg_time as i32 {\n            return true;\n        }\n        self.group_sys_message_cache\n            .read()\n            .await\n            .self_invited\n            .iter()\n            .any(|m| m.msg_seq == msg_seq)\n    }\n\n    async fn join_group_request_exists(&self, msg_seq: i64, msg_time: i64) -> bool {\n        if self.start_time > msg_time as i32 {\n            return true;\n        }\n        self.group_sys_message_cache\n            .read()\n            .await\n            .join_group_requests\n            .iter()\n            .any(|m| m.msg_seq == msg_seq)\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/c2c/mod.rs",
    "content": "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",
    "content": "use std::sync::Arc;\n\nuse ricq_core::common::group_uin2code;\nuse ricq_core::structs::NewMember;\nuse ricq_core::{pb, RQError, RQResult};\n\nuse crate::client::event::NewMemberEvent;\nuse crate::handler::QEvent;\nuse crate::Client;\n\nimpl Client {\n    pub(crate) async fn process_join_group(\n        self: &Arc<Self>,\n        msg: pb::msg::Message,\n    ) -> RQResult<()> {\n        let head = msg.head.ok_or(RQError::EmptyField(\"msg.head\"))?;\n        let group_code = group_uin2code(head.from_uin());\n        let member_uin = head.auth_uin();\n\n        self.handler\n            .handle(QEvent::NewMember(NewMemberEvent {\n                client: self.clone(),\n                inner: NewMember {\n                    group_code,\n                    member_uin,\n                },\n            }))\n            .await;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/c2c/temp_session.rs",
    "content": "use std::sync::Arc;\n\nuse ricq_core::msg::MessageChain;\nuse ricq_core::structs::GroupTempMessage;\nuse ricq_core::{pb, RQError, RQResult};\n\nuse crate::client::event::GroupTempMessageEvent;\nuse crate::handler::QEvent;\nuse crate::Client;\n\nimpl Client {\n    pub(crate) async fn process_temp_message(\n        self: &Arc<Self>,\n        msg: pb::msg::Message,\n    ) -> RQResult<()> {\n        let message = parse_temp_message(msg)?;\n        self.handler\n            .handle(QEvent::GroupTempMessage(GroupTempMessageEvent {\n                client: self.clone(),\n                inner: message,\n            }))\n            .await;\n        Ok(())\n    }\n}\n\npub fn parse_temp_message(msg: pb::msg::Message) -> RQResult<GroupTempMessage> {\n    let head = msg.head.unwrap();\n    let tmp_head = head\n        .c2c_tmp_msg_head\n        .ok_or(RQError::EmptyField(\"c2c_tmp_msg_head\"))?;\n\n    Ok(GroupTempMessage {\n        seqs: vec![head.msg_seq.unwrap_or_default()],\n        rands: vec![\n            if let Some(attr) = &msg.body.as_ref().unwrap().rich_text.as_ref().unwrap().attr {\n                attr.random()\n            } else {\n                0\n            },\n        ],\n        time: head.msg_time.unwrap(),\n        from_uin: head.from_uin.unwrap_or_default(),\n        from_nick: head.from_nick.unwrap_or_default(),\n        elements: MessageChain::from(msg.body.unwrap().rich_text.unwrap().elems), // todo ptt_store\n        group_code: tmp_head.group_code.unwrap_or_default(),\n    })\n}\n"
  },
  {
    "path": "ricq/src/client/processor/config_push_svc.rs",
    "content": "use std::time::Duration;\n\nuse bytes::Bytes;\n\nuse ricq_core::command::config_push_svc::ConfigPushBody;\nuse ricq_core::command::config_push_svc::ConfigPushReq;\nuse ricq_core::common::RQAddr;\n\nuse crate::client::tcp::sort_addrs;\nuse crate::client::Client;\nuse crate::RQError;\n\nimpl Client {\n    pub(crate) async fn process_config_push_req(\n        &self,\n        config_push_req: ConfigPushReq,\n    ) -> Result<(), RQError> {\n        // send response to server\n        let resp = config_push_req.resp;\n        let response = self.engine.read().await.build_conf_push_resp_packet(\n            resp.t,\n            resp.pkt_seq,\n            resp.jce_buf,\n        );\n        self.send(response).await?;\n        match config_push_req.body {\n            ConfigPushBody::Unknown => {}\n            ConfigPushBody::SsoServers { .. } => {}\n            ConfigPushBody::FileStorageInfo { info: _, rsp_body } => {\n                let mut session = self.highway_session.write().await;\n                if let Some(rsp_body) = rsp_body {\n                    session.sig_session = Bytes::from(rsp_body.sig_session.unwrap_or_default());\n                    session.session_key = Bytes::from(rsp_body.session_key.unwrap_or_default());\n                    session.uin = self.uin().await;\n                    session.app_id = self.engine.read().await.transport.version.app_id as i32;\n                    for addr in rsp_body.addrs.into_iter() {\n                        let service_type = addr.service_type.unwrap_or_default();\n                        if service_type == 10 {\n                            let addrs: Vec<RQAddr> = addr\n                                .addrs\n                                .into_iter()\n                                .map(|addr| {\n                                    RQAddr(\n                                        addr.ip.unwrap_or_default(),\n                                        addr.port.unwrap_or_default() as u16,\n                                    )\n                                })\n                                .collect();\n                            // 先写入，确保启动后可以快速使用\n                            self.highway_addrs.write().await.extend(addrs);\n                            // 去重，排序\n                            {\n                                let mut addrs = self.highway_addrs.read().await.clone();\n                                addrs.dedup_by(|a, b| (a.0 == b.0 && a.1 == b.1));\n                                let sorted_addrs = sort_addrs(addrs, Duration::from_secs(5)).await;\n                                let mut highway_addrs = self.highway_addrs.write().await;\n                                highway_addrs.clear();\n                                highway_addrs.extend(sorted_addrs);\n                            }\n                        } else if service_type == 11 {\n                            // TODO\n                        }\n                    }\n                }\n            }\n        }\n        // TODO process\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/message_svc.rs",
    "content": "use std::sync::Arc;\nuse std::time::UNIX_EPOCH;\n\nuse cached::Cached;\n\nuse ricq_core::{jce, pb};\n\nuse crate::client::event::KickedOfflineEvent;\nuse crate::client::{Client, NetworkStatus};\nuse crate::handler::QEvent;\n\nimpl Client {\n    pub(crate) async fn process_push_notify(self: &Arc<Self>, notify: jce::RequestPushNotify) {\n        match notify.msg_type {\n            35 | 36 | 37 | 45 | 46 | 84 | 85 | 86 | 87 => {\n                // pull group system msg(group request), then process\n                match self.get_all_group_system_messages().await {\n                    Ok(msgs) => {\n                        self.process_group_system_messages(msgs).await;\n                    }\n                    Err(err) => {\n                        tracing::warn!(\"failed to get group system message {}\", err);\n                    }\n                }\n            }\n            187..=191 => {\n                // pull friend system msg(friend request), then process\n                match self.get_friend_system_messages().await {\n                    Ok(msgs) => {\n                        self.process_friend_system_messages(msgs).await;\n                    }\n                    Err(err) => {\n                        tracing::warn!(\"failed to get friend system message {}\", err);\n                    }\n                }\n            }\n            _ => {\n                // TODO tracing.warn!()\n            }\n        }\n        // pull friend msg and other, then process\n        let all_message = self.sync_all_message().await;\n        match all_message {\n            Ok(msgs) => {\n                self.process_message_sync(msgs).await;\n            }\n            Err(err) => {\n                tracing::warn!(\"failed to sync message {}\", err);\n            }\n        }\n    }\n\n    pub(crate) async fn process_push_force_offline(\n        self: &Arc<Self>,\n        offline: jce::RequestPushForceOffline,\n    ) {\n        self.stop(NetworkStatus::KickedOffline);\n        self.handler\n            .handle(QEvent::KickedOffline(KickedOfflineEvent {\n                client: self.clone(),\n                inner: offline,\n            }))\n            .await;\n    }\n\n    pub(crate) async fn process_message_sync(self: &Arc<Self>, msgs: Vec<pb::msg::Message>) {\n        for msg in msgs {\n            let head = msg.head.clone().unwrap();\n            if self.msg_exists(&head).await {\n                continue;\n            }\n            match msg.head.as_ref().unwrap().msg_type() {\n                9 | 10 | 31 | 79 | 97 | 120 | 132 | 133 | 166 | 167 => {\n                    if let Err(err) = self.process_friend_message(msg).await {\n                        tracing::error!(\"failed to process friend message {err}\");\n                    }\n                }\n                33 => {\n                    if let Err(err) = self.process_join_group(msg).await {\n                        tracing::error!(\"failed to process join group {err}\");\n                    }\n                }\n                140 | 141 => {\n                    if let Err(err) = self.process_temp_message(msg).await {\n                        tracing::error!(\"failed to process temp message {err}\");\n                    }\n                }\n                208 => {\n                    // friend ptt_store\n                }\n                _ => tracing::warn!(\"unhandled sync message type\"),\n            }\n        }\n    }\n\n    async fn msg_exists(&self, head: &pb::msg::MessageHead) -> bool {\n        let now = UNIX_EPOCH.elapsed().unwrap().as_secs() as i32;\n        let msg_time = head.msg_time.unwrap_or_default();\n        if now - msg_time > 60 || self.start_time > msg_time {\n            return true;\n        }\n        let mut c2c_cache = self.c2c_cache.write().await;\n        let key = (\n            head.from_uin(),\n            head.to_uin(),\n            head.msg_seq(),\n            head.msg_uid(),\n        );\n        if c2c_cache.cache_get(&key).is_some() {\n            return true;\n        }\n        c2c_cache.cache_set(key, ());\n        if c2c_cache.cache_misses().unwrap_or_default() > 100 {\n            c2c_cache.flush();\n            c2c_cache.cache_reset_metrics();\n        }\n        false\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/mod.rs",
    "content": "use std::sync::Arc;\n\nuse bytes::Bytes;\n\nuse ricq_core::protocol::packet::Packet;\n\npub mod c2c;\npub mod config_push_svc;\npub mod message_svc;\npub mod online_push;\npub mod reg_prxy_svc;\npub mod stat_svc;\npub mod wtlogin;\n\nmacro_rules! log_error {\n    ($process: expr, $info: expr) => {\n        if let Err(e) = $process {\n            tracing::error!($info, e);\n        }\n    };\n}\n\nimpl super::Client {\n    /// 接收到的 Packet 统一分发\n    pub async fn process_income_packet(self: &Arc<Self>, pkt: Packet) {\n        tracing::trace!(\"received pkt: {}\", &pkt.command_name);\n        // response, send_and_wait 的包将会在此被截流\n        {\n            if let Some(sender) = self.packet_promises.write().await.remove(&pkt.seq_id) {\n                sender.send(pkt).unwrap();\n                return;\n            }\n        }\n\n        tracing::trace!(\"pkt: {} passed packet_promises\", &pkt.command_name);\n\n        {\n            if let Some(handler) = self.packet_handler.read().await.get(&pkt.command_name) {\n                let _ = handler.send(pkt.clone());\n            }\n        }\n\n        let cli = self.clone();\n        tokio::spawn(async move {\n            match pkt.command_name.as_ref() {\n                \"OnlinePush.PbPushGroupMsg\" => {\n                    let p = cli\n                        .engine\n                        .read()\n                        .await\n                        .decode_group_message_packet(pkt.body);\n                    match p {\n                        Ok(part) => {\n                            log_error!(\n                                cli.process_group_message_part(part).await,\n                                \"process_group_message_part error: {:?}\"\n                            )\n                        }\n                        Err(err) => {\n                            tracing::warn!(\"failed to decode [OnlinePush.PbPushGroupMsg]: {}\", err);\n                        }\n                    }\n                }\n                \"ConfigPushSvc.PushReq\" => {\n                    let req = cli.engine.read().await.decode_push_req_packet(pkt.body);\n                    match req {\n                        Ok(req) => {\n                            log_error!(\n                                cli.process_config_push_req(req).await,\n                                \"process_config_push_req error: {:?}\"\n                            )\n                        }\n                        Err(err) => {\n                            tracing::warn!(\"failed to decode [ConfigPushSvc.PushReq]: {}\", err);\n                        }\n                    }\n                }\n                \"RegPrxySvc.PushParam\" => {\n                    let other_clients = cli.engine.read().await.decode_push_param_packet(&pkt.body);\n                    match other_clients {\n                        Ok(other_clients) => {\n                            log_error!(\n                                cli.process_push_param(other_clients).await,\n                                \"process_push_param error: {:?}\"\n                            )\n                        }\n                        Err(err) => {\n                            tracing::warn!(\"failed to decode [RegPrxySvc.PushParam]: {}\", err);\n                        }\n                    }\n                }\n                \"MessageSvc.PushNotify\" => {\n                    // c2c流程：\n                    // 1. Server 发送 PushNotify 到 Client, 表示有通知需要 Client 拉取 (不带具体内容)\n                    // 2. Client 根据 msg_type 发送请求拉取具体通知内容\n                    // 类型：好友申请、群申请、私聊消息、其他?\n                    let resp = cli.engine.read().await.decode_svc_notify(pkt.body);\n                    match resp {\n                        Ok(notify) => {\n                            cli.process_push_notify(notify).await;\n                        }\n                        Err(err) => {\n                            tracing::warn!(\"failed to decode [MessageSvc.PushNotify]: {}\", err);\n                        }\n                    }\n                }\n                \"OnlinePush.ReqPush\" => {\n                    let resp = cli\n                        .engine\n                        .read()\n                        .await\n                        .decode_online_push_req_packet(pkt.body);\n                    match resp {\n                        Ok(resp) => {\n                            log_error!(\n                                cli.delete_online_push(\n                                    resp.uin,\n                                    0,\n                                    Bytes::new(),\n                                    pkt.seq_id as u16,\n                                    resp.msg_infos.clone(),\n                                )\n                                .await,\n                                \"delete_online_push error: {:?}\"\n                            );\n                            cli.process_push_req(resp.msg_infos).await;\n                        }\n                        Err(err) => {\n                            tracing::warn!(\"failed to decode [OnlinePush.ReqPush]: {}\", err);\n                        }\n                    }\n                }\n                \"OnlinePush.PbPushTransMsg\" => {\n                    let online_push_trans = cli\n                        .engine\n                        .read()\n                        .await\n                        .decode_online_push_trans_packet(pkt.body);\n                    match online_push_trans {\n                        Ok(online_push_trans) => {\n                            cli.process_push_trans(online_push_trans).await;\n                        }\n                        Err(err) => {\n                            tracing::warn!(\"failed to decode [OnlinePush.PbPushTransMsg]: {}\", err);\n                        }\n                    }\n                }\n                \"MessageSvc.PushForceOffline\" => {\n                    let offline = cli.engine.read().await.decode_force_offline(pkt.body);\n                    match offline {\n                        Ok(offline) => {\n                            cli.process_push_force_offline(offline).await;\n                        }\n                        Err(err) => {\n                            tracing::warn!(\n                                \"failed to decode [MessageSvc.PushForceOffline]: {}\",\n                                err\n                            );\n                        }\n                    }\n                }\n                \"StatSvc.ReqMSFOffline\" => {\n                    let offline = cli.engine.read().await.decode_msf_force_offline(pkt.body);\n                    match offline {\n                        Ok(offline) => {\n                            cli.process_msf_force_offline(offline).await;\n                        }\n                        Err(err) => {\n                            tracing::warn!(\"failed to decode [StatSvc.ReqMSFOffline]: {}\", err);\n                        }\n                    }\n                }\n                \"OnlinePush.PbC2CMsgSync\" => {\n                    // 其他设备发送消息，同步\n                    let push = cli.engine.read().await.decode_c2c_sync_packet(pkt.body);\n                    match push {\n                        Ok(push) => {\n                            log_error!(\n                                cli.process_c2c_sync(pkt.seq_id, push).await,\n                                \"process_c2c_sync error: {:?}\"\n                            )\n                        }\n                        Err(err) => {\n                            tracing::warn!(\"failed to decode [OnlinePush.PbC2CMsgSync]: {}\", err);\n                        }\n                    }\n                }\n                \"OnlinePush.SidTicketExpired\" => {\n                    log_error!(\n                        cli.process_sid_ticket_expired(pkt.seq_id).await,\n                        \"process_sid_ticket_expired error: {:?}\"\n                    )\n                }\n                \"RegPrxySvc.GetMsgV2\"\n                | \"RegPrxySvc.PbGetMsg\"\n                | \"RegPrxySvc.NoticeEnd\"\n                | \"MessageSvc.PushReaded\" => {\n                    tracing::trace!(\"ignore pkt: {}\", &pkt.command_name);\n                }\n                _ => {\n                    tracing::debug!(\"unhandled pkt: {}\", &pkt.command_name);\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/online_push.rs",
    "content": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse bytes::{Buf, Bytes};\nuse cached::Cached;\n\nuse prost::Message;\nuse ricq_core::command::online_push::GroupMessagePart;\nuse ricq_core::command::online_push::{OnlinePushTrans, PushTransInfo};\nuse ricq_core::msg::MessageChain;\nuse ricq_core::structs::{\n    DeleteFriend, FriendInfo, FriendMessageRecall, FriendPoke, GroupAudio, GroupAudioMessage,\n    GroupLeave, GroupMessage, GroupMessageRecall, GroupMute, GroupNameUpdate, GroupPoke,\n};\nuse ricq_core::{jce, pb};\n\nuse crate::client::event::{\n    DeleteFriendEvent, FriendMessageRecallEvent, FriendPokeEvent, GroupAudioMessageEvent,\n    GroupDisbandEvent, GroupLeaveEvent, GroupMessageEvent, GroupMessageRecallEvent, GroupMuteEvent,\n    GroupNameUpdateEvent, GroupPokeEvent, MemberPermissionChangeEvent, NewFriendEvent,\n};\nuse crate::client::handler::QEvent;\nuse crate::client::Client;\nuse crate::RQResult;\n\nimpl Client {\n    pub(crate) async fn process_group_message_part(\n        self: &Arc<Self>,\n        group_message_part: GroupMessagePart,\n    ) -> RQResult<()> {\n        // receipt message\n        if group_message_part.from_uin == self.uin().await {\n            if let Some(tx) = self\n                .receipt_waiters\n                .lock()\n                .await\n                .cache_remove(&group_message_part.rand)\n            {\n                let _ = tx.send(group_message_part.seq);\n                return Ok(());\n            }\n        }\n\n        if let Some(ptt) = group_message_part.ptt {\n            self.handler\n                .handle(QEvent::GroupAudioMessage(GroupAudioMessageEvent {\n                    client: self.clone(),\n                    inner: GroupAudioMessage {\n                        seqs: vec![group_message_part.seq],\n                        rands: vec![group_message_part.rand],\n                        group_code: group_message_part.group_code,\n                        group_name: group_message_part.group_name,\n                        group_card: group_message_part.group_card,\n                        from_uin: group_message_part.from_uin,\n                        time: group_message_part.time,\n                        audio: GroupAudio(ptt),\n                    },\n                }))\n                .await;\n            return Ok(());\n        }\n\n        // merge parts\n        let pkg_num = group_message_part.pkg_num;\n        let group_msg = if pkg_num > 1 {\n            let mut builder = self.group_message_builder.write().await;\n            if builder.cache_misses().unwrap_or_default() > 100 {\n                builder.flush();\n                builder.cache_reset_metrics();\n            }\n            // muti-part\n            let div_seq = group_message_part.div_seq;\n            let parts = builder.cache_get_or_set_with(div_seq, Vec::new);\n            parts.push(group_message_part);\n            if parts.len() < pkg_num as usize {\n                // wait for more parts\n                None\n            } else {\n                Some(builder.cache_remove(&div_seq).unwrap_or_default())\n            }\n        } else {\n            // single-part\n            Some(vec![group_message_part])\n        };\n\n        // handle message\n        if let Some(group_msg) = group_msg {\n            // message is finish\n            self.handler\n                .handle(QEvent::GroupMessage(GroupMessageEvent {\n                    client: self.clone(),\n                    inner: self.parse_group_message(group_msg).await?,\n                }))\n                .await; //todo\n        }\n        Ok(())\n    }\n\n    pub(crate) async fn parse_group_message(\n        &self,\n        mut parts: Vec<GroupMessagePart>,\n    ) -> RQResult<GroupMessage> {\n        parts.sort_by(|a, b| a.pkg_index.cmp(&b.pkg_index));\n\n        let group_code = parts.first().map(|p| p.group_code).unwrap_or_default();\n        let group_name = parts\n            .first_mut()\n            .map(|p| std::mem::take(&mut p.group_name))\n            .unwrap_or_default();\n        let group_card = parts\n            .first_mut()\n            .map(|p| std::mem::take(&mut p.group_card))\n            .unwrap_or_default();\n        let from_uin = parts.first().map(|p| p.from_uin).unwrap_or_default();\n        let time = parts.first().map(|p| p.time).unwrap_or_default();\n\n        let mut seqs = Vec::with_capacity(parts.len());\n        let mut rands = Vec::with_capacity(parts.len());\n        let mut elements = Vec::with_capacity(6); // number by experience\n        for p in parts {\n            seqs.push(p.seq);\n            rands.push(p.rand);\n            elements.extend(p.elems.into_iter().filter_map(|e| e.elem));\n        }\n        // dbg!(elements.len()); // most of message will be 4, complex message like share card is 5\n\n        Ok(GroupMessage {\n            seqs,\n            rands,\n            group_code,\n            group_name,\n            group_card,\n            from_uin,\n            time,\n            elements: MessageChain(elements),\n        })\n\n        // TODO: extInfo\n        // TODO: group_card_update\n        // TODO: ptt_store\n    }\n\n    pub(crate) async fn process_push_req(self: &Arc<Self>, msg_infos: Vec<jce::PushMessageInfo>) {\n        for info in msg_infos {\n            if self.push_req_exists(&info).await {\n                continue;\n            }\n            match info.msg_type {\n                732 => {\n                    let mut r = info.v_msg;\n                    let group_code = r.get_u32() as i64;\n                    let i_type = r.get_u8();\n                    r.get_u8();\n                    match i_type {\n                        0x0c => {\n                            let operator = r.get_u32() as i64;\n                            if operator == self.uin().await {\n                                continue;\n                            }\n                            r.advance(6);\n                            let target = r.get_u32() as i64;\n                            let duration = Duration::from_secs(r.get_u32() as u64);\n                            self.handler\n                                .handle(QEvent::GroupMute(GroupMuteEvent {\n                                    client: self.clone(),\n                                    inner: GroupMute {\n                                        group_code,\n                                        operator_uin: operator,\n                                        target_uin: target,\n                                        duration,\n                                    },\n                                }))\n                                .await;\n                        }\n                        0x10 | 0x11 | 0x14 | 0x15 => {\n                            // group notify msg\n                            r.advance(1);\n                            let b = pb::notify::NotifyMsgBody::decode(&*r).unwrap();\n                            if let Some(opt_msg_recall) = b.opt_msg_recall {\n                                let operator_uin = opt_msg_recall.uin;\n                                // use map iterator here will produce massive asm code\n                                for rm in opt_msg_recall.recalled_msg_list {\n                                    if rm.msg_type == 2 {\n                                        continue;\n                                    }\n                                    self.handler\n                                        .handle(QEvent::GroupMessageRecall(\n                                            GroupMessageRecallEvent {\n                                                client: self.clone(),\n                                                inner: GroupMessageRecall {\n                                                    msg_seq: rm.seq,\n                                                    group_code,\n                                                    operator_uin,\n                                                    author_uin: rm.author_uin,\n                                                    time: rm.time,\n                                                },\n                                            },\n                                        ))\n                                        .await;\n                                }\n                            }\n\n                            if let Some(t) = b.opt_general_gray_tip {\n                                let mut sender: i64 = 0;\n                                let mut receiver: i64 = 0;\n                                for templ in t.msg_templ_param {\n                                    match &*templ.name {\n                                        \"uin_str1\" => {\n                                            sender = templ.value.parse().unwrap_or_default()\n                                        }\n                                        \"uin_str2\" => {\n                                            receiver = templ.value.parse().unwrap_or_default()\n                                        }\n                                        _ => {}\n                                    }\n                                }\n                                if sender != 0 {\n                                    self.handler\n                                        .handle(QEvent::GroupPoke(GroupPokeEvent {\n                                            client: self.clone(),\n                                            inner: GroupPoke {\n                                                group_code,\n                                                sender,\n                                                receiver,\n                                            },\n                                        }))\n                                        .await;\n                                }\n                            }\n                            // TODO 一些没什么用的 event 暂时没写\n                        }\n                        _ => {}\n                    }\n                }\n                528 => {\n                    let mut v_msg = info.v_msg;\n                    let msg: jce::MsgType0x210 = jcers::from_buf(&mut v_msg).unwrap();\n                    match msg.sub_msg_type {\n                        0x8A | 0x8B => {\n                            let s8a = pb::Sub8A::decode(&*msg.v_protobuf).unwrap();\n                            for m in s8a.msg_info {\n                                self.handler\n                                    .handle(QEvent::FriendMessageRecall(FriendMessageRecallEvent {\n                                        client: self.clone(),\n                                        inner: FriendMessageRecall {\n                                            msg_seq: m.msg_seq,\n                                            friend_uin: m.from_uin,\n                                            time: m.msg_time,\n                                        },\n                                    }))\n                                    .await;\n                            }\n                        }\n                        0xB3 => {\n                            let msg_add_frd_notify = pb::SubB3::decode(&*msg.v_protobuf).unwrap();\n                            if let Some(f) = msg_add_frd_notify.msg_add_frd_notify {\n                                self.handler\n                                    .handle(QEvent::NewFriend(NewFriendEvent {\n                                        client: self.clone(),\n                                        inner: FriendInfo {\n                                            uin: f.uin,\n                                            nick: f.nick,\n                                            ..Default::default()\n                                        },\n                                    }))\n                                    .await;\n                            }\n                        }\n                        0xD4 => {\n                            let d4 = pb::SubD4::decode(&*msg.v_protobuf).unwrap();\n                            self.handler\n                                .handle(QEvent::GroupLeave(GroupLeaveEvent {\n                                    client: self.clone(),\n                                    inner: GroupLeave {\n                                        group_code: d4.uin,\n                                        member_uin: self.uin().await,\n                                        operator_uin: None,\n                                    },\n                                }))\n                                .await;\n                        }\n                        0x122 | 0x123 => {\n                            let t =\n                                pb::notify::GeneralGrayTipInfo::decode(&*msg.v_protobuf).unwrap();\n                            let mut sender: i64 = 0;\n                            let mut receiver: i64 = 0;\n                            for templ in t.msg_templ_param {\n                                if templ.name == \"uin_str1\" {\n                                    sender = templ.value.parse().unwrap_or_default()\n                                } else if templ.name == \"uin_str2\" {\n                                    receiver = templ.value.parse().unwrap_or_default()\n                                }\n                            }\n                            if sender != 0 {\n                                self.handler\n                                    .handle(QEvent::FriendPoke(FriendPokeEvent {\n                                        client: self.clone(),\n                                        inner: FriendPoke { sender, receiver },\n                                    }))\n                                    .await;\n                            }\n                        }\n                        0x27 => {\n                            let s27 =\n                                pb::msgtype0x210::SubMsg0x27Body::decode(&*msg.v_protobuf).unwrap();\n                            for mod_info in s27.mod_infos {\n                                if let Some(mod_group_profile) = mod_info.mod_group_profile {\n                                    for profile_info in mod_group_profile.group_profile_infos {\n                                        if profile_info.field.unwrap_or_default() != 1 {\n                                            continue;\n                                        }\n                                        self.handler\n                                            .handle(QEvent::GroupNameUpdate(GroupNameUpdateEvent {\n                                                client: self.clone(),\n                                                inner: GroupNameUpdate {\n                                                    group_code: mod_group_profile\n                                                        .group_code\n                                                        .unwrap_or_default()\n                                                        as i64,\n                                                    operator_uin: mod_group_profile\n                                                        .cmd_uin\n                                                        .unwrap_or_default()\n                                                        as i64,\n                                                    group_name: String::from_utf8_lossy(\n                                                        profile_info.value(),\n                                                    )\n                                                    .into_owned(),\n                                                },\n                                            }))\n                                            .await;\n                                    }\n                                }\n                                if let Some(del_friend) = mod_info.del_friend {\n                                    for uin in del_friend.uins {\n                                        self.handler\n                                            .handle(QEvent::DeleteFriend(DeleteFriendEvent {\n                                                client: self.clone(),\n                                                inner: DeleteFriend { uin: uin as i64 },\n                                            }))\n                                            .await;\n                                    }\n                                }\n                            }\n                        }\n                        0x44 => {\n                            // group sync\n                            // friend sync\n                        }\n                        _ => {}\n                    }\n                }\n                _ => {}\n            }\n        }\n    }\n\n    async fn push_req_exists(&self, info: &jce::PushMessageInfo) -> bool {\n        let msg_time = info.msg_time as i32; // 可能是0，不过滤\n        if msg_time != 0 && self.start_time > msg_time {\n            return true;\n        }\n        let mut push_req_cache = self.push_req_cache.write().await;\n        let key = (info.msg_seq, info.msg_uid);\n        if push_req_cache.cache_get(&key).is_some() {\n            return true;\n        }\n        push_req_cache.cache_set(key, ());\n        if push_req_cache.cache_misses().unwrap_or_default() > 10 {\n            push_req_cache.flush();\n            push_req_cache.cache_reset_metrics();\n        }\n        false\n    }\n\n    pub(crate) async fn process_push_trans(self: &Arc<Self>, push_trans: OnlinePushTrans) {\n        if self.push_trans_exists(&push_trans).await {\n            return;\n        }\n        match push_trans.info {\n            PushTransInfo::MemberLeave(leave) => {\n                self.handler\n                    .handle(QEvent::GroupLeave(GroupLeaveEvent {\n                        client: self.clone(),\n                        inner: leave,\n                    }))\n                    .await;\n            }\n            PushTransInfo::MemberPermissionChange(change) => {\n                self.handler\n                    .handle(QEvent::MemberPermissionChange(\n                        MemberPermissionChangeEvent {\n                            client: self.clone(),\n                            inner: change,\n                        },\n                    ))\n                    .await;\n            }\n            PushTransInfo::GroupDisband(disband) => {\n                self.handler\n                    .handle(QEvent::GroupDisband(GroupDisbandEvent {\n                        client: self.clone(),\n                        inner: disband,\n                    }))\n                    .await;\n            }\n        }\n    }\n\n    async fn push_trans_exists(&self, info: &OnlinePushTrans) -> bool {\n        let msg_time = info.msg_time;\n        if self.start_time > msg_time {\n            return true;\n        }\n        let mut push_trans_cache = self.push_trans_cache.write().await;\n        let key = (info.msg_seq, info.msg_uid);\n        if push_trans_cache.cache_get(&key).is_some() {\n            return true;\n        }\n        push_trans_cache.cache_set(key, ());\n        if push_trans_cache.cache_misses().unwrap_or_default() > 10 {\n            push_trans_cache.flush();\n            push_trans_cache.cache_reset_metrics();\n        }\n        false\n    }\n\n    pub(crate) async fn process_c2c_sync(\n        self: &Arc<Self>,\n        pkt_seq: i32,\n        push: pb::msg::PbPushMsg,\n    ) -> RQResult<()> {\n        let req = self.engine.read().await.build_delete_online_push_packet(\n            self.uin().await,\n            push.svrip(),\n            Bytes::from(push.push_token.unwrap_or_default()),\n            pkt_seq as u16,\n            vec![],\n        );\n        let _ = self.send(req).await?;\n        if let Some(msg) = push.msg {\n            self.process_message_sync(vec![msg]).await;\n        }\n        Ok(())\n    }\n\n    pub(crate) async fn process_sid_ticket_expired(self: &Arc<Self>, seq: i32) -> RQResult<()> {\n        self.request_change_sig(Some(3554528)).await?;\n        self.register_client().await?;\n        self.send_sid_ticket_expired_response(seq).await?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/reg_prxy_svc.rs",
    "content": "use crate::client::{Client, OtherClientInfo};\nuse crate::RQError;\n\n// use crate::client::income::decoder::online_push::GroupMessagePart;\n\nimpl Client {\n    pub(crate) async fn process_push_param(\n        &self,\n        other_clients: Vec<OtherClientInfo>,\n    ) -> Result<(), RQError> {\n        tracing::debug!(\"{:?}\", other_clients);\n        // TODO merge part and dispatch to handler\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/stat_svc.rs",
    "content": "use std::sync::Arc;\n\nuse ricq_core::jce;\n\nuse crate::client::event::MSFOfflineEvent;\nuse crate::client::{Client, NetworkStatus};\nuse crate::handler::QEvent;\n\nimpl Client {\n    // TODO 待测试\n    pub(crate) async fn process_msf_force_offline(\n        self: &Arc<Self>,\n        offline: jce::RequestMSFForceOffline,\n    ) {\n        self.send_msg_offline_rsp(offline.uin, offline.seq_no)\n            .await\n            .ok();\n        self.stop(NetworkStatus::MsfOffline);\n        self.handler\n            .handle(QEvent::MSFOffline(MSFOfflineEvent {\n                client: self.clone(),\n                inner: offline,\n            }))\n            .await;\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/processor/wtlogin.rs",
    "content": "use crate::handler::QEvent;\nuse crate::Client;\nuse ricq_core::command::wtlogin::*;\n\nimpl Client {\n    pub(crate) async fn process_login_response(&self, login_response: &LoginResponse) {\n        if let LoginResponse::Success(ref success) = login_response {\n            if let Some(info) = &success.account_info {\n                let mut account_info = self.account_info.write().await;\n                account_info.nickname = info.nick.clone();\n                account_info.age = info.age;\n                account_info.gender = info.gender;\n            }\n        }\n        self.engine\n            .write()\n            .await\n            .process_login_response(login_response);\n        self.handler.handle(QEvent::Login(self.uin().await)).await;\n    }\n\n    pub(crate) async fn process_trans_emp_response(&self, qrcode_state: &QRCodeState) {\n        if let QRCodeState::Confirmed(resp) = qrcode_state {\n            self.engine.write().await.process_qrcode_confirmed(resp);\n        }\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/qimei.rs",
    "content": "use rand::{CryptoRng, RngCore};\nuse ricq_core::protocol::device::Device;\nuse ricq_core::protocol::qimei::{Qimei, QimeiRequest, QimeiResponse};\nuse ricq_core::protocol::version::Version;\nuse ricq_core::{RQError, RQResult};\n\npub async fn get_qimei<RNG: RngCore + CryptoRng>(\n    rng: &mut RNG,\n    device: &Device,\n    version: &Version,\n) -> RQResult<Qimei> {\n    let crypt_key = \"0123456789abcdef\".as_bytes();\n    let req = QimeiRequest::new(rng, device, version, crypt_key).unwrap();\n    let resp: QimeiResponse = reqwest::Client::new()\n        .post(\"https://snowflake.qq.com/ola/android\")\n        .json(&req)\n        .send()\n        .await\n        .map_err(|e| RQError::Other(e.to_string()))?\n        .json()\n        .await\n        .map_err(|e| RQError::Other(e.to_string()))?;\n    resp.to_payload(crypt_key).map_err(Into::into)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use ricq_core::protocol::version::ANDROID_PHONE;\n\n    #[tokio::test]\n    async fn test_qimei() {\n        use rand::SeedableRng;\n        let mut rng = rand::rngs::StdRng::seed_from_u64(1239123240);\n        let device = Device::random_with_rng(&mut rng);\n        let version = ANDROID_PHONE;\n        let resp = get_qimei(&mut rng, &device, &version).await.unwrap();\n        println!(\"{resp:?}\")\n    }\n}\n"
  },
  {
    "path": "ricq/src/client/tcp.rs",
    "content": "use std::net::SocketAddr;\nuse std::time::Duration;\n\nuse tokio::net::TcpStream;\nuse tokio::task::JoinSet;\n\npub async fn tcp_connect_timeout(\n    addr: SocketAddr,\n    timeout: Duration,\n) -> tokio::io::Result<TcpStream> {\n    let conn = tokio::net::TcpStream::connect(addr);\n    tokio::time::timeout(timeout, conn)\n        .await\n        .map_err(tokio::io::Error::from)\n        .flatten()\n}\n\n/// Race the given address, call `join_set.join_next()` to get next fastest `(addr, conn)` pair.\nasync fn race_addrs(\n    addrs: Vec<SocketAddr>,\n    timeout: Duration,\n) -> JoinSet<tokio::io::Result<(SocketAddr, TcpStream)>> {\n    let mut join_set = JoinSet::new();\n    for addr in addrs {\n        join_set.spawn(async move {\n            let a = addr;\n            tcp_connect_timeout(addr, timeout).await.map(|s| (a, s))\n        });\n    }\n    join_set\n}\n\npub async fn sort_addrs<Addr>(addrs: Vec<Addr>, timeout: Duration) -> Vec<Addr>\nwhere\n    SocketAddr: From<Addr>,\n    Addr: From<SocketAddr>,\n{\n    let mut join_set = race_addrs(addrs.into_iter().map(Into::into).collect(), timeout).await;\n    let mut ret = Vec::new();\n    while let Some(result) = join_set.join_next().await {\n        if let Ok(Ok((addr, _))) = result {\n            ret.push(addr.into());\n        }\n    }\n    ret\n}\n\npub async fn tcp_connect_fastest(\n    addrs: Vec<SocketAddr>,\n    timeout: Duration,\n) -> tokio::io::Result<TcpStream> {\n    let mut join_set = race_addrs(addrs, timeout).await;\n    while let Some(result) = join_set.join_next().await {\n        if let Ok(Ok((_, stream))) = result {\n            return Ok(stream);\n        }\n    }\n    Err(tokio::io::Error::new(\n        tokio::io::ErrorKind::NotConnected,\n        \"NotConnected\",\n    ))\n}\n\n#[cfg(test)]\nmod tests {\n    use std::net::Ipv4Addr;\n\n    use super::*;\n\n    #[tokio::test]\n    async fn test_sort() {\n        let addrs = vec![\n            SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 80),\n            SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 8000),\n        ];\n        let out = race_addrs(addrs.clone(), Duration::from_secs(10)).await;\n        println!(\"{out:?}\");\n        let str = tcp_connect_fastest(addrs, Duration::from_secs(10)).await;\n        println!(\"{str:?}\");\n    }\n}\n"
  },
  {
    "path": "ricq/src/config.rs",
    "content": "use std::fmt::Debug;\n\nuse ricq_core::protocol::{\n    device::Device,\n    version::Version,\n    version::{get_version, Protocol},\n};\n\n#[derive(Debug)]\npub struct Config {\n    pub device: Device,\n    pub version: Version,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            device: Device::random(),\n            version: get_version(Protocol::IPad),\n        }\n    }\n}\n\nimpl Config {\n    pub fn new(device: Device, version: Version) -> Self {\n        Self { device, version }\n    }\n}\n"
  },
  {
    "path": "ricq/src/ext/common.rs",
    "content": "use std::sync::atomic::Ordering;\nuse std::sync::Arc;\n\nuse crate::Client;\n\n/// 登录后必须执行的操作\npub async fn after_login(client: &Arc<Client>) {\n    if let Err(err) = client.register_client().await {\n        tracing::error!(\"failed to register client: {}\", err)\n    }\n    start_heartbeat(client.clone()).await;\n    if let Err(err) = client.refresh_status().await {\n        tracing::error!(\"failed to refresh status: {}\", err)\n    }\n}\n\n/// 如果当前启动心跳，spawn 开始心跳\npub async fn start_heartbeat(client: Arc<Client>) {\n    if !client.heartbeat_enabled.load(Ordering::Relaxed) {\n        tokio::spawn(async move {\n            client.do_heartbeat().await;\n        });\n    }\n}\n"
  },
  {
    "path": "ricq/src/ext/image.rs",
    "content": "use std::future::Future;\nuse std::pin::Pin;\n\nuse ricq_core::command::img_store::GroupImageStoreResp;\nuse ricq_core::command::long_conn::OffPicUpResp;\nuse ricq_core::highway::BdhInput;\nuse ricq_core::msg::elem::{FriendImage, GroupImage};\nuse ricq_core::{RQError, RQResult};\n\nuse crate::structs::ImageInfo;\nuse crate::Client;\n\npub async fn upload_group_image_ext<F>(\n    cli: &Client,\n    group_code: i64,\n    image_info: ImageInfo,\n    f: F,\n) -> RQResult<GroupImage>\nwhere\n    F: for<'a> FnOnce(\n        &'a ImageInfo,\n    ) -> Pin<Box<dyn Future<Output = RQResult<Vec<u8>>> + Send + 'a>>,\n{\n    let sign = cli.get_highway_session_key().await;\n    let group_image = match cli.get_group_image_store(group_code, &image_info).await? {\n        GroupImageStoreResp::Exist { file_id, addrs } => {\n            image_info.into_group_image(file_id, addrs.first().cloned().unwrap_or_default(), sign)\n        }\n        GroupImageStoreResp::NotExist {\n            file_id,\n            upload_key,\n            mut upload_addrs,\n        } => {\n            let data = f(&image_info).await?;\n            let addr = upload_addrs\n                .pop()\n                .ok_or(RQError::EmptyField(\"upload_addrs\"))?;\n            cli.highway_upload_bdh(\n                addr.into(),\n                BdhInput {\n                    command_id: 2,\n                    ticket: upload_key,\n                    ext: vec![],\n                    encrypt: false,\n                    chunk_size: 256 * 1024,\n                    send_echo: true,\n                },\n                &data,\n            )\n            .await\n            .map(|_| image_info.into_group_image(file_id, addr, sign))?\n        }\n    };\n    Ok(group_image)\n}\n\npub async fn upload_friend_image_ext<F>(\n    cli: &Client,\n    target: i64,\n    image_info: ImageInfo,\n    f: F,\n) -> RQResult<FriendImage>\nwhere\n    F: for<'a> FnOnce(\n        &'a ImageInfo,\n    ) -> Pin<Box<dyn Future<Output = RQResult<Vec<u8>>> + Send + 'a>>,\n{\n    let friend_image = match cli.get_off_pic_store(target, &image_info).await? {\n        OffPicUpResp::Exist { res_id, uuid } => image_info.into_friend_image(res_id, uuid),\n        OffPicUpResp::UploadRequired {\n            res_id,\n            uuid,\n            upload_key,\n            mut upload_addrs,\n        } => {\n            let data = f(&image_info).await?;\n            let addr = upload_addrs\n                .pop()\n                .ok_or(RQError::EmptyField(\"upload_addrs\"))?;\n            cli.highway_upload_bdh(\n                addr.into(),\n                BdhInput {\n                    command_id: 1,\n                    ticket: upload_key,\n                    ext: vec![],\n                    encrypt: false,\n                    chunk_size: 256 * 1024,\n                    send_echo: true,\n                },\n                &data,\n            )\n            .await\n            .map(|_| image_info.into_friend_image(res_id, uuid))?\n        }\n    };\n    Ok(friend_image)\n}\n"
  },
  {
    "path": "ricq/src/ext/login.rs",
    "content": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse ricq_core::command::wtlogin::{LoginResponse, QRCodeConfirmed, QRCodeState};\nuse ricq_core::{RQError, RQResult};\n\nuse crate::Client;\n\n/// 扫码登录：自动查询二维码状态，忽略中间结果，成功或失败返回\npub async fn auto_query_qrcode(client: &Arc<Client>, sig: &[u8]) -> RQResult<()> {\n    loop {\n        tokio::time::sleep(Duration::from_secs(1)).await;\n        let qrcode_state = client.query_qrcode_result(sig).await?;\n        match qrcode_state {\n            QRCodeState::Timeout => return Err(RQError::Timeout),\n            QRCodeState::Canceled => return Err(RQError::Other(\"canceled\".into())),\n            QRCodeState::Confirmed(QRCodeConfirmed {\n                ref tmp_pwd,\n                ref tmp_no_pic_sig,\n                ref tgt_qr,\n                ..\n            }) => {\n                let login_resp = client.qrcode_login(tmp_pwd, tmp_no_pic_sig, tgt_qr).await?;\n                return match login_resp {\n                    LoginResponse::Success { .. } => Ok(()),\n                    LoginResponse::DeviceLockLogin { .. } => {\n                        match client.device_lock_login().await? {\n                            LoginResponse::Success { .. } => Ok(()),\n                            other => Err(RQError::Other(format!(\n                                \"device_lock_login failed {other:?}\"\n                            ))),\n                        }\n                    }\n                    other => Err(RQError::Other(format!(\"invalid login resp: {other:?}\"))),\n                };\n            }\n            _ => {\n                // do nothing\n            }\n        }\n        tokio::time::sleep(Duration::from_secs(4)).await;\n    }\n}\n"
  },
  {
    "path": "ricq/src/ext/mod.rs",
    "content": "pub mod common;\npub mod image;\npub mod login;\npub mod reconnect;\n"
  },
  {
    "path": "ricq/src/ext/reconnect.rs",
    "content": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse async_trait::async_trait;\nuse tokio::io::{AsyncRead, AsyncWrite};\n\nuse ricq_core::command::wtlogin::LoginResponse;\n\nuse crate::client::net::Connector;\nuse crate::client::NetworkStatus;\nuse crate::ext::common::after_login;\nuse crate::{Client, RQError, RQResult};\n\n/// 自动重连，在掉线后使用，会阻塞到重连结束\npub async fn auto_reconnect<T: AsyncRead + AsyncWrite + 'static + Send>(\n    client: Arc<Client>,\n    credential: Credential,\n    interval: Duration,\n    max: usize,\n    connector: impl Connector<T>,\n) {\n    let mut count = 0;\n    loop {\n        // 如果不是网络原因掉线，不重连（服务端强制下线/被踢下线/用户手动停止）\n        if client.get_status() != (NetworkStatus::NetworkOffline as u8) {\n            tracing::warn!(\n                \"client status: {}, auto_reconnect break\",\n                client.get_status()\n            );\n            break;\n        }\n        client.stop(NetworkStatus::NetworkOffline);\n        tracing::error!(\"client will reconnect after {} seconds\", interval.as_secs());\n        tokio::time::sleep(interval).await;\n        let stream = if let Ok(stream) = connector.connect(&client).await {\n            count = 0;\n            stream\n        } else {\n            count += 1;\n            if count > max {\n                tracing::error!(\"reconnect_count: {}, break!\", count);\n                break;\n            }\n            continue;\n        };\n        let c = client.clone();\n        let handle = tokio::spawn(async move { c.start(stream).await });\n        tokio::task::yield_now().await; // 等一下，确保连上了\n        if let Err(err) = fast_login(&client, &credential).await {\n            // token 可能过期了\n            tracing::error!(\"failed to fast_login: {}\", err);\n            client.stop(NetworkStatus::NetworkOffline);\n            count += 1;\n            if count > max {\n                tracing::error!(\"reconnect_count: {}, break!\", count);\n                break;\n            }\n            continue;\n        }\n        tracing::info!(\"succeed to reconnect\");\n        after_login(&client).await;\n        handle.await.ok();\n    }\n}\n\npub struct Password {\n    pub uin: i64,\n    pub password: String,\n}\n\npub enum Credential {\n    Token(ricq_core::Token),\n    Password(Password),\n}\n\n/// 用于重连\n#[async_trait]\npub trait FastLogin {\n    async fn fast_login(&self, client: &Arc<Client>) -> RQResult<()>;\n}\n\n#[async_trait]\nimpl FastLogin for ricq_core::Token {\n    async fn fast_login(&self, client: &Arc<Client>) -> RQResult<()> {\n        match client.token_login(self.clone()).await? {\n            LoginResponse::Success(_) => Ok(()),\n            other => Err(RQError::Other(format!(\"failed to token_login, {other:?}\"))),\n        }\n    }\n}\n\n#[async_trait]\nimpl FastLogin for Password {\n    async fn fast_login(&self, client: &Arc<Client>) -> RQResult<()> {\n        let resp = client.password_login(self.uin, &self.password).await?;\n        match resp {\n            LoginResponse::Success { .. } => return Ok(()),\n            LoginResponse::DeviceLockLogin { .. } => {\n                return if let LoginResponse::Success { .. } = client.device_lock_login().await? {\n                    Ok(())\n                } else {\n                    Err(RQError::Other(\"failed to login\".into()))\n                };\n            }\n            other => Err(RQError::Other(format!(\"failed to login, {other:?}\"))),\n        }\n    }\n}\n\n/// 如果你非常确定登录过程中不会遇到验证码，可以用 fast_login\npub async fn fast_login(client: &Arc<Client>, credential: &Credential) -> RQResult<()> {\n    match credential {\n        Credential::Token(token) => token.fast_login(client).await,\n        Credential::Password(password) => password.fast_login(client).await,\n    }\n}\n"
  },
  {
    "path": "ricq/src/lib.rs",
    "content": "#![feature(async_closure)]\n#![feature(let_chains)]\n#![feature(result_flattening)]\npub mod client;\nmod config;\npub mod ext;\npub mod qsign;\npub mod structs;\n\npub use client::handler;\npub use client::Client;\npub use config::Config;\npub use device::Device;\npub use version::Protocol;\n\npub use ricq_core::command::wtlogin::{\n    LoginDeviceLockLogin, LoginDeviceLocked, LoginNeedCaptcha, LoginResponse, LoginSuccess,\n    LoginUnknownStatus, QRCodeConfirmed, QRCodeImageFetch, QRCodeState,\n};\npub use ricq_core::error::{RQError, RQResult};\nuse ricq_core::jce;\npub use ricq_core::msg;\npub use ricq_core::protocol::device;\npub use ricq_core::protocol::version;\n"
  },
  {
    "path": "ricq/src/qsign.rs",
    "content": "use bytes::{BufMut, BytesMut};\nuse ricq_core::binary::packet_writer::WriteLV;\nuse ricq_core::hex::encode_hex;\nuse serde::{Deserialize, Serialize};\nuse std::time::Duration;\n\npub struct QSignClient {\n    url: String,\n    key: String,\n    client: reqwest::Client,\n    timeout: Duration,\n}\n#[derive(Debug, Clone, Default, Serialize, Deserialize)]\npub struct QSignResponse<T> {\n    pub code: i64,\n    pub msg: String,\n    pub data: T,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct SignData {\n    pub token: String,\n\n    pub extra: String,\n\n    pub sign: String,\n\n    #[serde(rename = \"o3did\")]\n    pub o3_did: String,\n\n    pub request_callback: Vec<RequestCallback>,\n}\n\n#[derive(Debug, Clone, Default, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct RequestCallback {\n    pub cmd: String,\n\n    pub body: String,\n\n    pub callback_id: i64,\n}\n\nimpl QSignClient {\n    pub fn new(mut url: String, key: String, timeout: Duration) -> reqwest::Result<Self> {\n        let client = reqwest::ClientBuilder::new().build()?;\n        if url.ends_with('/') {\n            url.pop();\n        }\n        Ok(QSignClient {\n            url,\n            key,\n            client,\n            timeout,\n        })\n    }\n\n    pub async fn register(\n        &self,\n        uin: i64,\n        qimei36: &str,\n        android_id: &str,\n        guid: &[u8],\n    ) -> reqwest::Result<QSignResponse<String>> {\n        let url = format!(\"{}/register\", self.url);\n        self.client\n            .get(url)\n            .query(&[\n                (\"uin\", uin.to_string().as_str()),\n                (\"android_id\", android_id),\n                (\"guid\", encode_hex(guid).as_str()),\n                (\"qimei36\", qimei36),\n                (\"key\", self.key.as_str()),\n            ])\n            .timeout(self.timeout)\n            .send()\n            .await?\n            .json()\n            .await\n    }\n\n    pub async fn custom_energy(\n        &self,\n        uin: i64,\n        data: &str,\n        salt: &[u8],\n        guid: &[u8],\n        android_id: &str,\n    ) -> reqwest::Result<QSignResponse<String>> {\n        let url = format!(\"{}/custom_energy\", self.url);\n        self.client\n            .get(url)\n            .query(&[\n                (\"uin\", uin.to_string().as_str()),\n                (\"salt\", encode_hex(salt).as_str()),\n                (\"data\", data),\n                (\"android_id\", android_id),\n                (\"guid\", encode_hex(guid).as_str()),\n            ])\n            .timeout(self.timeout)\n            .send()\n            .await?\n            .json()\n            .await\n    }\n\n    pub fn calc_salt(uin: u64, guid: &[u8], sdk_version: &str, sub_cmd: u32) -> Vec<u8> {\n        let mut buf = BytesMut::new();\n        match sub_cmd {\n            2 | 7 => buf.put_u64(uin),\n            9 | 0xa | 0xf => buf.put_u32(0),\n            _ => panic!(\"sub_cmd not supported\"),\n        }\n        buf.write_short_lv(guid);\n        buf.write_short_lv(sdk_version.as_bytes());\n        buf.put_u32(sub_cmd);\n        match sub_cmd {\n            9 | 0xa | 0xf => buf.put_u32(0),\n            _ => {}\n        }\n        buf.to_vec()\n    }\n\n    pub async fn energy(\n        &self,\n        uin: i64,\n        data: &str,\n        version: &str,\n        guid: &[u8],\n        android_id: &str,\n    ) -> reqwest::Result<QSignResponse<String>> {\n        let url = format!(\"{}/energy\", self.url);\n        self.client\n            .get(url)\n            .query(&[\n                (\"version\", version),\n                (\"uin\", uin.to_string().as_str()),\n                (\"data\", data),\n                (\"guid\", encode_hex(guid).as_str()),\n                (\"android_id\", android_id),\n            ])\n            .timeout(self.timeout)\n            .send()\n            .await?\n            .json()\n            .await\n    }\n\n    // TODO test sign\n    #[allow(clippy::too_many_arguments)]\n    pub async fn sign(\n        &self,\n        uin: i64,\n        qua: &str,\n        cmd: &str,\n        seq: i32,\n        buffer: &[u8],\n        qimei36: &str,\n        android_id: &str,\n        guid: &[u8],\n    ) -> reqwest::Result<QSignResponse<SignData>> {\n        let url = format!(\"{}/sign\", self.url);\n        self.client\n            .post(url)\n            .form(&[\n                (\"uin\", uin.to_string().as_str()),\n                (\"qua\", qua),\n                (\"cmd\", cmd),\n                (\"seq\", seq.to_string().as_str()),\n                (\"buffer\", encode_hex(buffer).as_str()),\n                (\"qimei36\", qimei36),\n                (\"android_id\", android_id),\n                (\"guid\", encode_hex(guid).as_str()),\n            ])\n            .timeout(self.timeout)\n            .send()\n            .await?\n            .json()\n            .await\n    }\n    pub async fn submit(\n        &self,\n        uin: i64,\n        cmd: &str,\n        callback_id: i64,\n        buffer: &[u8],\n    ) -> reqwest::Result<()> {\n        let url = format!(\"{}/submit\", self.url);\n        self.client\n            .get(url)\n            .query(&[\n                (\"uin\", uin.to_string().as_str()),\n                (\"cmd\", cmd),\n                (\"callback_id\", callback_id.to_string().as_str()),\n                (\"buffer\", encode_hex(buffer).as_str()),\n            ])\n            .timeout(self.timeout)\n            .send()\n            .await?;\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::client::qimei::get_qimei;\n    use rand::SeedableRng;\n    use ricq_core::protocol::{device::Device, sig::Sig, version::ANDROID_PHONE};\n\n    async fn get_device() -> Device {\n        let mut rng = rand::prelude::StdRng::seed_from_u64(10);\n        let version = ANDROID_PHONE;\n        let mut device = Device::random_with_rng(&mut rng);\n        let qimei = get_qimei(&mut rng, &device, &version).await.unwrap();\n        device.set_qimei(qimei);\n        device\n    }\n\n    #[tokio::test]\n    async fn test_custom_energy() {\n        let uin = 12345;\n        let device = get_device().await;\n        let sig = Sig::new(&device);\n        let cli = QSignClient::new(\n            String::from(\"http://localhost:8080\"),\n            String::from(\"114514\"),\n            Duration::from_secs(60),\n        )\n        .unwrap();\n        let resp = cli\n            .custom_energy(\n                uin,\n                \"810_9\",\n                &[\n                    0, 0, 0, 0, 0, 16, 64, 70, 49, 11, 87, 2, 23, 195, 124, 180, 140, 194, 140,\n                    180, 113, 103, 0, 10, 54, 46, 48, 46, 48, 46, 50, 53, 52, 54, 0, 0, 0, 9, 0, 0,\n                    0, 0,\n                ],\n                &sig.guid,\n                &device.android_id,\n            )\n            .await;\n        println!(\"{resp:?}\");\n    }\n    #[tokio::test]\n    async fn test_energy() {\n        let uin = 12345;\n        let device = get_device().await;\n        let sig = Sig::new(&device);\n        let cli = QSignClient::new(\n            String::from(\"http://localhost:8080\"),\n            String::from(\"114514\"),\n            Duration::from_secs(60),\n        )\n        .unwrap();\n        let resp = cli\n            .energy(uin, \"810_9\", \"6.0.0.2546\", &sig.guid, &device.android_id)\n            .await;\n        println!(\"{resp:?}\");\n    }\n}\n"
  },
  {
    "path": "ricq/src/structs/image_info.rs",
    "content": "use serde::{Deserialize, Serialize};\n\nuse ricq_core::common::RQAddr;\n\nuse ricq_core::hex::encode_hex;\nuse ricq_core::msg::elem::{FriendImage, GroupImage};\nuse ricq_core::RQResult;\n\n// 仅用于上传图片，一些临时变量，太多了放一起\n#[derive(Serialize, Deserialize, Debug, Clone)]\npub struct ImageInfo {\n    pub md5: Vec<u8>,\n    pub width: u32,\n    pub height: u32,\n    pub image_type: i32,\n    pub size: u32,\n    pub filename: String,\n}\n\nimpl ImageInfo {\n    pub fn try_new(data: &[u8]) -> RQResult<Self> {\n        let md5 = md5::compute(data).to_vec();\n\n        #[cfg(feature = \"image-detail\")]\n        let (width, height, format, ext_name) = {\n            let img_reader = image::io::Reader::new(std::io::Cursor::new(data))\n                .with_guessed_format()\n                .map_err(ricq_core::RQError::IO)?;\n            let format = img_reader.format().unwrap_or(image::ImageFormat::Png);\n            let (width, height) = img_reader.into_dimensions().unwrap_or((720, 480));\n            let ext_name = format.extensions_str().first().expect(\"image_format error\");\n            (width, height, format, ext_name)\n        };\n        #[cfg(not(feature = \"image-detail\"))]\n        let (width, height, ext_name) = (1280, 720, \"png\");\n\n        Ok(ImageInfo {\n            filename: format!(\"{}.{}\", encode_hex(&md5), ext_name),\n            md5,\n            width,\n            height,\n            #[cfg(feature = \"image-detail\")]\n            image_type: match format {\n                image::ImageFormat::Jpeg => 1000,\n                image::ImageFormat::Png => 1001,\n                image::ImageFormat::WebP => 1002,\n                image::ImageFormat::Bmp => 1005,\n                image::ImageFormat::Gif => 2000,\n                _ => 1000,\n            },\n            #[cfg(not(feature = \"image-detail\"))]\n            image_type: 1001, // PNG\n            size: data.len() as u32,\n        })\n    }\n\n    // download path: \"/{to_uin}-{unknown?}-{md5}\"\n    pub fn into_friend_image(self, res_id: String, download_path: String) -> FriendImage {\n        FriendImage {\n            res_id,\n            file_path: format!(\"{}.png\", encode_hex(&self.md5)),\n            md5: self.md5,\n            size: self.size,\n            width: self.width,\n            height: self.height,\n            image_type: self.image_type,\n            download_path,\n            ..Default::default()\n        }\n    }\n\n    pub fn into_group_image(self, file_id: u64, addr: RQAddr, signature: Vec<u8>) -> GroupImage {\n        GroupImage {\n            file_path: format!(\"{}.png\", encode_hex(&self.md5)),\n            file_id: file_id as i64,\n            size: self.size,\n            width: self.width,\n            height: self.height,\n            md5: self.md5,\n            image_type: self.image_type,\n            signature,\n            server_ip: addr.0,\n            server_port: addr.1 as u32,\n            orig_url: None,\n        }\n    }\n}\n"
  },
  {
    "path": "ricq/src/structs/mod.rs",
    "content": "pub use image_info::*;\npub use ricq_core::structs::*;\n\nmod image_info;\n"
  },
  {
    "path": "ricq-core/Cargo.toml",
    "content": "[package]\nname = \"ricq-core\"\nversion = \"0.1.20\"\nedition = \"2021\"\ndescription = \"Packet encoders and decoders for ricq\"\nlicense = \"MPL-2.0\"\nhomepage = \"https://github.com/lz1998/ricq\"\nrepository = \"https://github.com/lz1998/ricq\"\nkeywords = [\"qq\", \"protocol\", \"android\", \"mirai\"]\n\n[dependencies]\nbyteorder.workspace = true\nbytes.workspace = true\nderivative.workspace = true\nflate2.workspace = true\ngeneric-array.workspace = true\njcers = { workspace = true, features = [\"derive\"] }\nmd5.workspace = true\np256 = { workspace = true, features = [\"ecdh\"], default-features = false }\nprost = { workspace = true, features = [\"std\"], default-features = false }\nprost-types.workspace = true\nrand.workspace = true\nserde = { workspace = true, features = [\"derive\"] }\nthiserror.workspace = true\ncrypto-common.workspace = true\nserde_json.workspace = true\nblock-padding = { workspace = true, features = [\"std\"] }\nspki.workspace = true\nbase64.workspace = true\ncbc = { workspace = true, features = [\"alloc\"] }\naes.workspace = true\nrsa.workspace = true\nx509-cert.workspace = true\nchrono.workspace = true\nsha2.workspace = true\n\n[build-dependencies]\nprost-build = \"0.9\"\n"
  },
  {
    "path": "ricq-core/build.rs",
    "content": "use std::path::{Path, PathBuf};\nfn recurse_dir(v: &mut Vec<PathBuf>, dir: impl AsRef<Path>) {\n    for entry in\n        std::fs::read_dir(&dir).unwrap_or_else(|_| panic!(\"Unable to read dir: {:?}\", dir.as_ref()))\n    {\n        let path = entry.expect(\"Unable to get direntry\").path();\n        if path.is_dir() {\n            recurse_dir(v, path);\n        } else if let Some(true) = path.extension().map(|v| v == \"proto\") {\n            v.push(path);\n        }\n    }\n}\nfn main() {\n    let mut files = Vec::new();\n    recurse_dir(&mut files, \"src/pb\");\n    prost_build::compile_protos(&files, &[\"src/pb\"]).unwrap();\n}\n"
  },
  {
    "path": "ricq-core/src/binary/binary_reader.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::{Buf, Bytes};\n\npub trait BinaryReader {\n    fn read_string(&mut self) -> String;\n    fn read_string_short(&mut self) -> String;\n    fn read_bytes_short(&mut self) -> Bytes;\n    fn read_tlv_map(&mut self, tag_size: usize) -> HashMap<u16, Bytes>;\n    fn read_string_limit(&mut self, limit: usize) -> String;\n}\n\nimpl<B> BinaryReader for B\nwhere\n    B: Buf,\n{\n    fn read_string(&mut self) -> String {\n        let len = self.get_i32() as usize - 4;\n        String::from_utf8_lossy(&self.copy_to_bytes(len)).into_owned()\n    }\n\n    fn read_string_short(&mut self) -> String {\n        let len = self.get_u16() as usize;\n        String::from_utf8_lossy(&self.copy_to_bytes(len)).into_owned()\n    }\n\n    fn read_bytes_short(&mut self) -> Bytes {\n        let len = self.get_u16() as usize;\n        self.copy_to_bytes(len)\n    }\n\n    fn read_tlv_map(&mut self, tag_size: usize) -> HashMap<u16, Bytes> {\n        let mut m = HashMap::new();\n        loop {\n            if self.remaining() < tag_size {\n                return m;\n            }\n            let mut k = 0;\n            if tag_size == 1 {\n                k = self.get_u8() as u16;\n            } else if tag_size == 2 {\n                k = self.get_u16();\n            } else if tag_size == 4 {\n                k = self.get_i32() as u16;\n            }\n            if k == 255 {\n                return m;\n            }\n            if self.remaining() < 2 {\n                return m;\n            }\n            let len = self.get_u16() as usize;\n            if self.remaining() < len {\n                return m;\n            }\n            m.insert(k, self.copy_to_bytes(len));\n        }\n    }\n\n    fn read_string_limit(&mut self, limit: usize) -> String {\n        String::from_utf8_lossy(&self.copy_to_bytes(limit)).into_owned()\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/binary/binary_writer.rs",
    "content": "use crate::crypto::qqtea_encrypt;\nuse crate::hex::decode_hex;\nuse bytes::{Buf, BufMut, BytesMut};\n\npub trait BinaryWriter {\n    fn write_bytes_short(&mut self, data: &[u8]);\n    fn encrypt_and_write(&mut self, key: &[u8], data: &[u8]);\n    fn write_hex(&mut self, h: &str);\n    fn write_int_lv_packet(&mut self, offset: usize, data: &[u8]);\n    fn write_string(&mut self, v: &str);\n    fn write_uni_packet(\n        &mut self,\n        command_name: &str,\n        session_id: &[u8],\n        extra_data: &[u8],\n        body: &[u8],\n    );\n    fn write_tlv_limited_size(&mut self, data: &[u8], limit: isize);\n}\n\nimpl<B> BinaryWriter for B\nwhere\n    B: BufMut,\n{\n    fn write_bytes_short(&mut self, data: &[u8]) {\n        self.put_u16(data.len() as u16);\n        self.put_slice(data.chunk())\n    }\n\n    fn encrypt_and_write(&mut self, key: &[u8], data: &[u8]) {\n        let ed = qqtea_encrypt(data, key);\n        self.put_slice(&ed)\n    }\n\n    fn write_hex(&mut self, h: &str) {\n        let b = decode_hex(h).expect(\"write_hex failed\");\n        self.put_slice(&b);\n    }\n\n    fn write_int_lv_packet(&mut self, offset: usize, data: &[u8]) {\n        self.put_u32((data.len() + offset) as u32);\n        self.put_slice(data);\n    }\n\n    fn write_string(&mut self, v: &str) {\n        let payload = v.as_bytes();\n        self.put_u32((payload.len() + 4) as u32);\n        self.put_slice(payload)\n    }\n\n    fn write_uni_packet(\n        &mut self,\n        command_name: &str,\n        session_id: &[u8],\n        extra_data: &[u8],\n        body: &[u8],\n    ) {\n        let mut w1 = BytesMut::new();\n        {\n            w1.write_string(command_name);\n            w1.put_u32(8);\n            w1.put_slice(session_id);\n            if extra_data.is_empty() {\n                w1.put_u32(0x04)\n            } else {\n                w1.put_u32((extra_data.len() + 4) as u32);\n                w1.put_slice(extra_data);\n            }\n        }\n\n        self.put_u32((w1.len() + 4) as u32);\n        self.put_slice(&w1);\n        self.put_u32((body.len() + 4) as u32);\n        self.put_slice(body);\n    }\n\n    fn write_tlv_limited_size(&mut self, data: &[u8], limit: isize) {\n        if data.len() <= limit as usize {\n            self.write_bytes_short(data);\n            return;\n        }\n        self.write_bytes_short(&data[..(limit as usize)])\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/binary/mod.rs",
    "content": "mod binary_reader;\nmod binary_writer;\npub mod packet_writer;\n\npub use binary_reader::BinaryReader;\npub use binary_writer::BinaryWriter;\n"
  },
  {
    "path": "ricq-core/src/binary/packet_writer.rs",
    "content": "use bytes::*;\nuse std::marker::PhantomData;\n\npub trait PacketWriter<B: BufMut> {\n    fn write(self, buf: &mut B);\n}\n\npub trait PacketAppender<B: BufMut>: PacketWriter<B> + Sized {\n    fn append<W: PacketWriter<B>>(self, w: W) -> impl PacketWriter<B> {\n        |buf: &mut B| {\n            self.write(buf);\n            w.write(buf);\n        }\n    }\n}\n\nimpl<F, B> PacketWriter<B> for F\nwhere\n    B: BufMut,\n    F: FnOnce(&mut B),\n{\n    fn write(self, buf: &mut B) {\n        self(buf)\n    }\n}\n\nimpl<B> PacketWriter<B> for &[u8]\nwhere\n    B: BufMut,\n{\n    fn write(self, buf: &mut B) {\n        buf.put_slice(self);\n    }\n}\n\npub enum Either<L, R> {\n    Left(L),\n    Right(R),\n}\n\nimpl<B, L, R> PacketWriter<B> for Either<L, R>\nwhere\n    B: BufMut,\n    L: PacketWriter<B>,\n    R: PacketWriter<B>,\n{\n    fn write(self, buf: &mut B) {\n        match self {\n            Either::Left(l) => l.write(buf),\n            Either::Right(r) => r.write(buf),\n        }\n    }\n}\n\npub struct CounterWriter<B, W>\nwhere\n    B: BufMut,\n    W: PacketWriter<B>,\n{\n    pub count: usize,\n    pub writer: W,\n    pub _mark: PhantomData<B>,\n}\n\nimpl<B, T> PacketWriter<B> for CounterWriter<B, T>\nwhere\n    B: BufMut,\n    T: PacketWriter<B>,\n{\n    fn write(self, buf: &mut B) {\n        self.writer.write(buf)\n    }\n}\n\nimpl<B, T> PacketAppender<B> for CounterWriter<B, T>\nwhere\n    B: BufMut,\n    T: PacketWriter<B>,\n{\n    fn append<W>(self, w: W) -> CounterWriter<B, impl PacketWriter<B>>\n    where\n        W: PacketWriter<B>,\n    {\n        CounterWriter {\n            count: self.count + 1,\n            writer: |buf: &mut B| {\n                self.writer.write(buf);\n                w.write(buf);\n            },\n            _mark: PhantomData,\n        }\n    }\n}\n\nimpl<B: BufMut> Default for CounterWriter<B, fn(&mut B)> {\n    fn default() -> Self {\n        CounterWriter {\n            count: 0,\n            writer: |_| {},\n            _mark: PhantomData,\n        }\n    }\n}\n\nimpl<B, T> CounterWriter<B, T>\nwhere\n    B: BufMut,\n    T: PacketWriter<B>,\n{\n    pub fn append_option<W>(self, w: Option<W>) -> CounterWriter<B, impl PacketWriter<B>>\n    where\n        W: PacketWriter<B>,\n    {\n        CounterWriter {\n            count: self.count + if w.is_some() { 1 } else { 0 },\n            writer: |buf: &mut B| {\n                self.writer.write(buf);\n                if let Some(w) = w {\n                    w.write(buf)\n                }\n            },\n            _mark: PhantomData,\n        }\n    }\n}\n// write length-value\npub trait WriteLV: BufMut {\n    fn write_short_lv<W>(&mut self, w: W)\n    where\n        W: PacketWriter<Self>,\n        Self: Sized;\n}\n\nmacro_rules! impl_write_lv {\n    () => {\n        fn write_short_lv<W>(&mut self, w: W)\n        where\n            W: PacketWriter<Self>,\n            Self: Sized,\n        {\n            let len_start = self.len();\n            self.put_u16(0);\n            let body_start = self.len();\n            w.write(self);\n            let body_len = (self.len() - body_start) as u16;\n            (&mut self[len_start..len_start + 2]).put_u16(body_len);\n        }\n    };\n}\n\nimpl WriteLV for Vec<u8> {\n    impl_write_lv!();\n}\n\nimpl WriteLV for BytesMut {\n    impl_write_lv!();\n}\n"
  },
  {
    "path": "ricq-core/src/command/common.rs",
    "content": "use bytes::{BufMut, Bytes, BytesMut};\nuse prost::Message;\n\nuse crate::protocol::oicq;\nuse crate::protocol::packet::*;\nuse crate::Engine;\n\nimpl Engine {\n    pub fn build_oicq_request_packet(&self, uin: i64, command_id: u16, body: &[u8]) -> Bytes {\n        let req = oicq::Message {\n            uin: uin as u32,\n            command: command_id,\n            body: Bytes::from(body.to_vec()),\n            encryption_method: oicq::EncryptionMethod::ECDH,\n        };\n        self.transport.oicq_codec.encode(req)\n    }\n\n    pub fn uni_packet_with_seq(&self, seq: i32, command: &str, body: Bytes) -> Packet {\n        Packet {\n            packet_type: PacketType::Simple,\n            encrypt_type: EncryptType::D2Key,\n            seq_id: seq,\n            body,\n            command_name: command.to_owned(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    pub fn uni_packet(&self, command: &str, body: Bytes) -> Packet {\n        let seq = self.next_seq();\n        self.uni_packet_with_seq(seq as i32, command, body)\n    }\n}\n\npub fn pack_uni_request_data(data: &[u8]) -> Bytes {\n    let mut r = BytesMut::new();\n    r.put_slice(&[0x0A]);\n    r.put_slice(data);\n    r.put_slice(&[0x0B]);\n    Bytes::from(r)\n}\n\npub trait PbToBytes<B: Message> {\n    fn to_bytes(&self) -> Bytes;\n}\n\nimpl<B: Message> PbToBytes<B> for B {\n    fn to_bytes(&self) -> Bytes {\n        let mut buf = BytesMut::new();\n        prost::Message::encode(self, &mut buf).expect(\"prost encode failed\");\n        buf.freeze()\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/config_push_svc/builder.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::Bytes;\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;\nuse crate::jce;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    // ConfigPushSvc.PushResp\n    pub fn build_conf_push_resp_packet(&self, t: i32, pkt_seq: i64, jce_buf: Bytes) -> Packet {\n        let mut req = jcers::JceMut::new();\n        req.put_i32(t, 1);\n        req.put_i64(pkt_seq, 2);\n        req.put_bytes(jce_buf, 3);\n\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\"PushResp\".to_string(), pack_uni_request_data(&req.freeze()))]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            s_servant_name: \"QQService.ConfigPushSvc.MainServant\".to_string(),\n            s_func_name: \"PushResp\".to_string(),\n            s_buffer: buf.freeze(),\n            context: Default::default(),\n            status: Default::default(),\n            ..Default::default()\n        };\n        self.uni_packet(\"ConfigPushSvc.PushResp\", pkt.freeze())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/config_push_svc/decoder.rs",
    "content": "use bytes::Bytes;\n\nuse crate::command::config_push_svc::*;\nuse crate::{jce, pb, RQError, RQResult};\nuse prost::Message;\n\n// TODO 还没测试\nimpl super::super::super::Engine {\n    // ConfigPushSvc.PushReq\n    pub fn decode_push_req_packet(&self, mut payload: Bytes) -> RQResult<ConfigPushReq> {\n        let mut request: jce::RequestPacket = jcers::from_buf(&mut payload)?;\n        let mut data: jce::RequestDataVersion2 = jcers::from_buf(&mut request.s_buffer)?;\n        let mut a = data\n            .map\n            .remove(\"PushReq\")\n            .ok_or_else(|| RQError::Decode(\"missing PushReq\".into()))?;\n        let mut b = a\n            .remove(\"ConfigPush.PushReq\")\n            .ok_or_else(|| RQError::Decode(\"missing ConfigPush.PushReq\".into()))?;\n        let _ = b.split_to(1);\n        let mut r = jcers::Jce::new(&mut b);\n        let t: i32 = r.get_by_tag(1)?;\n        let mut jce_buf: Bytes = r.get_by_tag(2)?;\n        let seq: i64 = r.get_by_tag(3)?;\n        let mut body = ConfigPushBody::Unknown;\n        if !jce_buf.is_empty() {\n            body = match t {\n                1 => {\n                    let mut sso_pkt = jcers::Jce::new(&mut jce_buf);\n                    let servers: Vec<jce::SsoServerInfo> = sso_pkt.get_by_tag(1)?;\n                    ConfigPushBody::SsoServers { servers }\n                }\n                2 => {\n                    let mut info: jce::FileStoragePushFSSvcList = jcers::from_buf(&mut jce_buf)?;\n                    let rsp_body = match pb::cmd0x6ff::C501RspBody::decode(\n                        &mut info.big_data_channel.pb_buf,\n                    ) {\n                        Ok(c501_rsp_body) => c501_rsp_body.rsp_body,\n                        _ => None,\n                    };\n                    ConfigPushBody::FileStorageInfo { info, rsp_body }\n                }\n                _ => ConfigPushBody::Unknown,\n            }\n        }\n        Ok(ConfigPushReq {\n            resp: ConfigPushResp {\n                t,\n                pkt_seq: seq,\n                jce_buf,\n            },\n            body,\n        })\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/config_push_svc/mod.rs",
    "content": "#![allow(clippy::large_enum_variant)]\nuse bytes::Bytes;\n\nuse crate::{jce, pb};\n\npub mod builder;\npub mod decoder;\n\n#[derive(Default, Debug)]\npub struct ConfigPushReq {\n    pub resp: ConfigPushResp,\n    pub body: ConfigPushBody,\n}\n\n#[derive(Debug, derivative::Derivative)]\n#[derivative(Default)]\npub enum ConfigPushBody {\n    #[derivative(Default)]\n    Unknown,\n    SsoServers {\n        servers: Vec<jce::SsoServerInfo>,\n    },\n    FileStorageInfo {\n        info: jce::FileStoragePushFSSvcList,\n        rsp_body: Option<pb::cmd0x6ff::SubCmd0x501RspBody>,\n    },\n}\n\n#[derive(Default, Debug)]\npub struct ConfigPushResp {\n    pub t: i32,\n    pub pkt_seq: i64,\n    pub jce_buf: Bytes,\n}\n"
  },
  {
    "path": "ricq-core/src/command/friendlist/builder.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::{BufMut, Bytes, BytesMut};\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;\nuse crate::common::group_code2uin;\nuse crate::protocol::packet::*;\nuse crate::{jce, pb};\n\nimpl super::super::super::Engine {\n    // friendlist.getFriendGroupList\n    pub fn build_friend_group_list_request_packet(\n        &self,\n        friend_start_index: i16,\n        friend_list_count: i16,\n        group_start_index: i16,\n        group_list_count: i16,\n    ) -> Packet {\n        let mut d50 = BytesMut::new();\n        prost::Message::encode(\n            &pb::D50ReqBody {\n                appid: 1002,\n                req_music_switch: 1,\n                req_mutualmark_alienation: 1,\n                req_ksing_switch: 1,\n                req_mutualmark_lbsshare: 1,\n                ..Default::default()\n            },\n            &mut d50,\n        )\n        .expect(\"failed to encode pb\");\n\n        let req = jce::FriendListRequest {\n            reqtype: 3,\n            if_reflush: if friend_start_index <= 0 { 0 } else { 1 },\n            uin: self.uin(),\n            start_index: friend_start_index,\n            friend_count: friend_list_count,\n            group_id: 0,\n            if_get_group_info: 1,\n            group_start_index: group_start_index as u8,\n            group_count: group_list_count as u8,\n            if_get_msf_group: 0,\n            if_show_term_type: 1,\n            version: 27,\n            uin_list: vec![],\n            app_type: 0,\n            if_get_dov_id: 0,\n            if_get_both_flag: 0,\n            d50: Bytes::from(d50),\n            d6b: Bytes::new(),\n            sns_type_list: vec![13580, 13581, 13582],\n        };\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\"FL\".to_string(), pack_uni_request_data(&req.freeze()))]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            c_packet_type: 0x003,\n            i_request_id: 1921334514,\n            s_servant_name: \"mqq.IMService.FriendListServiceServantObj\".to_string(),\n            s_func_name: \"GetFriendListReq\".to_string(),\n            s_buffer: buf.freeze(),\n            context: Default::default(),\n            status: Default::default(),\n            ..Default::default()\n        };\n        self.uni_packet(\"friendlist.getFriendGroupList\", pkt.freeze())\n    }\n\n    // friendlist.GetTroopListReqV2\n    pub fn build_group_list_request_packet(&self, vec_cookie: &[u8]) -> Packet {\n        let req = jce::TroopListRequest {\n            uin: self.uin(),\n            get_msf_msg_flag: 1,\n            cookies: Bytes::from(vec_cookie.to_vec()),\n            group_info: vec![],\n            group_flag_ext: 1,\n            version: 7,\n            company_id: 0,\n            version_num: 1,\n            get_long_group_name: 1,\n        };\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\n                \"GetTroopListReqV2Simplify\".to_string(),\n                pack_uni_request_data(&req.freeze()),\n            )]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            c_packet_type: 0x00,\n            i_message_type: 0,\n            i_request_id: self.next_packet_seq(),\n            s_servant_name: \"mqq.IMService.FriendListServiceServantObj\".to_string(),\n            s_func_name: \"GetTroopListReqV2Simplify\".to_string(),\n            s_buffer: buf.freeze(),\n            context: Default::default(),\n            status: Default::default(),\n            ..Default::default()\n        };\n        self.uni_packet(\"friendlist.GetTroopListReqV2\", pkt.freeze())\n    }\n\n    // friendlist.GetTroopMemberListReq\n    pub fn build_group_member_list_request_packet(&self, group_code: i64, next_uin: i64) -> Packet {\n        let payload = jce::TroopMemberListRequest {\n            uin: self.uin(),\n            group_code,\n            next_uin,\n            group_uin: group_code2uin(group_code),\n            version: 2,\n            ..Default::default()\n        };\n        let mut b = BytesMut::new();\n        b.put_slice(&[0x0A]);\n        b.put_slice(&payload.freeze());\n        b.put_slice(&[0x0B]);\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\"GTML\".to_string(), b.into())]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            i_request_id: self.next_packet_seq(),\n            s_servant_name: \"mqq.IMService.FriendListServiceServantObj\".to_string(),\n            s_func_name: \"GetTroopMemberListReq\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"friendlist.GetTroopMemberListReq\", pkt.freeze())\n    }\n\n    // friendlist.ModifyGroupCardReq\n    pub fn build_edit_group_tag_packet(\n        &self,\n        group_code: i64,\n        member_uin: i64,\n        new_tag: String,\n    ) -> Packet {\n        let payload = jce::ModifyGroupCardRequest {\n            group_code,\n            uin_info: vec![jce::UinInfo {\n                uin: member_uin,\n                flag: 31,\n                name: new_tag,\n                ..Default::default()\n            }],\n            ..Default::default()\n        };\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\n                \"MGCREQ\".to_string(),\n                pack_uni_request_data(&payload.freeze()),\n            )]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            i_request_id: self.next_packet_seq(),\n            s_servant_name: \"mqq.IMService.FriendListServiceServantObj\".to_string(),\n            s_func_name: \"ModifyGroupCardReq\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"friendlist.ModifyGroupCardReq\", pkt.freeze())\n    }\n\n    // friendlist.DelFriend\n    pub fn build_delete_friend_packet(&self, del_uin: i64) -> Packet {\n        let payload = jce::DelFriendReq {\n            uin: self.uin(),\n            del_uin,\n            del_type: 2,\n            version: 1,\n        };\n\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\"DF\".to_string(), pack_uni_request_data(&payload.freeze()))]),\n        };\n\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            i_request_id: self.next_packet_seq(),\n            s_servant_name: \"mqq.IMService.FriendListServiceServantObj\".to_string(),\n            s_func_name: \"DelFriendReq\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n\n        self.uni_packet(\"friendlist.delFriend\", pkt.freeze())\n    }\n\n    /// 好友分组操作\n    fn build_friend_list_set_group_req_packet(&self, req_type: i32, body: Bytes) -> Packet {\n        let payload = jce::FriendListSetGroupReq {\n            req_type,\n            uin: self.uin(),\n            body,\n        };\n\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\n                \"SetGroupReq\".to_string(),\n                pack_uni_request_data(&payload.freeze()),\n            )]),\n        };\n\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            i_request_id: self.next_packet_seq(),\n            s_servant_name: \"mqq.IMService.FriendListServiceServantObj\".to_string(),\n            s_func_name: \"SetGroupReq\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n\n        self.uni_packet(\"friendlist.SetGroupReq\", pkt.freeze())\n    }\n\n    /// 添加好友分组\n    // friendlist.SetGroupReq\n    pub fn build_friend_list_add_group_req_packet(&self, sort_id: u8, group_name: &str) -> Packet {\n        let mut body = BytesMut::new();\n        body.put_u8(sort_id);\n        body.put_u8(group_name.len() as u8);\n        body.put_slice(group_name.as_bytes());\n        self.build_friend_list_set_group_req_packet(0, body.freeze())\n    }\n\n    /// 重命名好友分组\n    // friendlist.SetGroupReq\n    pub fn build_friend_list_rename_group_req_packet(\n        &self,\n        group_id: u8,\n        group_name: &str,\n    ) -> Packet {\n        let mut body = BytesMut::new();\n        body.put_u8(group_id);\n        body.put_u8(group_name.len() as u8);\n        body.put_slice(group_name.as_bytes());\n        self.build_friend_list_set_group_req_packet(1, body.freeze())\n    }\n\n    /// 删除好友分组\n    // friendlist.SetGroupReq\n    pub fn build_friend_list_del_group_req_packet(&self, group_id: u8) -> Packet {\n        let mut body = BytesMut::new();\n        body.put_u8(group_id);\n        self.build_friend_list_set_group_req_packet(2, body.freeze())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/friendlist/decoder.rs",
    "content": "use bytes::{Buf, Bytes};\nuse jcers::Jce;\n\nuse crate::command::friendlist::*;\nuse crate::structs::{FriendInfo, GroupInfo, GroupMemberInfo, GroupMemberPermission};\nuse crate::{jce, RQError, RQResult};\n\nimpl super::super::super::Engine {\n    // friendlist.getFriendGroupList\n    pub fn decode_friend_group_list_response(\n        &self,\n        mut payload: Bytes,\n    ) -> RQResult<FriendListResponse> {\n        let mut request: jce::RequestPacket =\n            jcers::from_buf(&mut payload).map_err(RQError::from)?;\n        let mut data: jce::RequestDataVersion3 =\n            jcers::from_buf(&mut request.s_buffer).map_err(RQError::from)?;\n        let mut fl_resp = data.map.remove(\"FLRESP\").ok_or_else(|| {\n            RQError::Decode(\"decode_friend_group_list_response FLRESP not found\".into())\n        })?;\n        fl_resp.advance(1);\n        let resp: jce::FriendListResponse = jcers::from_buf(&mut fl_resp).map_err(RQError::from)?;\n        Ok(FriendListResponse {\n            total_count: resp.total_friend_count,\n            online_friend_count: resp.online_friend_count,\n            friends: resp\n                .friend_info_list\n                .into_iter()\n                .map(|f| FriendInfo {\n                    uin: f.friend_uin,\n                    nick: f.nick,\n                    remark: f.remark,\n                    face_id: f.face_id,\n                    group_id: f.group_id,\n                })\n                .collect(),\n            friend_groups: resp\n                .group_info_list\n                .into_iter()\n                .map(|g| {\n                    (\n                        g.group_id,\n                        FriendGroupInfo {\n                            group_id: g.group_id,\n                            group_name: g.group_name,\n                            friend_count: g.friend_count,\n                            online_friend_count: g.online_friend_count,\n                            seq_id: g.seq_id,\n                        },\n                    )\n                })\n                .collect(),\n        })\n    }\n\n    // friendlist.GetTroopListReqV2\n    pub fn decode_group_list_response(&self, mut payload: Bytes) -> RQResult<GroupListResponse> {\n        let mut request: jce::RequestPacket =\n            jcers::from_buf(&mut payload).map_err(RQError::from)?;\n        let mut data: jce::RequestDataVersion3 =\n            jcers::from_buf(&mut request.s_buffer).map_err(RQError::from)?;\n        let mut fl_resp = data.map.remove(\"GetTroopListRespV2\").ok_or_else(|| {\n            RQError::Decode(\"decode_group_list_response GetTroopListRespV2 not found\".into())\n        })?;\n        fl_resp.advance(1);\n        let mut r = Jce::new(&mut fl_resp);\n        let vec_cookie: Bytes = r.get_by_tag(4).map_err(RQError::from)?;\n        let groups: Vec<jce::TroopNumber> = r.get_by_tag(5).map_err(RQError::from)?;\n        let l = groups\n            .into_iter()\n            .map(|g| GroupInfo {\n                uin: g.group_uin,\n                code: g.group_code,\n                name: g.group_name,\n                memo: g.group_memo,\n                owner_uin: g.group_owner_uin,\n                member_count: g.member_num as u16,\n                max_member_count: g.max_group_member_num as u16,\n                shut_up_timestamp: g.shut_up_timestamp,\n                my_shut_up_timestamp: g.my_shut_up_timestamp,\n                ..Default::default()\n            })\n            .collect();\n        Ok(GroupListResponse {\n            groups: l,\n            vec_cookie,\n        })\n    }\n\n    // friendlist.GetTroopMemberListReq\n    pub fn decode_group_member_list_response(\n        &self,\n        mut payload: Bytes,\n        group_owner_uin: i64,\n    ) -> RQResult<GroupMemberListResponse> {\n        let mut request: jce::RequestPacket =\n            jcers::from_buf(&mut payload).map_err(RQError::from)?;\n        let mut data: jce::RequestDataVersion3 =\n            jcers::from_buf(&mut request.s_buffer).map_err(RQError::from)?;\n        let mut fl_resp = data.map.remove(\"GTMLRESP\").ok_or_else(|| {\n            RQError::Decode(\"decode_group_member_list_response GTMLRESP not found\".into())\n        })?;\n        fl_resp.advance(1);\n        let mut r = Jce::new(&mut fl_resp);\n        let members: Vec<jce::TroopMemberInfo> = r.get_by_tag(3).map_err(RQError::from)?;\n        let next_uin = r.get_by_tag(4).map_err(RQError::from)?;\n        let mut l: Vec<GroupMemberInfo> = Vec::new();\n        for m in members {\n            l.push(GroupMemberInfo {\n                uin: m.member_uin,\n                gender: m.gender,\n                nickname: m.nick,\n                card_name: m.name,\n                level: m.member_level as u16,\n                join_time: m.join_time,\n                last_speak_time: m.last_speak_time,\n                special_title: m.special_title,\n                special_title_expire_time: m.special_title_expire_time,\n                shut_up_timestamp: m.shut_up_timestap,\n                permission: if group_owner_uin == m.member_uin {\n                    GroupMemberPermission::Owner\n                } else {\n                    match m.flag {\n                        1 => GroupMemberPermission::Administrator,\n                        _ => GroupMemberPermission::Member,\n                    }\n                },\n                ..Default::default()\n            })\n        }\n        Ok(GroupMemberListResponse { next_uin, list: l })\n    }\n\n    //friendlist.delFriend\n    pub fn decode_remove_friend(&self, mut payload: Bytes) -> RQResult<jce::DelFriendResp> {\n        let mut req: jce::RequestPacket = jcers::from_buf(&mut payload)?;\n\n        let mut data: jce::RequestDataVersion3 = jcers::from_buf(&mut req.s_buffer)?;\n\n        let mut r = data\n            .map\n            .remove(\"DFRESP\")\n            .ok_or_else(|| RQError::Decode(\"decode_remove_friend `DFRESP` not found\".into()))?;\n        jcers::from_buf(&mut r).map_err(Into::into)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/friendlist/mod.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::Bytes;\n\nuse crate::structs::*;\n\npub mod builder;\npub mod decoder;\n\n#[derive(Debug, Default)]\npub struct FriendListResponse {\n    /// 好友列表\n    pub friends: Vec<FriendInfo>,\n    /// 好友分组\n    pub friend_groups: HashMap<u8, FriendGroupInfo>,\n    /// 好友数量\n    pub total_count: i16,\n    /// 在线好友数量\n    pub online_friend_count: i16,\n}\n\n#[derive(Debug)]\npub struct GroupListResponse {\n    pub groups: Vec<GroupInfo>,\n    pub vec_cookie: Bytes,\n}\n\n#[derive(Debug)]\npub struct GroupMemberListResponse {\n    pub next_uin: i64,\n    pub list: Vec<GroupMemberInfo>,\n}\n"
  },
  {
    "path": "ricq-core/src/command/group_anonymous_generate_nick/builder.rs",
    "content": "use crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    // group_anonymous_generate_nick.group\n    pub fn build_get_anony_info_request(&self, group_code: i64) -> Packet {\n        let req = pb::cmd0x3bb::AnonyMsg {\n            cmd: Some(1),\n            anony_req: Some(pb::cmd0x3bb::C3bbReqBody {\n                uin: Some(self.uin() as u64),\n                group_code: Some(group_code as u64),\n            }),\n            ..Default::default()\n        };\n        self.uni_packet(\"group_anonymous_generate_nick.group\", req.to_bytes())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/group_anonymous_generate_nick/decoder.rs",
    "content": "use bytes::Bytes;\nuse prost::Message;\n\nuse crate::msg::elem::Anonymous;\nuse crate::{pb, RQError, RQResult};\n\nimpl super::super::super::Engine {\n    // group_member_card.get_group_member_card_info\n    pub fn decode_get_anony_info_response(&self, payload: Bytes) -> RQResult<Option<Anonymous>> {\n        let resp = pb::cmd0x3bb::AnonyMsg::decode(&*payload)?;\n        let rsp = resp.anony_rsp.ok_or(RQError::EmptyField(\"anony_rsp\"))?;\n        let enable_anony = rsp\n            .anony_status\n            .map(|s| s.forbid_talking.unwrap_or(1) == 0)\n            .unwrap_or_default();\n        if !enable_anony {\n            return Ok(None);\n        }\n        Ok(Some(Anonymous {\n            anon_id: vec![],\n            nick: String::from_utf8_lossy(&rsp.anony_name.unwrap_or_default()).into_owned(),\n            portrait_index: rsp.portrait_index.unwrap_or_default() as i32,\n            bubble_index: rsp.bubble_index.unwrap_or_default() as i32,\n            expire_time: rsp.expired_time.unwrap_or_default() as i32,\n            color: rsp.color.unwrap_or_default(),\n        }))\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/group_anonymous_generate_nick/mod.rs",
    "content": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/group_member_card/builder.rs",
    "content": "use crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::*;\n\nimpl super::super::super::Engine {\n    // group_member_card.get_group_member_card_info\n    pub fn build_group_member_info_request_packet(&self, group_code: i64, uin: i64) -> Packet {\n        let payload = pb::GroupMemberReqBody {\n            group_code,\n            uin,\n            new_client: true,\n            client_type: 1,\n            rich_card_name_ver: 1,\n        };\n        self.uni_packet(\n            \"group_member_card.get_group_member_card_info\",\n            payload.to_bytes(),\n        )\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/group_member_card/decoder.rs",
    "content": "use bytes::Bytes;\nuse prost::Message;\n\nuse crate::structs::{GroupMemberInfo, GroupMemberPermission};\nuse crate::{pb, RQError, RQResult};\n\nimpl super::super::super::Engine {\n    // group_member_card.get_group_member_card_info\n    pub fn decode_group_member_info_response(&self, payload: Bytes) -> RQResult<GroupMemberInfo> {\n        let resp = pb::GroupMemberRspBody::decode(&*payload)?;\n        let group_code = resp.group_code;\n        let mem_info = resp\n            .mem_info\n            .ok_or_else(|| RQError::Decode(\"mem_info is none\".to_string()))?;\n        Ok(GroupMemberInfo {\n            group_code,\n            uin: mem_info.uin,\n            gender: mem_info.sex as u8,\n            nickname: String::from_utf8_lossy(&mem_info.nick).into_owned(),\n            card_name: String::from_utf8_lossy(&mem_info.card).into_owned(),\n            level: mem_info.level as u16,\n            join_time: mem_info.join,\n            last_speak_time: mem_info.last_speak,\n            special_title: String::from_utf8_lossy(&mem_info.special_title).into_owned(),\n            special_title_expire_time: mem_info.special_title_expire_time as i64,\n            permission: match mem_info.role {\n                3 => GroupMemberPermission::Owner,\n                2 => GroupMemberPermission::Administrator,\n                _ => GroupMemberPermission::Member,\n            }, // TODO group owner\n            ..Default::default()\n        })\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/group_member_card/mod.rs",
    "content": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/heartbeat/builder.rs",
    "content": "use crate::protocol::packet::*;\n\nimpl super::super::super::Engine {\n    // Heartbeat.Alive\n    pub fn build_heartbeat_packet(&self) -> Packet {\n        let seq = self.next_seq();\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::NoEncrypt,\n            seq_id: seq as i32,\n            command_name: \"Heartbeat.Alive\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/heartbeat/mod.rs",
    "content": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/img_store/builder.rs",
    "content": "use crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    #[allow(clippy::too_many_arguments)]\n    pub fn build_group_image_store_packet(\n        &self,\n        group_code: i64,\n        file_name: String,\n        md5: Vec<u8>,\n        size: u64,\n        width: u32,\n        height: u32,\n        image_type: u32,\n    ) -> Packet {\n        let req = pb::cmd0x388::D388ReqBody {\n            net_type: Some(3),\n            subcmd: Some(1),\n            // TODO 支持多张图片？\n            tryup_img_req: vec![pb::cmd0x388::TryUpImgReq {\n                group_code: Some(group_code as u64),\n                src_uin: Some(self.uin() as u64),\n                file_md5: Some(md5),\n                file_size: Some(size),\n                file_name: Some(file_name.into_bytes()),\n                src_term: Some(5),\n                platform_type: Some(9),\n                bu_type: Some(1),\n                pic_type: Some(image_type),\n                pic_width: Some(width),\n                pic_height: Some(height),\n                build_ver: Some(self.transport.version.build_ver.as_bytes().to_vec()),\n                app_pic_type: Some(1006), // 1052?\n                ..Default::default()\n            }],\n            extension: Some(vec![]),\n            ..Default::default()\n        };\n        self.uni_packet(\"ImgStore.GroupPicUp\", req.to_bytes())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/img_store/decoder.rs",
    "content": "use bytes::Bytes;\nuse prost::Message;\n\nuse crate::command::img_store::GroupImageStoreResp;\nuse crate::common::RQAddr;\nuse crate::RQError::EmptyField;\nuse crate::{pb, RQError, RQResult};\n\nimpl super::super::super::Engine {\n    pub fn decode_group_image_store_response(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<GroupImageStoreResp> {\n        let mut rsp = pb::cmd0x388::D388RspBody::decode(&*payload)?;\n        let rsp = rsp.tryup_img_rsp.pop().ok_or(EmptyField(\"tryup_img_rsp\"))?;\n        if rsp.result() != 0 {\n            return Err(RQError::Other(\n                String::from_utf8_lossy(&rsp.fail_msg.unwrap_or_default()).into_owned(),\n            ));\n        }\n        Ok(if rsp.file_exit() {\n            GroupImageStoreResp::Exist {\n                file_id: rsp.fileid.unwrap_or_default(),\n                addrs: rsp\n                    .up_ip\n                    .into_iter()\n                    .zip(rsp.up_port)\n                    .map(|(ip, port)| RQAddr(ip, port as u16))\n                    .collect(),\n            }\n        } else {\n            GroupImageStoreResp::NotExist {\n                file_id: rsp.fileid.unwrap_or_default(),\n                upload_key: rsp.up_ukey.unwrap_or_default(),\n                upload_addrs: rsp\n                    .up_ip\n                    .into_iter()\n                    .zip(rsp.up_port)\n                    .map(|(ip, port)| RQAddr(ip, port as u16))\n                    .collect(),\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/img_store/mod.rs",
    "content": "use crate::common::RQAddr;\n\npub mod builder;\npub mod decoder;\n\n#[derive(Debug, Clone)]\npub enum GroupImageStoreResp {\n    Exist {\n        file_id: u64,\n        addrs: Vec<RQAddr>,\n    },\n    NotExist {\n        file_id: u64,\n        upload_key: Vec<u8>,\n        upload_addrs: Vec<RQAddr>,\n    },\n}\n"
  },
  {
    "path": "ricq-core/src/command/long_conn/builder.rs",
    "content": "use crate::command::common::PbToBytes;\nuse crate::protocol::packet::Packet;\n\nimpl crate::Engine {\n    // LongConn.OffPicUp\n    #[allow(clippy::too_many_arguments)]\n    pub fn build_off_pic_up_packet(\n        &self,\n        target: i64,\n        file_name: String,\n        md5: Vec<u8>,\n        size: u64,\n        width: u32,\n        height: u32,\n        image_type: u32,\n    ) -> Packet {\n        let req = crate::pb::cmd0x352::ReqBody {\n            subcmd: Some(1),\n            tryup_img_req: vec![crate::pb::cmd0x352::D352TryUpImgReq {\n                src_uin: Some(self.uin() as u64),\n                dst_uin: Some(target as u64),\n                file_name: Some(file_name.as_bytes().to_vec()), //todo\n                file_md5: Some(md5),\n                file_size: Some(size),\n                pic_width: Some(width),\n                pic_height: Some(height),\n                pic_type: Some(image_type),\n                pic_original: Some(true),\n                build_ver: Some(self.transport.version.build_ver.as_bytes().to_vec()),\n                bu_type: Some(1),\n                src_term: Some(5),\n                platform_type: Some(9),\n                ..Default::default()\n            }],\n            ..Default::default()\n        };\n        self.uni_packet(\"LongConn.OffPicUp\", req.to_bytes())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/long_conn/decoder.rs",
    "content": "use bytes::Bytes;\n\nuse crate::common::RQAddr;\nuse crate::{pb, RQError, RQResult};\nuse prost::Message;\n\nuse super::OffPicUpResp;\n\nimpl crate::Engine {\n    // LongConn.OffPicUp\n    pub fn decode_off_pic_up_response(&self, payload: Bytes) -> RQResult<OffPicUpResp> {\n        let mut resp = pb::cmd0x352::RspBody::decode(&*payload)?;\n        if let Some(err) = resp.fail_msg {\n            return Err(RQError::Other(String::from_utf8_lossy(&err).into_owned()));\n        }\n        if resp.subcmd() != 1 {\n            return Err(RQError::Other(format!(\n                \"subcmd is not 1: {}\",\n                resp.subcmd()\n            )));\n        }\n        let img = resp\n            .tryup_img_rsp\n            .pop()\n            .ok_or(RQError::EmptyField(\"tryup_img_rsp\"))?;\n\n        if img.result() != 0 {\n            return Err(RQError::Other(\n                String::from_utf8_lossy(&img.fail_msg.unwrap_or_default()).into_owned(),\n            ));\n        }\n        if img.file_exit() {\n            Ok(OffPicUpResp::Exist {\n                uuid: String::from_utf8_lossy(img.up_uuid()).into_owned(),\n                res_id: img.up_resid.unwrap_or_default(),\n            })\n        } else {\n            Ok(OffPicUpResp::UploadRequired {\n                uuid: String::from_utf8_lossy(img.up_uuid()).into_owned(),\n                res_id: img.up_resid.unwrap_or_default(),\n                upload_key: img.up_ukey.unwrap_or_default(),\n                upload_addrs: img\n                    .up_ip\n                    .into_iter()\n                    .zip(img.up_port)\n                    .map(|(ip, port)| RQAddr(ip, port as u16))\n                    .collect(),\n            })\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/long_conn/mod.rs",
    "content": "use crate::common::RQAddr;\n\nmod builder;\nmod decoder;\n\n#[derive(Debug, Clone)]\npub enum OffPicUpResp {\n    Exist {\n        res_id: String,\n        uuid: String,\n    },\n    UploadRequired {\n        res_id: String,\n        uuid: String,\n        upload_key: Vec<u8>,\n        upload_addrs: Vec<RQAddr>,\n    },\n}\n"
  },
  {
    "path": "ricq-core/src/command/longmsg/builder.rs",
    "content": "use crate::pb;\nuse prost::Message;\n\nimpl super::super::super::Engine {\n    pub fn build_long_req(&self, dst_uin: i64, msg_content: Vec<u8>, msg_ukey: Vec<u8>) -> Vec<u8> {\n        pb::longmsg::LongReqBody {\n            subcmd: 1,\n            term_type: 5,\n            platform_type: 9,\n            msg_up_req: vec![pb::longmsg::LongMsgUpReq {\n                msg_type: 3, // group\n                dst_uin,\n                msg_id: 0,\n                msg_content,\n                store_type: 2,\n                msg_ukey,\n                need_cache: 0,\n            }],\n            ..Default::default()\n        }\n        .encode_to_vec()\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/longmsg/mod.rs",
    "content": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/message_svc/builder.rs",
    "content": "use prost::Message;\n\nuse crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    // MessageSvc.PbSendMsg\n    #[allow(clippy::too_many_arguments)]\n    pub fn build_group_sending_packet(\n        &self,\n        group_code: i64,\n        elems: Vec<pb::msg::Elem>,\n        ptt: Option<pb::msg::Ptt>,\n        ran: i32,\n        pkg_num: i32,\n        pkg_index: i32,\n        pkg_div: i32,\n        forward: bool,\n    ) -> Packet {\n        let req = pb::msg::SendMessageRequest {\n            routing_head: Some(pb::msg::RoutingHead {\n                routing_head: Some(pb::msg::routing_head::RoutingHead::Grp(pb::msg::Grp {\n                    group_code: Some(group_code),\n                })),\n            }),\n            content_head: Some(pb::msg::ContentHead {\n                pkg_num: Some(pkg_num),\n                pkg_index: Some(pkg_index),\n                div_seq: Some(pkg_div),\n                ..Default::default()\n            }),\n            msg_body: Some(pb::msg::MessageBody {\n                rich_text: Some(pb::msg::RichText {\n                    elems,\n                    ptt,\n                    ..Default::default()\n                }),\n                ..Default::default()\n            }),\n            msg_seq: Some(self.next_group_seq()),\n            msg_rand: Some(ran),\n            // 群消息没有 sync_cookie\n            msg_via: Some(1), // 从哪进入界面(联系人列表/搜索/...)\n            msg_ctrl: if forward {\n                // 合并转发\n                Some(pb::msg::MsgCtrl { msg_flag: Some(4) })\n            } else {\n                None\n            },\n            ..Default::default()\n        };\n        self.uni_packet(\"MessageSvc.PbSendMsg\", req.to_bytes())\n    }\n\n    // build sync_cookie\n    fn sync_cookie(&self, time: i64) -> Vec<u8> {\n        if !self.transport.sig.sync_cookie.is_empty() {\n            return self.transport.sig.sync_cookie.to_vec();\n        }\n        pb::msg::SyncCookie {\n            time1: Some(time),\n            time: Some(time),\n            ran1: Some(rand::random::<u32>() as i64),\n            ran2: Some(rand::random::<u32>() as i64),\n            const1: Some(self.transport.sig.sync_const1 as i64),\n            const2: Some(self.transport.sig.sync_const2 as i64),\n            const3: Some(self.transport.sig.sync_const3 as i64),\n            last_sync_time: Some(time),\n            const4: Some(0),\n        }\n        .encode_to_vec()\n    }\n\n    // MessageSvc.PbGetMsg\n    pub fn build_get_message_request_packet(&self, flag: i32, time: i64) -> Packet {\n        // start = 0, continue = 1, stop = 2\n        let sync_cookie = self.sync_cookie(time);\n        let req = pb::msg::GetMessageRequest {\n            sync_flag: Some(flag),\n            sync_cookie: Some(sync_cookie),\n            latest_ramble_number: Some(20),\n            other_ramble_number: Some(3),\n            online_sync_flag: Some(1),\n            context_flag: Some(1),\n            msg_req_type: Some(1),\n            pubaccount_cookie: Some(vec![]),\n            msg_ctrl_buf: Some(vec![]),\n            server_buf: Some(vec![]),\n            ..Default::default()\n        };\n        self.uni_packet(\"MessageSvc.PbGetMsg\", req.to_bytes())\n    }\n\n    // MessageSvc.PbDeleteMsg\n    pub fn build_delete_message_request_packet(&self, items: Vec<pb::MessageItem>) -> Packet {\n        let body = pb::DeleteMessageRequest { items }.to_bytes();\n        self.uni_packet(\"MessageSvc.PbDeleteMsg\", body)\n    }\n\n    // MessageSvc.PbSendMsg\n    pub fn build_send_message_packet(\n        &self,\n        routing_head: pb::msg::routing_head::RoutingHead,\n        elems: Vec<pb::msg::Elem>,\n        ptt: Option<pb::msg::Ptt>,\n        seq: i32,\n        ran: i32,\n        time: i64,\n    ) -> Packet {\n        let sync_cookie = self.sync_cookie(time);\n        let req = pb::msg::SendMessageRequest {\n            routing_head: Some(pb::msg::RoutingHead {\n                routing_head: Some(routing_head),\n            }),\n            content_head: Some(pb::msg::ContentHead {\n                pkg_num: Some(1),\n                pkg_index: Some(0),\n                div_seq: Some(0),\n                ..Default::default()\n            }),\n            msg_body: Some(pb::msg::MessageBody {\n                rich_text: Some(pb::msg::RichText {\n                    elems,\n                    ptt,\n                    ..Default::default()\n                }),\n                ..Default::default()\n            }),\n            msg_seq: Some(seq),\n            msg_rand: Some(ran),\n            sync_cookie: Some(sync_cookie),\n            msg_via: Some(1),\n            ..Default::default()\n        };\n        self.uni_packet(\"MessageSvc.PbSendMsg\", req.to_bytes())\n    }\n\n    // MessageSvc.PbGetGroupMsg\n    pub fn build_get_group_msg_request(\n        &self,\n        group_code: i64,\n        begin_seq: i64,\n        end_seq: i64,\n    ) -> Packet {\n        let req = pb::msg::GetGroupMsgReq {\n            group_code: Some(group_code as u64),\n            begin_seq: Some(begin_seq as u64),\n            end_seq: Some(end_seq as u64),\n            public_group: Some(false),\n            ..Default::default()\n        };\n        self.uni_packet(\"MessageSvc.PbGetGroupMsg\", req.to_bytes())\n    }\n\n    pub fn build_friend_recall_packet(\n        &self,\n        uin: i64,\n        msg_time: i64,\n        seqs: Vec<i32>,\n        rands: Vec<i32>,\n    ) -> Packet {\n        let req = pb::msg::MsgWithDrawReq {\n            c2c_with_draw: vec![pb::msg::C2cMsgWithDrawReq {\n                msg_info: seqs\n                    .into_iter()\n                    .zip(rands)\n                    .map(|(seq, ran)| pb::msg::C2cMsgInfo {\n                        from_uin: Some(self.uin()),\n                        to_uin: Some(uin),\n                        msg_seq: Some(seq),\n                        msg_uid: Some(0x0100_0000_0000_0000 | (ran as i64 & 0xFFFFFFFF)), // TODO\n                        msg_time: Some(msg_time),\n                        msg_random: Some(ran),\n                        routing_head: Some(pb::msg::RoutingHead {\n                            routing_head: Some(pb::msg::routing_head::RoutingHead::C2c(\n                                pb::msg::C2c { to_uin: Some(uin) },\n                            )),\n                        }),\n                        ..Default::default()\n                    })\n                    .collect(),\n                long_message_flag: Some(0),\n                reserved: Some(vec![0x08, 0x00]), // TODO PB: GrpTmp {1: 1，2: group_uin}\n                sub_cmd: Some(1),\n            }],\n            ..Default::default()\n        };\n        self.uni_packet(\"PbMessageSvc.PbMsgWithDraw\", req.to_bytes())\n    }\n\n    pub fn build_group_recall_packet(\n        &self,\n        group_code: i64,\n        seqs: Vec<i32>,\n        rands: Vec<i32>,\n    ) -> Packet {\n        let req = pb::msg::MsgWithDrawReq {\n            group_with_draw: vec![pb::msg::GroupMsgWithDrawReq {\n                sub_cmd: Some(1),\n                group_code: Some(group_code),\n                user_def: Some(vec![0x08, 0x00]),\n                msg_list: seqs\n                    .into_iter()\n                    .zip(rands)\n                    .map(|(seq, ran)| pb::msg::GroupMsgInfo {\n                        msg_seq: Some(seq),\n                        msg_random: Some(ran),\n                        msg_type: Some(0),\n                    })\n                    .collect(),\n                group_type: None,\n            }],\n            ..Default::default()\n        };\n        self.uni_packet(\"PbMessageSvc.PbMsgWithDraw\", req.to_bytes())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/message_svc/decoder.rs",
    "content": "use bytes::{Buf, Bytes};\n\nuse crate::pb::msg::GetMessageResponse;\nuse crate::{jce, RQError, RQResult};\nuse prost::Message;\n\nimpl crate::Engine {\n    // MessageSvc.PushNotify\n    pub fn decode_svc_notify(&self, mut payload: Bytes) -> RQResult<jce::RequestPushNotify> {\n        payload.advance(4);\n        let mut req: jce::RequestPacket = jcers::from_buf(&mut payload)?;\n        let mut data: jce::RequestDataVersion2 = jcers::from_buf(&mut req.s_buffer)?;\n        let mut notify_data = data\n            .map\n            .remove(\"req_PushNotify\")\n            .ok_or_else(|| RQError::Decode(\"req_PushNotify\".into()))?\n            .remove(\"PushNotifyPack.RequestPushNotify\")\n            .ok_or_else(|| RQError::Decode(\"PushNotifyPack.RequestPushNotify\".into()))?;\n        notify_data.advance(1);\n        let notify: jce::RequestPushNotify = jcers::from_buf(&mut notify_data)?;\n        Ok(notify)\n    }\n\n    // MessageSvc.PushForceOffline\n    pub fn decode_force_offline(\n        &self,\n        mut payload: Bytes,\n    ) -> RQResult<jce::RequestPushForceOffline> {\n        let mut req: jce::RequestPacket = jcers::from_buf(&mut payload)?;\n        let mut data: jce::RequestDataVersion2 = jcers::from_buf(&mut req.s_buffer)?;\n        let mut data = data\n            .map\n            .remove(\"req_PushForceOffline\")\n            .ok_or_else(|| RQError::Decode(\"req_PushForceOffline\".into()))?\n            .remove(\"PushNotifyPack.RequestPushForceOffline\")\n            .ok_or_else(|| RQError::Decode(\"PushNotifyPack.RequestPushForceOffline\".into()))?;\n        data.advance(1);\n        let offline: jce::RequestPushForceOffline = jcers::from_buf(&mut data)?;\n        Ok(offline)\n    }\n\n    // MessageSvc.PbGetMsg\n    pub fn decode_message_svc_packet(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<super::MessageSyncResponse> {\n        let resp = GetMessageResponse::decode(&*payload)?;\n        Ok(super::MessageSyncResponse {\n            msg_rsp_type: resp.msg_rsp_type.unwrap_or_default(),\n            sync_flag: resp.sync_flag.unwrap_or(2), // default stop\n            sync_cookie: resp.sync_cookie,\n            pub_account_cookie: resp.pub_account_cookie,\n            msgs: resp\n                .uin_pair_msgs\n                .into_iter()\n                .flat_map(|x| x.messages)\n                .collect(),\n        })\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/message_svc/mod.rs",
    "content": "use crate::pb;\n\npub mod builder;\npub mod decoder;\n\npub struct MessageSyncResponse {\n    pub msg_rsp_type: i32,\n    pub sync_flag: i32,\n    pub sync_cookie: Option<Vec<u8>>,\n    pub pub_account_cookie: Option<Vec<u8>>,\n    pub msgs: Vec<pb::msg::Message>,\n}\n"
  },
  {
    "path": "ricq-core/src/command/mod.rs",
    "content": "pub mod common;\npub mod config_push_svc;\npub mod friendlist;\npub mod group_anonymous_generate_nick;\npub mod group_member_card;\npub mod heartbeat;\npub mod img_store;\npub mod long_conn;\npub mod longmsg;\npub mod message_svc;\npub mod multi_msg;\npub mod oidb_svc;\npub mod online_push;\npub mod pb_message_svc;\npub mod profile_service;\npub mod ptt_center_svr;\npub mod ptt_store;\npub mod reg_prxy_svc;\npub mod signature;\npub mod stat_svc;\npub mod summary_card;\npub mod visitor_svc;\npub mod wtlogin;\n"
  },
  {
    "path": "ricq-core/src/command/multi_msg/builder.rs",
    "content": "use std::collections::HashMap;\nuse std::io::Write;\n\nuse flate2::write::GzEncoder;\nuse flate2::Compression;\n\nuse crate::command::common::PbToBytes;\nuse crate::command::multi_msg::{ForwardMessage, PackedMessage};\nuse crate::msg::elem::RichMsg;\nuse crate::msg::MessageChain;\nuse crate::pb;\nuse crate::protocol::device::random_string;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    pub fn build_multi_msg_apply_down_req(&self, res_id: String) -> Packet {\n        let req = pb::multimsg::MultiReqBody {\n            subcmd: 2,\n            term_type: 5,\n            platform_type: 9,\n            net_type: 3,\n            build_ver: self.transport.version.build_ver.into(),\n            multimsg_applydown_req: vec![pb::multimsg::MultiMsgApplyDownReq {\n                msg_resid: res_id.into_bytes(),\n                msg_type: 3,\n                ..Default::default()\n            }],\n            bu_type: 2,\n            req_channel_type: 2,\n            ..Default::default()\n        };\n        self.uni_packet(\"MultiMsg.ApplyDown\", req.to_bytes())\n    }\n\n    pub fn build_multi_msg_apply_up_req(\n        &self,\n        msg_size: i64,\n        msg_md5: Vec<u8>,\n        bu_type: i32,\n        dst_uin: i64,\n    ) -> Packet {\n        let req = pb::multimsg::MultiReqBody {\n            subcmd: 1,\n            term_type: 5,\n            platform_type: 9,\n            net_type: 3,\n            build_ver: self.transport.version.build_ver.into(),\n            req_channel_type: 0,\n            multimsg_applyup_req: vec![pb::multimsg::MultiMsgApplyUpReq {\n                dst_uin,\n                msg_size,\n                msg_md5,\n                msg_type: 3, // group\n                ..Default::default()\n            }],\n            bu_type,\n            ..Default::default()\n        };\n        self.uni_packet(\"MultiMsg.ApplyUp\", req.to_bytes())\n    }\n\n    pub fn calculate_validation_data(\n        &self,\n        messages: Vec<super::ForwardMessage>,\n        group_code: i64,\n    ) -> Vec<u8> {\n        let PackedMessage {\n            mut buffer,\n            filename,\n        } = self.pack_forward_msg(messages, group_code);\n        let msgs = buffer.remove(&filename).expect(\"msgs not found\");\n        let mut pb_item_list = vec![pb::msg::PbMultiMsgItem {\n            file_name: Some(\"MultiMsg\".into()),\n            buffer: Some(pb::msg::PbMultiMsgNew { msg: msgs.clone() }),\n        }];\n        for (filename, msg) in buffer {\n            pb_item_list.push(pb::msg::PbMultiMsgItem {\n                file_name: Some(filename),\n                buffer: Some(pb::msg::PbMultiMsgNew { msg }),\n            });\n        }\n        let trans = pb::msg::PbMultiMsgTransmit {\n            msg: msgs,\n            pb_item_list,\n        };\n        let mut encoder = GzEncoder::new(vec![], Compression::default());\n        encoder.write_all(&trans.to_bytes()).ok();\n        encoder.finish().unwrap_or_default()\n    }\n\n    fn pack_forward_msg(\n        &self,\n        messages: Vec<super::ForwardMessage>,\n        group_code: i64,\n    ) -> PackedMessage {\n        let mut packed_buffers = HashMap::default();\n        let msgs: Vec<pb::msg::Message> = messages\n            .into_iter()\n            .map(|m| match m {\n                ForwardMessage::Message(message) => {\n                    self.pack_msg(message, group_code)\n                }\n                ForwardMessage::Forward(forward) => {\n                    let t_sum = forward.nodes.len();\n                    let preview = super::gen_forward_preview(&forward.nodes);\n                    let packed_message = self.pack_forward_msg(forward.nodes, group_code);\n                    packed_buffers.extend(packed_message.buffer);\n                    self.pack_msg(\n                        super::MessageNode {\n                            sender_id: forward.sender_id,\n                            time: forward.time,\n                            sender_name: forward.sender_name,\n                            elements: MessageChain(\n                                RichMsg {\n                                    template1: format!(\n                                        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>\"##,\n                                        packed_message.filename, t_sum, preview, t_sum\n                                    ),\n                                    service_id: 35,\n                                }.into()\n                            ),\n                        },\n                        group_code,\n                    )\n                }\n            })\n            .collect();\n        let filename = random_string(16);\n        packed_buffers.insert(filename.clone(), msgs);\n        PackedMessage {\n            filename,\n            buffer: packed_buffers,\n        }\n    }\n\n    fn pack_msg(&self, node: super::MessageNode, group_code: i64) -> pb::msg::Message {\n        pb::msg::Message {\n            head: Some(pb::msg::MessageHead {\n                from_uin: Some(node.sender_id),\n                msg_type: Some(82), // troop\n                msg_seq: Some(self.next_group_seq()),\n                msg_time: Some(node.time),\n                msg_uid: Some(0x01000000000000000 | rand::random::<u16>() as i64), // TODO ?\n                group_info: Some(pb::msg::GroupInfo {\n                    group_code: Some(group_code),\n                    group_card: Some(node.sender_name.into_bytes()),\n                    ..Default::default()\n                }),\n                mutiltrans_head: Some(pb::msg::MutilTransHead {\n                    status: Some(0),\n                    msg_id: Some(1),\n                }),\n                ..Default::default()\n            }),\n            body: Some(pb::msg::MessageBody {\n                rich_text: Some(pb::msg::RichText {\n                    elems: node.elements.into(),\n                    ..Default::default()\n                }),\n                ..Default::default()\n            }),\n            ..Default::default()\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/multi_msg/decoder.rs",
    "content": "use bytes::Bytes;\n\nuse crate::{pb, RQError, RQResult};\nuse prost::Message;\n\nimpl super::super::super::Engine {\n    pub fn decode_multi_msg_apply_down_resp(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<pb::multimsg::MultiMsgApplyDownRsp> {\n        pb::multimsg::MultiRspBody::decode(&*payload)?\n            .multimsg_applydown_rsp\n            .pop()\n            .ok_or(RQError::EmptyField(\"multimsg_applydown_rsp\"))\n    }\n\n    pub fn decode_multi_msg_apply_up_resp(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<pb::multimsg::MultiMsgApplyUpRsp> {\n        pb::multimsg::MultiRspBody::decode(&*payload)?\n            .multimsg_applyup_rsp\n            .pop()\n            .ok_or(RQError::EmptyField(\"multimsg_applyup_rsp\"))\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/multi_msg/mod.rs",
    "content": "use std::collections::HashMap;\nuse std::fmt::Write;\n\nuse crate::msg::MessageChain;\nuse crate::pb;\n\npub mod builder;\npub mod decoder;\n\npub enum ForwardMessage {\n    Message(MessageNode),\n    Forward(ForwardNode),\n}\n\npub fn gen_forward_preview(messages: &[ForwardMessage]) -> String {\n    let mut ret = String::new();\n    for msg in messages.iter().take(4) {\n        ret.push_str(r##\"<title size=\"26\" color=\"#777777\" maxLines=\"4\" lineSpace=\"12\">\"##);\n        match msg {\n            ForwardMessage::Message(v) => write!(&mut ret, \"{}: {}\", v.sender_name, v.elements),\n            ForwardMessage::Forward(v) => write!(&mut ret, \"{}: [转发消息]\", v.sender_name),\n        }\n        .unwrap();\n        ret.push_str(\"</title>\");\n    }\n    ret\n}\n\npub struct MessageNode {\n    pub sender_id: i64,\n    pub time: i32,\n    pub sender_name: String,\n    pub elements: MessageChain,\n}\n\nimpl From<MessageNode> for ForwardMessage {\n    fn from(n: MessageNode) -> Self {\n        Self::Message(n)\n    }\n}\n\npub struct ForwardNode {\n    pub sender_id: i64,\n    pub time: i32,\n    pub sender_name: String,\n    pub nodes: Vec<ForwardMessage>,\n}\n\nimpl From<ForwardNode> for ForwardMessage {\n    fn from(f: ForwardNode) -> Self {\n        Self::Forward(f)\n    }\n}\n\nstruct PackedMessage {\n    pub filename: String,\n    pub buffer: HashMap<String, Vec<pb::msg::Message>>,\n}\n"
  },
  {
    "path": "ricq-core/src/command/oidb_svc/builder.rs",
    "content": "use bytes::{BufMut, BytesMut};\n\nuse super::*;\nuse crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    // OidbSvc.0x4ff_9_IMCore\n    pub fn build_update_profile_detail_packet(&self, profile: ProfileDetailUpdate) -> Packet {\n        let mut w = BytesMut::new();\n        w.put_u32(self.uin() as u32);\n        w.put_u8(0);\n        w.put_u16(profile.0.len() as u16);\n        for (tag, value) in profile.0 {\n            w.put_u16(tag);\n            w.put_u16(value.len() as u16);\n            w.put_slice(&value);\n        }\n        let payload = self.transport.encode_oidb_packet(0x4ff, 9, w.freeze());\n        self.uni_packet(\"OidbSvc.0x4ff_9_IMCore\", payload)\n    }\n\n    // OidbSvc.0x88d_0\n    pub fn build_group_info_request_packet(&self, group_codes: Vec<i64>) -> Packet {\n        let body = pb::oidb::D88dReqBody {\n            app_id: Some(self.transport.version.app_id),\n            req_group_info: group_codes\n                .into_iter()\n                .map(|group_code| pb::oidb::ReqGroupInfo {\n                    group_code: Some(group_code as u64),\n                    stgroupinfo: Some(pb::oidb::D88dGroupInfo {\n                        group_owner: Some(0),\n                        group_uin: Some(0),\n                        group_create_time: Some(0),\n                        group_flag: Some(0),\n                        group_member_max_num: Some(0),\n                        group_member_num: Some(0),\n                        group_option: Some(0),\n                        group_level: Some(0),\n                        group_face: Some(0),\n                        group_name: Some(vec![]),\n                        group_memo: Some(vec![]),\n                        group_finger_memo: Some(vec![]),\n                        group_last_msg_time: Some(0),\n                        group_cur_msg_seq: Some(0),\n                        group_question: Some(vec![]),\n                        group_answer: Some(vec![]),\n                        group_grade: Some(0),\n                        active_member_num: Some(0),\n                        head_portrait_seq: Some(0),\n                        msg_head_portrait: Some(pb::oidb::D88dGroupHeadPortrait::default()),\n                        st_group_ex_info: Some(pb::oidb::D88dGroupExInfoOnly::default()),\n                        group_sec_level: Some(0),\n                        cmduin_privilege: Some(0),\n                        no_finger_open_flag: Some(0),\n                        no_code_finger_open_flag: Some(0),\n                        ..Default::default()\n                    }),\n                    ..Default::default()\n                })\n                .collect(),\n            pc_client_version: Some(0),\n        };\n        let payload = self.transport.encode_oidb_packet(0x88d, 0, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x88d_0\", payload)\n    }\n\n    // OidbSvc.0x570_8\n    pub fn build_group_mute_packet(\n        &self,\n        group_code: i64,\n        member_uin: i64,\n        duration: u32,\n    ) -> Packet {\n        let mut w = BytesMut::new();\n        w.put_u32(group_code as u32);\n        w.put_u8(32);\n        w.put_u16(1);\n        w.put_u32(member_uin as u32);\n        w.put_u32(duration);\n        let payload = self.transport.encode_oidb_packet(0x570, 8, w.freeze());\n        self.uni_packet(\"OidbSvc.0x570_8\", payload)\n    }\n\n    // OidbSvc.0x89a_0\n    fn build_group_operation_packet(&self, body: pb::oidb::D89aReqBody) -> Packet {\n        let payload = self.transport.encode_oidb_packet(0x89a, 0, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x89a_0\", payload)\n    }\n\n    // OidbSvc.0x89a_0\n    pub fn build_group_mute_all_packet(&self, group_code: i64, mute: bool) -> Packet {\n        let shut_up_time: i32 = if mute { 268435455 } else { 0 };\n        let body = pb::oidb::D89aReqBody {\n            group_code,\n            st_group_info: Some(pb::oidb::D89aGroupinfo {\n                shutup_time: Some(pb::oidb::d89a_groupinfo::ShutupTime::Val(shut_up_time)),\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        self.build_group_operation_packet(body)\n    }\n\n    // OidbSvc.0x89a_0\n    pub fn build_group_name_update_packet(&self, group_code: i64, name: String) -> Packet {\n        let body = pb::oidb::D89aReqBody {\n            group_code,\n            st_group_info: Some(pb::oidb::D89aGroupinfo {\n                ing_group_name: name.as_bytes().to_vec(),\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        self.build_group_operation_packet(body)\n    }\n\n    // OidbSvc.0x89a_0\n    pub fn build_group_memo_update_packet(&self, group_code: i64, memo: String) -> Packet {\n        let body = pb::oidb::D89aReqBody {\n            group_code,\n            st_group_info: Some(pb::oidb::D89aGroupinfo {\n                ing_group_memo: memo.as_bytes().to_vec(),\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        self.build_group_operation_packet(body)\n    }\n\n    // OidbSvc.0x8a0_0\n    pub fn build_group_kick_packet(\n        &self,\n        group_code: i64,\n        member_uins: Vec<i64>,\n        kick_msg: &str,\n        block: bool,\n    ) -> Packet {\n        let flag_block = if block { 1 } else { 0 };\n        let body = pb::oidb::D8a0ReqBody {\n            opt_uint64_group_code: group_code,\n            msg_kick_list: member_uins\n                .into_iter()\n                .map(|member_uin| pb::oidb::D8a0KickMemberInfo {\n                    opt_uint32_operate: 5,\n                    opt_uint64_member_uin: member_uin,\n                    opt_uint32_flag: flag_block,\n                    ..Default::default()\n                })\n                .collect(),\n            kick_msg: kick_msg.as_bytes().to_vec(),\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0x8a0, 0, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x8a0_0\", payload)\n    }\n\n    // OidbSvc.0xed3\n    pub fn build_group_poke_packet(&self, group_code: i64, target: i64) -> Packet {\n        let body = pb::oidb::Ded3ReqBody {\n            to_uin: target,\n            group_code,\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0xed3, 1, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0xed3\", payload)\n    }\n\n    // OidbSvc.0xed3\n    pub fn build_friend_poke_packet(&self, target: i64) -> Packet {\n        let body = pb::oidb::Ded3ReqBody {\n            to_uin: target,\n            aio_uin: target,\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0xed3, 1, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0xed3\", payload)\n    }\n\n    // OidbSvc.0x55c_1\n    pub fn build_group_admin_set_packet(&self, group_code: i64, member: i64, flag: bool) -> Packet {\n        let mut w = BytesMut::new();\n        w.put_u32(group_code as u32);\n        w.put_u32(member as u32);\n        w.put_u8(if flag { 0x01 } else { 0x00 });\n        let payload = self.transport.encode_oidb_packet(0x55c, 1, w.freeze());\n        self.uni_packet(\"OidbSvc.0x55c_1\", payload)\n    }\n\n    // OidbSvc.0x758\n    pub fn build_group_invite_packet(&self, group_code: i64, uin: i64) -> Packet {\n        let body = pb::oidb::D758ReqBody {\n            join_group_code: Some(group_code as u64),\n            be_invited_uin_info: vec![pb::oidb::InviteUinInfo {\n                uin: Some(uin as u64),\n                ..Default::default()\n            }],\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0x758, 1, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x758\", payload)\n    }\n\n    // OidbSvc.0x8a7_0\n    pub fn build_group_at_all_remain_request_packet(&self, group_code: i64) -> Packet {\n        let body = pb::oidb::D8a7ReqBody {\n            sub_cmd: Some(1),\n            limit_interval_type_for_uin: Some(2),\n            limit_interval_type_for_group: Some(1),\n            uin: Some(self.uin() as u64),\n            group_code: Some(group_code as u64),\n        };\n        let payload = self.transport.encode_oidb_packet(0x8a7, 0, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x8a7_0\", payload)\n    }\n\n    // OidbSvc.0x8fc_2\n    pub fn build_edit_special_title_packet(\n        &self,\n        group_code: i64,\n        member_uin: i64,\n        new_title: String,\n    ) -> Packet {\n        let body = pb::oidb::D8fcReqBody {\n            group_code: Some(group_code),\n            mem_level_info: vec![pb::oidb::D8fcMemberInfo {\n                uin: Some(member_uin),\n                uin_name: Some(new_title.as_bytes().to_vec()),\n                special_title: Some(new_title.as_bytes().to_vec()),\n                special_title_expire_time: Some(-1),\n                ..Default::default()\n            }],\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0x8fc, 2, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x8fc_2\", payload)\n    }\n\n    // OidbSvc.0x990\n    pub fn build_translate_request_packet(\n        &self,\n        src_language: String,\n        dst_language: String,\n        src_text_list: Vec<String>,\n    ) -> Packet {\n        let body = pb::oidb::TranslateReqBody {\n            batch_translate_req: Some(pb::oidb::BatchTranslateReq {\n                src_language,\n                dst_language,\n                src_text_list,\n            }),\n        };\n        let payload = self.transport.encode_oidb_packet(0x990, 2, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x990\", payload)\n    }\n\n    // OidbSvc.0xeac\n    pub fn build_essence_msg_operate_packet(\n        &self,\n        group_code: i64,\n        msg_seq: i32,\n        msg_rand: i32,\n        flag: bool,\n    ) -> Packet {\n        let body = pb::oidb::EacReqBody {\n            group_code: Some(group_code as u64),\n            seq: Some(msg_seq as u32),\n            random: Some(msg_rand as u32),\n        };\n        let payload =\n            self.transport\n                .encode_oidb_packet(0xeac, if flag { 1 } else { 2 }, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0xeac\", payload)\n    }\n\n    // OidbSvc.0xe07_0\n    pub fn build_image_ocr_request_packet(\n        &self,\n        url: String,\n        md5: String,\n        size: i32,\n        wight: i32,\n        height: i32,\n    ) -> Packet {\n        let body = pb::oidb::De07ReqBody {\n            version: 1,\n            entrance: 3,\n            ocr_req_body: Some(pb::oidb::OcrReqBody {\n                image_url: url,\n                origin_md5: md5.clone(),\n                after_compress_md5: md5,\n                after_compress_file_size: size,\n                after_compress_height: height,\n                after_compress_weight: wight,\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0xe07, 0, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0xe07_0\", payload)\n    }\n\n    pub fn build_share_music_request_packet(\n        &self,\n        share_target: ShareTarget,\n        music_share: MusicShare,\n        music_version: MusicVersion,\n    ) -> Packet {\n        let body = pb::oidb::Db77ReqBody {\n            app_id: music_version.app_id,\n            app_type: music_version.app_type,\n            msg_style: if music_share.music_url.is_empty() {\n                0\n            } else {\n                4\n            },\n            client_info: Some(pb::oidb::Db77ClientInfo {\n                platform: music_version.platform,\n                sdk_version: music_version.sdk_version.into(),\n                android_package_name: music_version.package_name.into(),\n                android_signature: music_version.signature.into(),\n                ..Default::default()\n            }),\n            send_type: share_target.send_type(),\n            recv_uin: match share_target {\n                ShareTarget::Friend(uin) => uin as u64,\n                ShareTarget::Group(code) => code as u64,\n                ShareTarget::Guild { channel_id, .. } => channel_id,\n            },\n            rich_msg_body: Some(pb::oidb::Db77RichMsgBody {\n                title: music_share.title,\n                summary: music_share.summary,\n                brief: music_share.brief,\n                url: music_share.url,\n                picture_url: music_share.picture_url,\n                music_url: music_share.music_url,\n                ..Default::default()\n            }),\n            recv_guild_id: if let ShareTarget::Guild { guild_id, .. } = share_target {\n                guild_id\n            } else {\n                0\n            },\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0xb77, 9, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0xb77_9\", payload)\n    }\n\n    pub fn build_share_link_request_packet(\n        &self,\n        share_target: ShareTarget,\n        link_share: LinkShare,\n    ) -> Packet {\n        let body = pb::oidb::Db77ReqBody {\n            app_id: 100446242,\n            app_type: 1,\n            msg_style: 0,\n            client_info: Some(pb::oidb::Db77ClientInfo {\n                platform: 1,\n                sdk_version: \"0.0.0\".into(),\n                android_package_name: \"com.tencent.mtt\".into(),\n                android_signature: \"d8391a394d4a179e6fe7bdb8a301258b\".into(),\n                ..Default::default()\n            }),\n            send_type: share_target.send_type(),\n            recv_uin: match share_target {\n                ShareTarget::Friend(uin) => uin as u64,\n                ShareTarget::Group(code) => code as u64,\n                ShareTarget::Guild { channel_id, .. } => channel_id,\n            },\n            rich_msg_body: Some(pb::oidb::Db77RichMsgBody {\n                summary: link_share.summary.unwrap_or_default(),\n                brief: link_share\n                    .brief\n                    .unwrap_or_else(|| format!(\"[分享] {}\", link_share.title)),\n                title: link_share.title,\n                url: link_share.url,\n                picture_url: link_share.picture_url.unwrap_or_else(|| \"none\".into()), // \"none\" will use default icon\n                music_url: String::new(),\n                action: String::new(),\n            }),\n            recv_guild_id: if let ShareTarget::Guild { guild_id, .. } = share_target {\n                guild_id\n            } else {\n                0\n            },\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0xb77, 9, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0xb77_9\", payload)\n    }\n\n    // OidbSvc.0x899_0\n    pub fn build_get_group_admin_list_request_packet(&self, group_code: u64) -> Packet {\n        let body = pb::cmd0x899::ReqBody {\n            group_code: Some(group_code),\n            start_uin: Some(0),\n            identify_flag: Some(2),\n            memberlist_opt: Some(pb::cmd0x899::Memberlist {\n                member_uin: Some(0),\n                privilege: Some(1),\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0x899, 0, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x899_0\", payload)\n    }\n\n    // OidbSvc.0xeb7\n    pub fn build_group_sign_in_packet(&self, group_code: i64) -> Packet {\n        let body = pb::oidb::Deb7ReqBody {\n            sign_in_write_req: Some(pb::oidb::StSignInWriteReq {\n                uid: Some(self.uin().to_string()),\n                group_id: Some(group_code.to_string()),\n                client_version: Some(self.transport.version.sort_version_name.to_string()),\n            }),\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0xeb7, 1, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0xeb7\", payload)\n    }\n    // OidbSvc.0x6d8_1\n    pub fn build_group_file_list_request_packet(\n        &self,\n        group_code: u64,\n        folder_id: String,\n        start_index: u32,\n    ) -> Packet {\n        let body = pb::oidb::D6d8ReqBody {\n            file_list_info_req: Some(pb::oidb::GetFileListReqBody {\n                group_code: Some(group_code),\n                app_id: Some(3),\n                folder_id: Some(folder_id),\n                file_count: Some(20),\n                all_file_count: Some(0),\n                req_from: Some(3),\n                sort_by: Some(1),\n                filter_code: Some(0),\n                uin: Some(0),\n                start_index: Some(start_index),\n                context: None,\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0x6d8, 1, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x6d8_1\", payload)\n    }\n    // OidbSvc.0x6d6_2\n    pub fn build_group_file_download_request_packet(\n        &self,\n        group_code: i64,\n        file_id: String,\n        bus_id: i32,\n    ) -> Packet {\n        let body = pb::oidb::D6d6ReqBody {\n            download_file_req: Some(pb::oidb::DownloadFileReqBody {\n                file_id: Some(file_id),\n                group_code: Some(group_code),\n                app_id: Some(3),\n                bus_id: Some(bus_id),\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(1750, 2, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x6d6_2\", payload)\n    }\n    // OidbSvc.0x6d8_1\n    pub fn build_group_file_count_request_packet(&self, group_code: u64) -> Packet {\n        let body = pb::oidb::D6d8ReqBody {\n            group_file_count_req: Some(pb::oidb::GetFileCountReqBody {\n                group_code: Some(group_code),\n                app_id: Some(3),\n                bus_id: Some(0),\n            }),\n            ..Default::default()\n        };\n        let payload = self.transport.encode_oidb_packet(0x6d8, 2, body.to_bytes());\n        self.uni_packet(\"OidbSvc.0x6d8_1\", payload)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/oidb_svc/decoder.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::{Bytes, BytesMut};\n\nuse crate::command::oidb_svc::GroupAtAllRemainInfo;\nuse crate::structs::{\n    GroupFileCount, GroupFileInfo, GroupFileItem, GroupFileList, GroupFolderInfo, GroupInfo,\n    GroupMemberPermission,\n};\nuse crate::{pb, RQResult};\nuse prost::Message;\n\nuse super::OcrResponse;\n\nimpl super::super::super::Engine {\n    // OidbSvc.0x88d_0\n    pub fn decode_group_info_response(&self, payload: Bytes) -> RQResult<Vec<GroupInfo>> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let groups = pb::oidb::D88dRspBody::decode(&*pkg.bodybuffer)?.rsp_group_info;\n        Ok(groups\n            .into_iter()\n            .filter_map(|g| {\n                let code = g.group_code? as i64;\n                let info = g.group_info?;\n                Some(GroupInfo {\n                    uin: info.group_uin? as i64,\n                    code,\n                    name: String::from_utf8_lossy(&info.group_name?).into_owned(),\n                    memo: String::from_utf8_lossy(&info.group_memo?).into_owned(),\n                    owner_uin: info.group_owner? as i64,\n                    group_create_time: info.group_create_time.unwrap_or_default(),\n                    group_level: info.group_level.unwrap_or_default(),\n                    member_count: info.group_member_num? as u16,\n                    max_member_count: info.group_member_max_num? as u16,\n                    shut_up_timestamp: info.shutup_timestamp.unwrap_or_default() as i64,\n                    my_shut_up_timestamp: info.shutup_timestamp_me.unwrap_or_default() as i64,\n                    last_msg_seq: info.group_cur_msg_seq.unwrap_or_default() as i64,\n                })\n            })\n            .collect())\n    }\n\n    // // OidbSvc.0x8a7_0\n    pub fn decode_group_at_all_remain_response(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<GroupAtAllRemainInfo> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let rsp = pb::oidb::D8a7RspBody::decode(&*pkg.bodybuffer)?;\n        Ok(GroupAtAllRemainInfo {\n            can_at_all: rsp.can_at_all(),\n            remain_at_all_count_for_group: rsp.remain_at_all_count_for_group(),\n            remain_at_all_count_for_uin: rsp.remain_at_all_count_for_uin(),\n        })\n    }\n\n    // OidbSvc.0x990\n    pub fn decode_translate_response(&self, payload: Bytes) -> RQResult<Vec<String>> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let rsp = pb::oidb::TranslateRspBody::decode(&*pkg.bodybuffer)?;\n        Ok(rsp.batch_translate_rsp.unwrap_or_default().dst_text_list)\n    }\n\n    // OidbSvc.0xeac_1/2\n    pub fn decode_essence_msg_response(&self, payload: Bytes) -> RQResult<pb::oidb::EacRspBody> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let resp = pb::oidb::EacRspBody::decode(&*pkg.bodybuffer)?;\n        Ok(resp)\n    }\n\n    // OidbSvc.0xe07_0\n    pub fn decode_image_ocr_response(&self, payload: Bytes) -> RQResult<OcrResponse> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let resp = pb::oidb::De07RspBody::decode(&*pkg.bodybuffer)?;\n        Ok(OcrResponse {\n            texts: resp\n                .ocr_rsp_body\n                .clone()\n                .unwrap_or_default()\n                .text_detections,\n            language: resp.ocr_rsp_body.unwrap_or_default().language,\n        })\n    }\n\n    // OidbSvc.0x899_0\n    pub fn decode_get_group_admin_list_response(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<HashMap<i64, GroupMemberPermission>> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let resp = pb::cmd0x899::RspBody::decode(&*pkg.bodybuffer)?;\n        Ok(resp\n            .memberlist\n            .into_iter()\n            .map(|mem| {\n                (\n                    mem.member_uin.unwrap_or_default() as i64,\n                    if mem.privilege == Some(1) {\n                        GroupMemberPermission::Owner\n                    } else if mem.privilege == Some(2) {\n                        GroupMemberPermission::Administrator\n                    } else {\n                        GroupMemberPermission::Member\n                    },\n                )\n            })\n            .collect())\n    }\n    // OidbSvc.0x6d8_1\n    pub fn decode_group_file_list_response(&self, payload: Bytes) -> RQResult<GroupFileList> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let resp = pb::oidb::D6d8RspBody::decode(&*pkg.bodybuffer)?;\n        let resp = &resp.file_list_info_rsp.unwrap_or_default();\n        Ok(GroupFileList {\n            all_file_count: resp.all_file_count(),\n            is_end: resp.is_end(),\n            items: resp\n                .item_list\n                .clone()\n                .into_iter()\n                .map(|f| {\n                    if let Some(fi) = f.file_info {\n                        let folder_info = f.folder_info.unwrap_or_default();\n                        GroupFileItem {\n                            file_info: GroupFileInfo {\n                                file_id: fi.file_id().to_string(),\n                                bus_id: fi.bus_id(),\n                                file_name: fi.file_name().to_string(),\n                                sha: format!(\"{:x}\", BytesMut::from(fi.sha())),\n                                dead_time: fi.dead_time(),\n                                file_size: fi.file_size(),\n                                upload_time: fi.upload_time(),\n                                uploader_uin: fi.uploader_uin(),\n                                uploader_name: fi.uploader_name().to_string(),\n                                parent_folder_id: fi.parent_folder_id().to_string(),\n                                local_path: fi.local_path().to_string(),\n                                modify_time: fi.modify_time(),\n                                download_times: fi.download_times(),\n                                md5: Bytes::from(fi.md5.unwrap_or_default()),\n                                sha3: Bytes::from(fi.sha3.unwrap_or_default()),\n                                uploaded_size: fi.uploaded_size.unwrap_or_default(),\n                            },\n                            folder_info: GroupFolderInfo {\n                                create_time: folder_info.create_time(),\n                                create_uin: folder_info.create_uin(),\n                                creator_name: folder_info.creator_name.unwrap_or_default(),\n                                folder_id: folder_info.folder_id.unwrap_or_default(),\n                                folder_name: folder_info.folder_name.unwrap_or_default(),\n                                modify_time: folder_info.modify_time.unwrap_or_default(),\n                                parent_folder_id: folder_info.parent_folder_id.unwrap_or_default(),\n                                total_file_count: folder_info.total_file_count.unwrap_or_default(),\n                            },\n                            r#type: f.r#type.unwrap_or_default(),\n                        }\n                    } else {\n                        GroupFileItem::default()\n                    }\n                })\n                .collect(),\n            next_index: resp.next_index(),\n            role: resp.role(),\n        })\n    }\n    // OidbSvc.0x6d6_2\n    pub fn decode_group_file_download_response(\n        &self,\n        payload: Bytes,\n        filename: &str,\n    ) -> RQResult<String> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let resp = pb::oidb::D6d6RspBody::decode(&*pkg.bodybuffer)?;\n        let f_rsp = resp.download_file_rsp.unwrap();\n        Ok(format!(\n            \"http://{}/ftn_handler/{:x}/?fname={}\",\n            f_rsp.download_ip(),\n            BytesMut::from(f_rsp.download_url()),\n            filename\n        ))\n    }\n    // OidbSvc.0x6d8_1\n    pub fn decode_group_file_count_response(&self, payload: Bytes) -> RQResult<GroupFileCount> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let resp = pb::oidb::D6d8RspBody::decode(&*pkg.bodybuffer)?;\n        if let Some(file_count_rsp) = resp.file_count_rsp {\n            Ok(GroupFileCount {\n                is_full: file_count_rsp.is_full.unwrap_or_default(),\n                all_file_count: file_count_rsp.all_file_count.unwrap_or_default(),\n                limit_count: file_count_rsp.limit_count.unwrap_or_default(),\n                file_too_many: file_count_rsp.file_too_many.unwrap_or_default(),\n            })\n        } else {\n            Err(crate::RQError::GetFileCountFailed)\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/oidb_svc/mod.rs",
    "content": "use std::collections::HashMap;\n\nuse crate::pb;\n\npub mod builder;\npub mod decoder;\n\n// 群 @全体 剩余次数\n#[derive(Default, Debug)]\npub struct GroupAtAllRemainInfo {\n    pub can_at_all: bool,\n    pub remain_at_all_count_for_group: u32,\n    pub remain_at_all_count_for_uin: u32,\n}\n\npub struct OcrResponse {\n    pub texts: Vec<pb::oidb::TextDetection>,\n    pub language: String,\n}\n\n// 编辑个人资料\n#[derive(Default, Debug)]\npub struct ProfileDetailUpdate(pub HashMap<u16, Vec<u8>>);\n\nimpl ProfileDetailUpdate {\n    pub fn new() -> Self {\n        Self::default()\n    }\n    pub fn name(&mut self, value: String) {\n        self.0.insert(20002, value.into_bytes());\n    }\n    pub fn email(&mut self, value: String) {\n        self.0.insert(20011, value.into_bytes());\n    }\n    pub fn personal_note(&mut self, value: String) {\n        self.0.insert(20019, value.into_bytes());\n    }\n    pub fn company(&mut self, value: String) {\n        self.0.insert(24008, value.into_bytes());\n    }\n    pub fn college(&mut self, value: String) {\n        self.0.insert(20021, value.into_bytes());\n    }\n}\n\npub enum ShareTarget {\n    Friend(i64),\n    Group(i64),\n    Guild { guild_id: u64, channel_id: u64 },\n}\n\nimpl ShareTarget {\n    pub fn send_type(&self) -> u32 {\n        match self {\n            ShareTarget::Friend { .. } => 0,\n            ShareTarget::Group { .. } => 1,\n            ShareTarget::Guild { .. } => 3,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Default)]\npub struct MusicShare {\n    pub title: String,\n    pub brief: String,\n    pub summary: String,\n    pub url: String,\n    pub picture_url: String,\n    pub music_url: String,\n}\n\n#[derive(Debug, Clone)]\npub struct MusicVersion {\n    pub app_id: u64,\n    pub app_type: u32,\n    pub platform: u32,\n    pub sdk_version: &'static str,\n    pub package_name: &'static str,\n    pub signature: &'static str,\n}\n\nimpl MusicVersion {\n    pub const QQ: MusicVersion = MusicVersion {\n        app_id: 100497308,\n        app_type: 1,\n        platform: 1,\n        sdk_version: \"0.0.0\",\n        package_name: \"com.tencent.qqmusic\",\n        signature: \"cbd27cd7c861227d013a25b2d10f0799\",\n    };\n\n    pub const NETEASE: MusicVersion = MusicVersion {\n        app_id: 100495085,\n        app_type: 1,\n        platform: 1,\n        sdk_version: \"0.0.0\",\n        package_name: \"com.netease.cloudmusic\",\n        signature: \"da6b069da1e2982db3e386233f68d76d\",\n    };\n\n    pub const MIGU: MusicVersion = MusicVersion {\n        app_id: 1101053067,\n        app_type: 1,\n        platform: 1,\n        sdk_version: \"0.0.0\",\n        package_name: \"cmccwm.mobilemusic\",\n        signature: \"6cdc72a439cef99a3418d2a78aa28c73\",\n    };\n\n    pub const KUGOU: MusicVersion = MusicVersion {\n        app_id: 205141,\n        app_type: 1,\n        platform: 1,\n        sdk_version: \"0.0.0\",\n        package_name: \"com.kugou.android\",\n        signature: \"fe4a24d80fcf253a00676a808f62c2c6\",\n    };\n\n    pub const KUWO: MusicVersion = MusicVersion {\n        app_id: 100243533,\n        app_type: 1,\n        platform: 1,\n        sdk_version: \"0.0.0\",\n        package_name: \"cn.kuwo.player\",\n        signature: \"bf9ff4ffb4c558a34ee3fd52c223ebf5\",\n    };\n}\n\n#[derive(Debug, Clone, Default)]\npub struct LinkShare {\n    pub title: String,\n    pub summary: Option<String>,\n    /// 从消息列表中看到的文字,默认为 \"[分享]\" + title\n    pub brief: Option<String>,\n    /// 预览图网址, 默认为 QQ 浏览器图标,似乎对域名有限制\n    pub picture_url: Option<String>,\n    pub url: String,\n}\n"
  },
  {
    "path": "ricq-core/src/command/online_push/builder.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::Bytes;\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;\nuse crate::jce;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    // OnlinePush.RespPush\n    pub fn build_delete_online_push_packet(\n        &self,\n        uin: i64,\n        svrip: i32,\n        push_token: Bytes,\n        seq: u16,\n        del_msg: Vec<jce::PushMessageInfo>,\n    ) -> Packet {\n        let req = jce::SvcRespPushMsg {\n            uin,\n            svrip,\n            push_token,\n            del_infos: del_msg\n                .into_iter()\n                .map(|m| jce::DelMsgInfo {\n                    from_uin: m.from_uin,\n                    msg_time: m.msg_time,\n                    msg_seq: m.msg_seq,\n                    msg_cookies: m.msg_cookies,\n                    ..Default::default()\n                })\n                .collect(),\n            ..Default::default()\n        };\n        let b = pack_uni_request_data(&req.freeze());\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\"resp\".to_string(), b)]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            i_request_id: seq as i32,\n            s_servant_name: \"OnlinePush\".to_string(),\n            s_func_name: \"SvcRespPushMsg\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"OnlinePush.RespPush\", pkt.freeze())\n    }\n\n    pub fn build_sid_ticket_expired_response(&self, seq: i32) -> Packet {\n        self.uni_packet_with_seq(seq, \"OnlinePush.SidTicketExpired\", Bytes::new())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/online_push/decoder.rs",
    "content": "use bytes::{Buf, Bytes};\nuse jcers::Jce;\n\nuse super::*;\nuse crate::common::group_uin2code;\nuse crate::structs::{GroupDisband, GroupLeave, GroupMemberPermission, MemberPermissionChange};\nuse crate::{jce, pb, RQError, RQResult};\nuse prost::Message;\n\nimpl super::super::super::Engine {\n    // 解析群消息分片 长消息需要合并\n    // OnlinePush.PbPushGroupMsg\n    pub fn decode_group_message_packet(&self, payload: Bytes) -> RQResult<GroupMessagePart> {\n        let message = pb::msg::PushMessagePacket::decode(&*payload)?;\n        (|| {\n            let msg = message.message.ok_or(\"message\")?;\n            let head = msg.head.ok_or(\"head\")?;\n            let body = msg.body.ok_or(\"body\")?;\n            let content = msg.content.ok_or(\"content\")?;\n            let rich_text = body.rich_text.ok_or(\"rich_text\")?;\n            let group_info = head.group_info.ok_or(\"group_info\")?;\n            Ok(GroupMessagePart {\n                seq: head.msg_seq.ok_or(\"msg_seq\")?,\n                rand: rich_text.attr.ok_or(\"attr\")?.random.ok_or(\"attr.random\")?,\n                group_code: group_info.group_code.ok_or(\"group_info.group_code\")?,\n                group_name: String::from_utf8_lossy(\n                    &group_info.group_name.ok_or(\"group_info.group_name\")?,\n                )\n                .into_owned(),\n                group_card: String::from_utf8_lossy(\n                    &group_info.group_card.ok_or(\"group_info.group_card\")?,\n                )\n                .into_owned(),\n                from_uin: head.from_uin.ok_or(\"from_uin\")?,\n                elems: rich_text.elems,\n                time: head.msg_time.ok_or(\"msg_time\")?,\n                pkg_num: content.pkg_num.ok_or(\"pkg_num\")?,\n                pkg_index: content.pkg_index.ok_or(\"pkg_index\")?,\n                div_seq: content.div_seq.ok_or(\"div_seq\")?,\n                ptt: rich_text.ptt,\n            })\n        })()\n        .map_err(|e: &'static str| RQError::Decode(format!(\"{e} is none\")))\n    }\n\n    // OnlinePush.ReqPush\n    pub fn decode_online_push_req_packet(&self, mut payload: Bytes) -> RQResult<ReqPush> {\n        let mut request: jce::RequestPacket = jcers::from_buf(&mut payload)?;\n        let mut data: jce::RequestDataVersion2 = jcers::from_buf(&mut request.s_buffer)?;\n        let mut req = data\n            .map\n            .remove(\"req\")\n            .ok_or_else(|| RQError::Decode(\"req is none\".into()))?;\n        let mut msg = req\n            .remove(\"OnlinePushPack.SvcReqPushMsg\")\n            .ok_or_else(|| RQError::Decode(\"OnlinePushPack.SvcReqPushMsg is none\".into()))?;\n        msg.advance(1);\n        let mut jr = Jce::new(&mut msg);\n        let uin: i64 = jr.get_by_tag(0)?;\n        let msg_infos: Vec<jce::PushMessageInfo> = jr.get_by_tag(2)?;\n\n        Ok(ReqPush { uin, msg_infos })\n    }\n\n    pub fn decode_online_push_trans_packet(&self, payload: Bytes) -> RQResult<OnlinePushTrans> {\n        let info = pb::msg::TransMsgInfo::decode(&*payload)?;\n        let msg_seq = info.msg_seq.unwrap_or_default();\n        let msg_uid = info.msg_uid.unwrap_or_default();\n        let msg_time = info.msg_time.unwrap_or_default();\n        let group_uin = info.from_uin.ok_or_else(|| {\n            RQError::Decode(\"decode_online_push_trans_packet from_uin is 0\".to_string())\n        })?;\n        let mut data = Bytes::from(\n            info.msg_data\n                .ok_or_else(|| RQError::Decode(\"msg_data is none\".into()))?,\n        );\n        // 去重暂时不做\n        match info.msg_type {\n            Some(34) => {\n                data.get_i32();\n                data.get_u8();\n                let target = data.get_u32() as i64;\n                let typ = data.get_u8() as i32;\n                let operator = data.get_u32() as i64;\n                match typ {\n                    0x01 | 0x81 => {\n                        return Ok(OnlinePushTrans {\n                            msg_seq,\n                            msg_uid,\n                            msg_time,\n                            info: PushTransInfo::GroupDisband(GroupDisband {\n                                group_code: group_uin2code(group_uin),\n                                operator_uin: operator,\n                            }),\n                        });\n                    }\n                    0x02 | 0x82 => {\n                        return Ok(OnlinePushTrans {\n                            msg_seq,\n                            msg_uid,\n                            msg_time,\n                            info: PushTransInfo::MemberLeave(GroupLeave {\n                                group_code: group_uin2code(group_uin),\n                                member_uin: target,\n                                operator_uin: None,\n                            }),\n                        });\n                    }\n                    0x03 | 0x83 => {\n                        return Ok(OnlinePushTrans {\n                            msg_seq,\n                            msg_uid,\n                            msg_time,\n                            info: PushTransInfo::MemberLeave(GroupLeave {\n                                group_code: group_uin2code(group_uin),\n                                member_uin: target,\n                                operator_uin: Some(operator),\n                            }),\n                        });\n                    }\n                    _ => {}\n                }\n            }\n            Some(44) => {\n                data.advance(5);\n                let var4 = data.get_u8() as i32;\n                let mut var5: i64 = 0;\n                let target = data.get_u32() as i64;\n                if var4 != 0 && var4 != 1 {\n                    var5 = data.get_u32() as i64;\n                }\n                if var5 == 0 && data.len() == 1 {\n                    let new_permission = if data.get_u8() == 1 {\n                        GroupMemberPermission::Administrator\n                    } else {\n                        GroupMemberPermission::Member\n                    };\n                    return Ok(OnlinePushTrans {\n                        msg_seq,\n                        msg_uid,\n                        msg_time,\n                        info: PushTransInfo::MemberPermissionChange(MemberPermissionChange {\n                            group_code: group_uin2code(group_uin),\n                            member_uin: target,\n                            new_permission,\n                        }),\n                    });\n                }\n            }\n            _ => {}\n        }\n        Err(RQError::Decode(format!(\n            \"decode_online_push_trans_packet unknown error: {:?}\",\n            info.msg_type\n        )))\n    }\n\n    // OnlinePush.PbC2CMsgSync\n    pub fn decode_c2c_sync_packet(&self, payload: Bytes) -> RQResult<pb::msg::PbPushMsg> {\n        pb::msg::PbPushMsg::decode(&*payload).map_err(Into::into)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/online_push/mod.rs",
    "content": "use crate::structs::{GroupDisband, GroupLeave, MemberPermissionChange};\nuse crate::{jce, pb};\n\npub mod builder;\npub mod decoder;\n\n#[derive(Debug, Default)]\npub struct ReqPush {\n    pub uin: i64,\n    pub msg_infos: Vec<jce::PushMessageInfo>,\n}\n\n#[derive(Debug, Clone)]\npub enum PushTransInfo {\n    MemberLeave(GroupLeave),\n    MemberPermissionChange(MemberPermissionChange),\n    GroupDisband(GroupDisband),\n    // TODO 转让\n}\n#[derive(Debug, Clone)]\npub struct OnlinePushTrans {\n    pub msg_seq: i32,\n    pub msg_uid: i64,\n    pub msg_time: i32,\n    pub info: PushTransInfo,\n}\n\n#[derive(Debug, Default, Clone, PartialEq)]\npub struct GroupMessagePart {\n    pub seq: i32,\n    pub rand: i32,\n    pub group_code: i64,\n    pub group_name: String,\n    pub group_card: String,\n    pub from_uin: i64,\n    pub elems: Vec<pb::msg::Elem>,\n    pub time: i32,\n    // 语音消息\n    pub ptt: Option<pb::msg::Ptt>,\n\n    // 整个message有多少个part，大于elem.len()时，应等待下一个片段到达后合并\n    pub pkg_num: i32,\n    // 分片的第几段\n    pub pkg_index: i32,\n    // 分片id，相同id的应该合并，且根据pkg_index排序\n    pub div_seq: i32,\n}\n"
  },
  {
    "path": "ricq-core/src/command/pb_message_svc/builder.rs",
    "content": "use crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    // PbMessageSvc.PbMsgReadedReport\n    pub fn build_group_msg_readed_packet(&self, group_code: i64, msg_seq: i32) -> Packet {\n        let req = pb::msg::PbMsgReadedReportReq {\n            grp_read_report: vec![pb::msg::PbGroupReadedReportReq {\n                group_code: Some(group_code as u64),\n                last_read_seq: Some(msg_seq as u64),\n            }],\n            ..Default::default()\n        };\n        self.uni_packet(\"PbMessageSvc.PbMsgReadedReport\", req.to_bytes())\n    }\n\n    // PbMessageSvc.PbMsgReadedReport\n    pub fn build_friend_msg_readed_packet(&self, uin: i64, time: i64) -> Packet {\n        let transport = &self.transport;\n        let req = pb::msg::PbMsgReadedReportReq {\n            c2_c_read_report: Some(pb::msg::PbC2cReadedReportReq {\n                pair_info: vec![pb::msg::UinPairReadInfo {\n                    peer_uin: Some(uin as u64),\n                    last_read_time: Some(time as u32),\n                    ..Default::default()\n                }],\n                sync_cookie: Some(transport.sig.sync_cookie.to_vec()),\n            }),\n            ..Default::default()\n        };\n        self.uni_packet(\"PbMessageSvc.PbMsgReadedReport\", req.to_bytes())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/pb_message_svc/mod.rs",
    "content": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/profile_service/builder.rs",
    "content": "use jcers::JcePut;\n\nuse crate::command::common::PbToBytes;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    // ProfileService.Pb.ReqSystemMsgNew.Group\n    pub fn build_system_msg_new_group_packet(&self, suspicious: bool) -> Packet {\n        let req = pb::structmsg::ReqSystemMsgNew {\n            msg_num: 100,\n            version: 1000,\n            checktype: 3,\n            flag: Some(pb::structmsg::FlagInfo {\n                grp_msg_kick_admin: 1,\n                grp_msg_hidden_grp: 1,\n                grp_msg_wording_down: 1,\n                grp_msg_get_official_account: 1,\n                grp_msg_get_pay_in_group: 1,\n                frd_msg_discuss2_many_chat: 1,\n                grp_msg_not_allow_join_grp_invite_not_frd: 1,\n                frd_msg_need_waiting_msg: 1,\n                frd_msg_uint32_need_all_unread_msg: 1,\n                grp_msg_need_auto_admin_wording: 1,\n                grp_msg_get_transfer_group_msg_flag: 1,\n                grp_msg_get_quit_pay_group_msg_flag: 1,\n                grp_msg_support_invite_auto_join: 1,\n                grp_msg_mask_invite_auto_join: 1,\n                grp_msg_get_disbanded_by_admin: 1,\n                grp_msg_get_c2c_invite_join_group: 1,\n                ..Default::default()\n            }),\n            friend_msg_type_flag: 1,\n            req_msg_type: if suspicious { 2 } else { 1 },\n            ..Default::default()\n        };\n        let payload = req.to_bytes();\n        self.uni_packet(\"ProfileService.Pb.ReqSystemMsgNew.Group\", payload)\n    }\n\n    // ProfileService.Pb.ReqSystemMsgNew.Friend\n    pub fn build_system_msg_new_friend_packet(&self) -> Packet {\n        let req = pb::structmsg::ReqSystemMsgNew {\n            msg_num: 20,\n            version: 1000,\n            checktype: 2,\n            flag: Some(pb::structmsg::FlagInfo {\n                frd_msg_discuss2_many_chat: 1,\n                frd_msg_get_busi_card: 1,\n                frd_msg_need_waiting_msg: 1,\n                frd_msg_uint32_need_all_unread_msg: 1,\n                grp_msg_mask_invite_auto_join: 1,\n                ..Default::default()\n            }),\n            friend_msg_type_flag: 1,\n            ..Default::default()\n        };\n        let payload = req.to_bytes();\n        self.uni_packet(\"ProfileService.Pb.ReqSystemMsgNew.Friend\", payload)\n    }\n\n    // ProfileService.Pb.ReqSystemMsgAction.Group\n    #[allow(clippy::too_many_arguments)]\n    pub fn build_system_msg_group_action_packet(\n        &self,\n        msg_seq: i64,\n        req_uin: i64,\n        group_code: i64,\n        msg_type: i32,\n        is_invite: bool,\n        accept: bool,\n        block: bool,\n        reason: String,\n    ) -> Packet {\n        let req = pb::structmsg::ReqSystemMsgAction {\n            msg_type,\n            msg_seq,\n            req_uin,\n            sub_type: 1,\n            src_id: 3,\n            sub_src_id: if is_invite { 10016 } else { 31 },\n            group_msg_type: if is_invite { 2 } else { 1 },\n            action_info: Some(pb::structmsg::SystemMsgActionInfo {\n                r#type: if accept { 11 } else { 12 },\n                group_code,\n                blacklist: block,\n                msg: reason,\n                sig: vec![],\n                ..Default::default()\n            }),\n            language: 1000,\n        };\n        let payload = req.to_bytes();\n        self.uni_packet(\"ProfileService.Pb.ReqSystemMsgAction.Group\", payload)\n    }\n\n    // ProfileService.Pb.ReqSystemMsgAction.Friend\n    pub fn build_system_msg_friend_action_packet(\n        &self,\n        req_id: i64,\n        req_uin: i64,\n        accept: bool,\n    ) -> Packet {\n        let req = pb::structmsg::ReqSystemMsgAction {\n            msg_type: 1,\n            msg_seq: req_id,\n            req_uin,\n            sub_type: 1,\n            src_id: 6,\n            sub_src_id: 7,\n            action_info: Some(pb::structmsg::SystemMsgActionInfo {\n                r#type: if accept { 2 } else { 3 },\n                blacklist: false,\n                add_frd_sn_info: Some(pb::structmsg::AddFrdSnInfo::default()),\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        let payload = req.to_bytes();\n        self.uni_packet(\"ProfileService.Pb.ReqSystemMsgAction.Friend\", payload)\n    }\n\n    // ProfileService.GroupMngReq\n    pub fn build_quit_group_packet(&self, group_code: i64) -> Packet {\n        let mut jce_mut = jcers::JceMut::new();\n        jce_mut.put_i32(2, 0);\n        jce_mut.put_i64(self.uin(), 1);\n        jce_mut.put_bytes(\n            bytes::Bytes::from({\n                let mut v = Vec::with_capacity(8);\n                v.extend((self.uin() as u32).to_be_bytes());\n                v.extend(group_code.to_be_bytes());\n                v\n            }),\n            2,\n        );\n        let buf = crate::jce::RequestDataVersion3 {\n            map: [(\n                \"GroupMngReq\".to_owned(),\n                crate::command::common::pack_uni_request_data(&jce_mut.freeze()),\n            )]\n            .into(),\n        };\n        let pkt = crate::jce::RequestPacket {\n            i_version: 3,\n            i_request_id: self.next_packet_seq(),\n            s_servant_name: \"KQQ.ProfileService.ProfileServantObj\".to_owned(),\n            s_func_name: \"GroupMngReq\".to_owned(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"ProfileService.GroupMngReq\", pkt.freeze())\n    }\n\n    pub fn build_get_rich_sig_request_packet(&self, user_ids: Vec<i64>) -> Packet {\n        let payload = crate::jce::GetRichSigReq {\n            req_rich_infos: user_ids\n                .into_iter()\n                .map(|id| crate::jce::ReqRichInfo {\n                    uin: id,\n                    dw_time: 0,\n                })\n                .collect(),\n            check_update: false,\n            show_date_sig: false,\n            get_large_tlv: true,\n        };\n        let buf = crate::jce::RequestDataVersion3 {\n            map: [(\n                \"GetRichSigReq\".to_owned(),\n                crate::command::common::pack_uni_request_data(&payload.freeze()),\n            )]\n            .into(),\n        };\n        let pkt = crate::jce::RequestPacket {\n            i_version: 3,\n            i_request_id: self.next_packet_seq(),\n            s_servant_name: \"KQQ.ProfileService.ProfileServantObj\".to_owned(),\n            s_func_name: \"GetRichSig\".to_owned(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"ProfileService.GetRichSig\", pkt.freeze())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/profile_service/decoder.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::{Buf, Bytes};\nuse prost::Message;\n\nuse crate::command::profile_service::*;\nuse crate::{jce, RQResult};\nuse crate::{pb, RQError};\n\nimpl super::super::super::Engine {\n    // ProfileService.Pb.ReqSystemMsgNew.Group\n    pub fn decode_system_msg_group_packet(&self, payload: Bytes) -> RQResult<GroupSystemMessages> {\n        let rsp = pb::structmsg::RspSystemMsgNew::decode(&*payload);\n        let mut join_group_requests = Vec::new();\n        let mut self_invited = Vec::new();\n        match rsp {\n            Ok(rsp) => {\n                for st in rsp\n                    .groupmsgs\n                    .into_iter()\n                    .filter_map(|st| st.msg.map(|m| (st.msg_seq, st.msg_time, st.req_uin, m)))\n                {\n                    let msg_seq = st.0;\n                    let msg_time = st.1;\n                    let req_uin = st.2;\n                    let msg = st.3;\n                    match msg.sub_type {\n                        // 1 进群申请\n                        1 => match msg.group_msg_type {\n                            1 => join_group_requests.push(JoinGroupRequest {\n                                msg_seq,\n                                msg_time,\n                                message: msg.msg_additional,\n                                req_uin,\n                                req_nick: msg.req_uin_nick,\n                                group_code: msg.group_code,\n                                group_name: msg.group_name,\n                                actor_uin: msg.actor_uin,\n                                suspicious: !msg.warning_tips.is_empty(),\n                                ..Default::default()\n                            }),\n                            2 => self_invited.push(SelfInvited {\n                                msg_seq,\n                                msg_time,\n                                invitor_uin: msg.action_uin,\n                                invitor_nick: msg.action_uin_nick,\n                                group_code: msg.group_code,\n                                group_name: msg.group_name,\n                                actor_uin: msg.actor_uin,\n                                actor_nick: msg.actor_uin_nick,\n                            }),\n                            22 => join_group_requests.push(JoinGroupRequest {\n                                msg_seq,\n                                msg_time,\n                                message: msg.msg_additional,\n                                req_uin,\n                                req_nick: msg.req_uin_nick,\n                                group_code: msg.group_code,\n                                group_name: msg.group_name,\n                                actor_uin: msg.actor_uin,\n                                suspicious: !msg.warning_tips.is_empty(),\n                                invitor_uin: Some(msg.action_uin),\n                                invitor_nick: Some(msg.action_uin_qq_nick),\n                            }),\n                            _ => {}\n                        },\n                        // 2 被邀请，不需要处理\n                        2 => {}\n                        // ?\n                        3 => {}\n                        // 自身状态变更(管理员/加群退群)\n                        5 => {}\n                        _ => {}\n                    }\n                }\n                Ok(GroupSystemMessages {\n                    self_invited,\n                    join_group_requests,\n                })\n            }\n            Err(_) => Err(RQError::Decode(\n                \"failed to decode RspSystemMsgNew\".to_string(),\n            )),\n        }\n    }\n\n    // ProfileService.Pb.ReqSystemMsgNew.Friend\n    pub fn decode_system_msg_friend_packet(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<FriendSystemMessages> {\n        let rsp = pb::structmsg::RspSystemMsgNew::decode(&*payload)\n            .map_err(|_| RQError::Decode(\"RspSystemMsgNew\".into()))?;\n        Ok(FriendSystemMessages {\n            requests: rsp\n                .friendmsgs\n                .into_iter()\n                .map(|m| {\n                    let msg = m.msg.as_ref();\n                    NewFriendRequest {\n                        msg_seq: m.msg_seq,\n                        message: msg\n                            .map(|msg| msg.msg_additional.to_owned())\n                            .unwrap_or_default(),\n                        req_uin: m.req_uin,\n                        req_nick: msg\n                            .map(|msg| msg.req_uin_nick.to_owned())\n                            .unwrap_or_default(),\n                    }\n                })\n                .collect(),\n        })\n    }\n\n    pub fn decode_get_rich_sig_response_packet(\n        &self,\n        mut payload: Bytes,\n    ) -> RQResult<Vec<RichSigInfo>> {\n        let mut request: jce::RequestPacket = jcers::from_buf(&mut payload)?;\n        let mut data: jce::RequestDataVersion2 = jcers::from_buf(&mut request.s_buffer)?;\n        let mut a = data\n            .map\n            .remove(\"GetRichSigRes\")\n            .ok_or_else(|| RQError::Decode(\"missing GetRichSigRes\".into()))?;\n        let mut b = a\n            .remove(\"KQQ.GetRichSigRes\")\n            .ok_or_else(|| RQError::Decode(\"missing KQQ.GetRichSigRes\".into()))?;\n        b.advance(1);\n        let resp: jce::GetRichSigRes = jcers::from_buf(&mut b)?;\n        Ok(resp\n            .sig_infos\n            .into_iter()\n            .map(|mut info| RichSigInfo {\n                status: info.status,\n                uin: info.uin,\n                dw_time: info.dw_time,\n                infos: {\n                    let mut infos = HashMap::new();\n                    while info.sig_info.remaining() > 2 {\n                        let tag = info.sig_info.get_u8();\n                        let len = info.sig_info.get_u8();\n                        if info.sig_info.len() < len as usize {\n                            break;\n                        }\n                        let value = info.sig_info.copy_to_bytes(len as usize);\n                        infos.insert(tag, value);\n                    }\n                    infos\n                },\n            })\n            .collect())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/profile_service/mod.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::Bytes;\n\npub mod builder;\npub mod decoder;\n\n#[derive(Debug, Default, Clone)]\npub struct GroupSystemMessages {\n    pub self_invited: Vec<SelfInvited>,\n    pub join_group_requests: Vec<JoinGroupRequest>,\n}\n\n// 自己被邀请\n#[derive(Debug, Default, Clone)]\npub struct SelfInvited {\n    pub msg_seq: i64,\n    pub msg_time: i64,\n    pub invitor_uin: i64,\n    pub invitor_nick: String,\n    pub group_code: i64,\n    pub group_name: String,\n    pub actor_uin: i64,\n    pub actor_nick: String,\n}\n\n// 用户申请进群\n#[derive(Debug, Default, Clone)]\npub struct JoinGroupRequest {\n    pub msg_seq: i64,\n    pub msg_time: i64,\n    pub message: String,\n    pub req_uin: i64,\n    pub req_nick: String,\n    pub group_code: i64,\n    pub group_name: String,\n    pub actor_uin: i64,\n    pub suspicious: bool,\n    pub invitor_uin: Option<i64>,\n    pub invitor_nick: Option<String>,\n}\n\n#[derive(Debug, Default, Clone)]\npub struct FriendSystemMessages {\n    pub requests: Vec<NewFriendRequest>,\n}\n\n#[derive(Debug, Default, Clone)]\npub struct NewFriendRequest {\n    pub msg_seq: i64,\n    pub message: String,\n    pub req_uin: i64,\n    pub req_nick: String,\n}\n\n#[derive(Debug, Default, Clone)]\npub struct RichSigInfo {\n    pub status: u8,\n    pub uin: i64,\n    pub dw_time: i64,\n    /// 1-actionText\n    /// 2-dataText\n    /// 4-locationText\n    /// 130-经纬度\n    /// 129-actionId+dataId\n    /// 猜测A：[1,128) 范围内，且不是1,2,4，字符串拼一起作为签名\n    /// 猜测B：3是签名\n    pub infos: HashMap<u8, Bytes>,\n}\n\nimpl RichSigInfo {\n    pub fn get_signature(&self) -> String {\n        String::from_utf8_lossy(&self.infos.get(&3).cloned().unwrap_or_default()).into_owned()\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/ptt_center_svr/builder.rs",
    "content": "use crate::command::common::PbToBytes;\nuse crate::hex::encode_hex;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    pub fn build_group_video_store_packet(\n        &self,\n        short_video_up_req: pb::short_video::ShortVideoUploadReq,\n    ) -> Packet {\n        let seq = self.next_seq();\n        let req = pb::short_video::ShortVideoReqBody {\n            seq: seq as i32,\n            cmd: 300,\n            ptt_short_video_upload_req: Some(short_video_up_req),\n            extension_req: vec![pb::short_video::ShortVideoExtensionReq {\n                sub_busi_type: 0,\n                user_cnt: 1,\n            }],\n            ..Default::default()\n        };\n        self.uni_packet_with_seq(\n            seq as i32,\n            \"PttCenterSvr.GroupShortVideoUpReq\",\n            req.to_bytes(),\n        )\n    }\n\n    pub fn build_short_video_up_req(\n        &self,\n        to_uin: i64,\n        file_md5: Vec<u8>,\n        thumb_file_md5: Vec<u8>,\n        file_size: i64,\n        thumb_file_size: i64,\n    ) -> pb::short_video::ShortVideoUploadReq {\n        pb::short_video::ShortVideoUploadReq {\n            from_uin: self.uin(),\n            to_uin,\n            chat_type: 1, // 私聊 0\n            client_type: 2,\n            info: Some(pb::short_video::ShortVideoFileInfo {\n                file_name: format!(\"{}.mp4\", encode_hex(&file_md5)),\n                file_md5,\n                thumb_file_md5,\n                file_size,\n                file_res_length: 1280,\n                file_res_width: 720,\n                file_format: 3,\n                file_time: 120, // 视频时长 秒\n                thumb_file_size,\n            }),\n            group_code: to_uin,\n            agent_type: 0,\n            business_type: 0,\n            support_large_size: 1,\n        }\n    }\n\n    // PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_DOWNLOAD-1200\n    pub fn build_c2c_ptt_down_req(&self, sender_uin: i64, file_uuid: Vec<u8>) -> Packet {\n        let req = pb::cmd0x346::C346ReqBody {\n            client_type: 104,\n            cmd: 1200,\n            business_id: 17, // 3?\n            apply_download_req: Some(pb::cmd0x346::ApplyDownloadReq {\n                uin: sender_uin,\n                uuid: file_uuid,\n                need_https_url: 1,\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        self.uni_packet(\n            \"PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_DOWNLOAD-1200\",\n            req.to_bytes(),\n        )\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/ptt_center_svr/decoder.rs",
    "content": "use bytes::Bytes;\n\nuse crate::pb::short_video::{ShortVideoRspBody, ShortVideoUploadRsp};\nuse crate::{pb, RQError, RQResult};\nuse prost::Message;\n\nimpl super::super::super::Engine {\n    pub fn decode_group_video_store_response(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<ShortVideoUploadRsp> {\n        ShortVideoRspBody::decode(&*payload)?\n            .ptt_short_video_upload_rsp\n            .ok_or(RQError::EmptyField(\"ptt_short_video_upload_rsp\"))\n    }\n\n    // PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_DOWNLOAD-1200\n    pub fn decode_c2c_ptt_down(&self, payload: Bytes) -> RQResult<String> {\n        pb::cmd0x346::C346RspBody::decode(&*payload)?\n            .apply_download_rsp\n            .ok_or(RQError::EmptyField(\"apply_download_rsp\"))?\n            .download_info\n            .ok_or(RQError::EmptyField(\"download_info\"))\n            .map(|info| info.download_url)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/ptt_center_svr/mod.rs",
    "content": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/ptt_store/builder.rs",
    "content": "use bytes::Bytes;\n\nuse crate::command::common::PbToBytes;\nuse crate::hex::encode_hex;\nuse crate::pb;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    pub fn build_group_try_up_ptt_req(\n        &self,\n        group_code: i64,\n        file_md5: Vec<u8>,\n        file_size: u64,\n        codec: u32,\n        voice_length: u32,\n    ) -> Bytes {\n        let req = pb::cmd0x388::D388ReqBody {\n            net_type: Some(3),\n            subcmd: Some(3),\n            tryup_ptt_req: vec![pb::cmd0x388::TryUpPttReq {\n                group_code: Some(group_code as u64),\n                src_uin: Some(self.uin() as u64),\n                file_md5: Some(file_md5.clone()),\n                file_size: Some(file_size),\n                file_name: Some(file_md5),\n                src_term: Some(5),\n                platform_type: Some(9),\n                bu_type: Some(4),\n                build_ver: Some(self.transport.version.build_ver.into()),\n                inner_ip: Some(0),\n                // TODO ?\n                voice_length: Some(voice_length),\n                new_up_chan: Some(true),\n                codec: Some(codec),\n                // 2021/1/26 因为 #577 修改为 resource.voiceCodec\n                voice_type: Some(1),\n                ..Default::default()\n            }],\n            ..Default::default()\n        };\n        req.to_bytes()\n    }\n\n    pub fn build_friend_try_up_ptt_req(\n        &self,\n        target: i64,\n        file_md5: Vec<u8>,\n        file_size: i64,\n        voice_length: i32,\n    ) -> Bytes {\n        let req = pb::cmd0x346::C346ReqBody {\n            cmd: 500,\n            seq: self.next_seq() as i32,\n            business_id: 17,\n            client_type: 104,\n            apply_upload_req: Some(pb::cmd0x346::ApplyUploadReq {\n                sender_uin: self.uin(),\n                recver_uin: target,\n                file_type: 2,\n                file_size,\n                file_name: encode_hex(&file_md5),\n                bytes_10m_md5: file_md5,\n                ..Default::default()\n            }),\n            extension_req: Some(pb::cmd0x346::ExtensionReq {\n                id: 3,\n                ptt_format: 1,\n                net_type: 3,\n                voice_type: 2,\n                ptt_time: voice_length,\n                ..Default::default()\n            }),\n            ..Default::default()\n        };\n        req.to_bytes()\n    }\n\n    pub fn build_group_ptt_down_req(&self, group_code: i64, file_md5: Vec<u8>) -> Packet {\n        let req = pb::cmd0x388::D388ReqBody {\n            net_type: Some(3),\n            subcmd: Some(4),\n            getptt_url_req: vec![pb::cmd0x388::GetPttUrlReq {\n                group_code: Some(group_code as u64),\n                dst_uin: Some(self.uin() as u64),\n                fileid: None,\n                file_md5: Some(file_md5),\n                req_term: Some(5),\n                req_platform_type: Some(9),\n                inner_ip: Some(0),\n                bu_type: Some(4), // 3?\n                build_ver: Some(self.transport.version.build_ver.into()),\n                codec: Some(0),\n                // 11=file_key, 14=2, 15=1 ?\n                ..Default::default()\n            }],\n            ..Default::default()\n        };\n        self.uni_packet(\"PttStore.GroupPttDown\", req.to_bytes())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/ptt_store/decoder.rs",
    "content": "use bytes::Bytes;\n\nuse crate::RQResult;\nuse crate::{pb, RQError};\nuse prost::Message;\n\nimpl super::super::super::Engine {\n    pub fn decode_group_try_up_ptt_resp(&self, payload: Bytes) -> RQResult<Vec<u8>> {\n        let mut rsp = pb::cmd0x388::D388RspBody::decode(&*payload)?;\n        let ptt = rsp\n            .tryup_ptt_rsp\n            .pop()\n            .ok_or(RQError::EmptyField(\"tryup_ptt_rsp\"))?;\n        ptt.file_key.ok_or(RQError::EmptyField(\"file_key\"))\n    }\n\n    pub fn decode_friend_try_up_ptt_resp(&self, payload: Bytes) -> RQResult<Vec<u8>> {\n        pb::cmd0x346::C346RspBody::decode(&*payload)?\n            .apply_upload_rsp\n            .map(|r| r.uuid)\n            .ok_or(RQError::EmptyField(\"apply_upload_rsp\"))\n    }\n\n    pub fn decode_group_ptt_down(&self, payload: Bytes) -> RQResult<String> {\n        let mut rsp = pb::cmd0x388::D388RspBody::decode(&*payload)?;\n        let ptt = rsp\n            .getptt_url_rsp\n            .pop()\n            .ok_or(RQError::EmptyField(\"getptt_url_rsp\"))?;\n        Ok(format!(\n            \"http://{}{}\",\n            ptt.domain.ok_or(RQError::EmptyField(\"ptt_domain\"))?,\n            String::from_utf8_lossy(&ptt.down_para.ok_or(RQError::EmptyField(\"ptt_down_para\"))?)\n        ))\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/ptt_store/mod.rs",
    "content": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/reg_prxy_svc/builder.rs",
    "content": "use std::collections::HashMap;\nuse std::time::UNIX_EPOCH;\n\nuse bytes::{BufMut, BytesMut};\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;\nuse crate::command::common::PbToBytes;\nuse crate::protocol::packet::Packet;\nuse crate::{jce, pb};\n\nimpl super::super::super::Engine {\n    // RegPrxySvc.getOffMsg\n    pub fn build_get_offline_msg_request_packet(&self, last_message_time: i64) -> Packet {\n        let transport = &self.transport;\n        let reg_req = jce::SvcReqRegisterNew {\n            request_optional: 0x101C2 | 32,\n            c2c_msg: jce::SvcReqGetMsgV2 {\n                uin: self.uin(),\n                date_time: match last_message_time {\n                    0 => 1,\n                    _ => last_message_time as i32,\n                },\n                recive_pic: 1,\n                ability: 15,\n                channel: 4,\n                inst: 1,\n                channel_ex: 1,\n                sync_cookie: transport.sig.sync_cookie.to_owned(),\n                sync_flag: 0,\n                ramble_flag: 0,\n                general_abi: 1,\n                pub_account_cookie: transport.sig.pub_account_cookie.to_owned(),\n            },\n            group_msg: jce::SvcReqPullGroupMsgSeq {\n                verify_type: 0,\n                filter: 1,\n                ..Default::default()\n            },\n            end_seq: UNIX_EPOCH.elapsed().unwrap().as_secs() as i64,\n            ..Default::default()\n        };\n        let flag = 0; // flag := msg.SyncFlag_START\n        let msg_req = pb::msg::GetMessageRequest {\n            sync_flag: Some(flag),\n            sync_cookie: Some(transport.sig.sync_cookie.to_vec()),\n            ramble_flag: Some(0),\n            context_flag: Some(1),\n            online_sync_flag: Some(0),\n            latest_ramble_number: Some(20),\n            other_ramble_number: Some(3),\n            ..Default::default()\n        }\n        .to_bytes();\n        let mut buf = BytesMut::new();\n        buf.put_slice(&[0, 0, 0, 0]);\n        buf.put_slice(&msg_req);\n        let buf = buf.freeze();\n        let mut req = jcers::JceMut::new();\n        req.put_bytes(buf, 0);\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([\n                (\"req_PbOffMsg\".to_string(), req.freeze()),\n                (\n                    \"req_OffMsg\".to_string(),\n                    pack_uni_request_data(&reg_req.freeze()),\n                ),\n            ]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            s_servant_name: \"RegPrxySvc\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"RegPrxySvc.getOffMsg\", pkt.freeze())\n    }\n\n    // RegPrxySvc.infoSync\n    pub fn build_sync_msg_request_packet(&self, last_message_time: i64) -> Packet {\n        let transport = &self.transport;\n        let oidb_req = pb::oidb::D769RspBody {\n            config_list: vec![\n                pb::oidb::D769ConfigSeq {\n                    r#type: Some(46),\n                    version: Some(0),\n                },\n                pb::oidb::D769ConfigSeq {\n                    r#type: Some(283),\n                    version: Some(0),\n                },\n            ],\n            ..Default::default()\n        }\n        .to_bytes();\n        let reg_req = jce::SvcReqRegisterNew {\n            request_optional: 128 | 64 | 256 | 2 | 8192 | 16384 | 65536,\n            dis_group_msg_filter: 1,\n            c2c_msg: jce::SvcReqGetMsgV2 {\n                uin: self.uin(),\n                date_time: match last_message_time {\n                    0 => 1,\n                    _ => last_message_time as i32,\n                },\n                recive_pic: 1,\n                ability: 15,\n                channel: 4,\n                inst: 1,\n                channel_ex: 1,\n                sync_cookie: transport.sig.sync_cookie.to_owned(),\n                sync_flag: 0, // START\n                ramble_flag: 0,\n                general_abi: 1,\n                pub_account_cookie: transport.sig.pub_account_cookie.to_owned(),\n            },\n            group_mask: 2,\n            end_seq: rand::random::<u32>() as i64,\n            _0769_body: oidb_req,\n            ..Default::default()\n        };\n        let flag = 0; // flag := msg.SyncFlag_START\n        let mut msg_req = pb::msg::GetMessageRequest {\n            sync_flag: Some(flag),\n            sync_cookie: Some(transport.sig.sync_cookie.to_vec()),\n            ramble_flag: Some(0),\n            context_flag: Some(1),\n            online_sync_flag: Some(0),\n            latest_ramble_number: Some(20),\n            other_ramble_number: Some(3),\n            msg_req_type: Some(1),\n            ..Default::default()\n        };\n        let off_msg = msg_req.to_bytes();\n        msg_req.msg_req_type = Some(2);\n        msg_req.sync_cookie = None;\n        msg_req.pubaccount_cookie = Some(transport.sig.pub_account_cookie.to_vec());\n        let pub_msg = msg_req.to_bytes();\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([\n                (\"req_PbOffMsg\".to_string(), {\n                    let mut w = jcers::JceMut::new();\n                    w.put_bytes(\n                        {\n                            let mut b = BytesMut::new();\n                            b.put_slice(&[0; 4]);\n                            b.put_slice(&off_msg);\n                            b.freeze()\n                        },\n                        0,\n                    );\n                    w.freeze()\n                }),\n                (\"req_PbPubMsg\".to_string(), {\n                    let mut w = jcers::JceMut::new();\n                    w.put_bytes(\n                        {\n                            let mut b = BytesMut::new();\n                            b.put_slice(&[0; 4]);\n                            b.put_slice(&pub_msg);\n                            b.freeze()\n                        },\n                        0,\n                    );\n                    w.freeze()\n                }),\n                (\n                    \"req_OffMsg\".to_string(),\n                    pack_uni_request_data(&reg_req.freeze()),\n                ),\n            ]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            s_servant_name: \"RegPrxySvc\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"RegPrxySvc.infoSync\", pkt.freeze())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/reg_prxy_svc/decoder.rs",
    "content": "use bytes::{Buf, Bytes};\n\nuse crate::structs::OtherClientInfo;\nuse crate::{jce, RQError, RQResult};\n\nimpl super::super::super::Engine {\n    // RegPrxySvc.PushParam\n    pub fn decode_push_param_packet(&self, payload: &[u8]) -> RQResult<Vec<OtherClientInfo>> {\n        let mut payload = Bytes::from(payload.to_owned());\n        let mut request: jce::RequestPacket =\n            jcers::from_buf(&mut payload).map_err(RQError::from)?;\n        let mut data: jce::RequestDataVersion2 =\n            jcers::from_buf(&mut request.s_buffer).map_err(RQError::from)?;\n        let mut req = data\n            .map\n            .remove(\"SvcRespParam\")\n            .ok_or_else(|| RQError::Decode(\"SvcRespParam is none\".to_string()))?;\n        let mut reader = req\n            .remove(\"RegisterProxySvcPack.SvcRespParam\")\n            .ok_or_else(|| {\n                RQError::Decode(\"RegisterProxySvcPack.SvcRespParam is none\".to_string())\n            })?;\n        reader.advance(1);\n        let rsp: jce::SvcRespParam = jcers::from_buf(&mut reader).map_err(RQError::from)?;\n        Ok(rsp\n            .online_infos\n            .iter()\n            .map(|i| OtherClientInfo {\n                app_id: i.instance_id as i64,\n                instance_id: i.instance_id,\n                sub_platform: String::from_utf8_lossy(&i.sub_platform).into_owned(),\n                device_kind: match i.u_client_type {\n                    65793 => \"Windows\".to_string(),\n                    65805 | 68104 => \"aPad\".to_string(),\n                    66818 | 66831 | 81154 => \"Mac\".to_string(),\n                    68361 | 72194 => \"iPad\".to_string(),\n                    75023 | 78082 | 78096 => \"Watch\".to_string(),\n                    77313 => \"Windows TIM\".to_string(),\n                    _ => String::from_utf8_lossy(&i.sub_platform).into_owned(),\n                },\n            })\n            .collect())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/reg_prxy_svc/mod.rs",
    "content": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/signature/builder.rs",
    "content": "use crate::command::common::PbToBytes;\nuse crate::pb::sig_act;\nuse crate::protocol::packet::Packet;\nuse std::time::UNIX_EPOCH;\n\nimpl super::super::super::Engine {\n    pub fn build_update_signature_packet(&self, signature: String) -> Packet {\n        let req = sig_act::ReqBody {\n            cmd: Some(2),\n            seq: Some(UNIX_EPOCH.elapsed().unwrap().as_millis() as u64),\n            plf: Some(sig_act::Platform {\n                platform: Some(109),\n                osver: Some(self.transport.device.version.release.to_owned()),\n                mqqver: Some(self.transport.version.sort_version_name.into()),\n            }),\n            auth_req: Some(sig_act::SigauthReq {\n                uin_disable: Some(self.uin() as u64),\n                itemid: Some(0),\n                len: Some(signature.len() as i32 + 27),\n                data: Some({\n                    let mut buf = vec![0x3, signature.as_bytes().len() as u8 + 1, 0x20];\n                    buf.extend(signature.into_bytes());\n                    buf.extend([\n                        0x91, 0x04, 0x00, 0x00, 0x00, 0x00, 0x92, 0x04, 0x00, 0x00, 0x00, 0x00,\n                        0xA2, 0x04, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x04, 0x00, 0x00, 0x00, 0x00,\n                    ]);\n                    buf\n                }),\n                fontid: Some(0),\n            }),\n            source: Some(1),\n            ..Default::default()\n        };\n        self.uni_packet(\"Signature.auth\", req.to_bytes())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/signature/mod.rs",
    "content": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/stat_svc/builder.rs",
    "content": "use std::collections::HashMap;\nuse std::time::UNIX_EPOCH;\n\nuse bytes::{BufMut, Bytes, BytesMut};\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;\nuse crate::command::common::PbToBytes;\nuse crate::jce;\nuse crate::protocol::packet::*;\nuse crate::structs::CustomOnlineStatus;\n\nimpl super::super::super::Engine {\n    // StatSvc.SetStatusFromClient\n    pub fn build_set_online_status_packet(\n        &self,\n        online_status: i32,\n        ext_online_status: i64,\n        custom_status: Option<CustomOnlineStatus>,\n    ) -> Packet {\n        let transport = &self.transport;\n        let svc = jce::SvcReqRegister {\n            uin: self.uin(),\n            bid: 1 | 2 | 4,\n            conn_type: 0,\n            status: online_status,\n            kick_pc: 0,\n            kick_weak: 0,\n            ios_version: transport.device.version.sdk as i64,\n            net_type: 1, // 0-移动网络 1-wifi\n            reg_type: 0,\n            guid: transport.sig.guid.to_owned(),\n            is_set_status: 1,\n            locale_id: 2052,\n            dev_name: transport.device.model.to_owned(),\n            dev_type: transport.device.model.to_owned(),\n            os_ver: transport.device.version.release.to_owned(),\n            open_push: 1,\n            large_seq: 1551,\n            vendor_name: transport.device.vendor_name.to_owned(),\n            vendor_os_name: transport.device.vendor_os_name.to_owned(),\n            ext_online_status,\n            timestamp: UNIX_EPOCH.elapsed().unwrap().as_secs() as i64,\n            custom_status: custom_status\n                .map(|custom_status| {\n                    crate::pb::online_status::CustomStatus {\n                        face_index: Some(custom_status.face_index),\n                        wording: Some(custom_status.wording),\n                        face_type: Some(1),\n                    }\n                    .to_bytes()\n                })\n                .unwrap_or_default(),\n            ..Default::default()\n        };\n        let pkt = self.svc_req_register_pkt(svc);\n        self.uni_packet(\"StatSvc.SetStatusFromClient\", pkt.freeze())\n    }\n\n    // StatSvc.register\n    pub fn build_client_register_packet(&self) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n\n        let svc = jce::SvcReqRegister {\n            uin: self.uin(),\n            bid: 1 | 2 | 4,\n            conn_type: 0,\n            status: 11,\n            kick_pc: 0,\n            kick_weak: 0,\n            ios_version: transport.device.version.sdk as i64,\n            net_type: 1, // 0-移动网络 1-wifi\n            reg_type: 0,\n            guid: transport.sig.guid.to_owned(),\n            is_set_status: 0,\n            locale_id: 2052,\n            dev_name: transport.device.model.to_owned(),\n            dev_type: transport.device.model.to_owned(),\n            os_ver: transport.device.version.release.to_owned(),\n            open_push: 1,\n            large_seq: 1551,\n            old_sso_ip: 0,\n            new_sso_ip: 31806887127679168,\n            channel_no: \"\".to_string(),\n            cpid: 0,\n            vendor_name: transport.device.vendor_name.to_owned(),\n            vendor_os_name: transport.device.vendor_os_name.to_owned(),\n            b769: Bytes::from_static(&[\n                0x0A, 0x04, 0x08, 0x2E, 0x10, 0x00, 0x0A, 0x05, 0x08, 0x9B, 0x02, 0x10, 0x00,\n            ]),\n            set_mute: 0,\n            ..Default::default()\n        };\n        let pkt = self.svc_req_register_pkt(svc);\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::D2Key,\n            seq_id: seq as i32,\n            body: pkt.freeze(),\n            command_name: \"StatSvc.register\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    fn svc_req_register_pkt(&self, svc: jce::SvcReqRegister) -> jce::RequestPacket {\n        let mut b = BytesMut::new();\n        b.put_slice(&[0x0A]);\n        b.put_slice(&svc.freeze());\n        b.put_slice(&[0x0B]);\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\"SvcReqRegister\".to_string(), b.into())]),\n        };\n        jce::RequestPacket {\n            i_version: 3,\n            s_servant_name: \"PushService\".to_string(),\n            s_func_name: \"SvcReqRegister\".to_string(),\n            s_buffer: buf.freeze(),\n            context: Default::default(),\n            status: Default::default(),\n            ..Default::default()\n        }\n    }\n\n    // StatSvc.GetDevLoginInfo\n    pub fn build_device_list_request_packet(&self) -> Packet {\n        let transport = &self.transport;\n        let req = jce::SvcReqGetDevLoginInfo {\n            guid: transport.sig.guid.to_owned(),\n            login_type: 1,\n            app_name: \"com.tencent.mobileqq\".into(),\n            require_max: 20,\n            get_dev_list_type: 20,\n            ..Default::default()\n        };\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\n                \"SvcReqGetDevLoginInfo\".to_string(),\n                pack_uni_request_data(&req.freeze()),\n            )]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            s_servant_name: \"StatSvc\".to_string(),\n            s_func_name: \"SvcReqGetDevLoginInfo\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"StatSvc.GetDevLoginInfo\", pkt.freeze())\n    }\n\n    // StatSvc.RspMSFForceOffline\n    pub fn build_msf_force_offline_rsp(&self, uin: i64, seq_no: i64) -> Packet {\n        let rsp = jce::RspMSFForceOffline {\n            uin,\n            seq_no,\n            const_zero: 0,\n        };\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\n                \"RspMSFForceOffline\".to_string(),\n                pack_uni_request_data(&rsp.freeze()),\n            )]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            s_servant_name: \"StatSvc\".to_string(),\n            s_func_name: \"RspMSFForceOffline\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"StatSvc.RspMSFForceOffline\", pkt.freeze())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/stat_svc/decoder.rs",
    "content": "use bytes::{Buf, Bytes};\nuse jcers::Jce;\n\nuse crate::{jce, RQError, RQResult};\n\nimpl super::super::super::Engine {\n    // StatSvc.register\n    pub fn decode_client_register_response(\n        &self,\n        mut payload: Bytes,\n    ) -> RQResult<jce::SvcRespRegister> {\n        let mut request: jce::RequestPacket =\n            jcers::from_buf(&mut payload).map_err(RQError::from)?;\n        let mut data: jce::RequestDataVersion2 =\n            jcers::from_buf(&mut request.s_buffer).map_err(RQError::from)?;\n        let mut a = data\n            .map\n            .remove(\"SvcRespRegister\")\n            .ok_or_else(|| RQError::Decode(\"missing SvcRespRegister\".into()))?;\n        let mut b = a\n            .remove(\"QQService.SvcRespRegister\")\n            .ok_or_else(|| RQError::Decode(\"missing QQService.SvcRespRegister\".into()))?;\n        b.advance(1);\n        jcers::from_buf(&mut b).map_err(RQError::from)\n    }\n\n    // StatSvc.GetDevLoginInfo\n    pub fn decode_dev_list_response(\n        &self,\n        mut payload: Bytes,\n    ) -> RQResult<Vec<jce::SvcDevLoginInfo>> {\n        let mut request: jce::RequestPacket =\n            jcers::from_buf(&mut payload).map_err(RQError::from)?;\n        let mut data: jce::RequestDataVersion2 =\n            jcers::from_buf(&mut request.s_buffer).map_err(RQError::from)?;\n        let mut req = data\n            .map\n            .remove(\"SvcRspGetDevLoginInfo\")\n            .ok_or_else(|| RQError::Decode(\"missing SvcRspGetDevLoginInfo\".into()))?;\n        let mut msg = req\n            .remove(\"QQService.SvcRspGetDevLoginInfo\")\n            .ok_or_else(|| RQError::Decode(\"missing QQService.SvcRspGetDevLoginInfo\".into()))?;\n        msg.advance(1);\n        let mut rsp = Jce::new(&mut msg);\n        let d: Vec<jce::SvcDevLoginInfo> = rsp.get_by_tag(4).map_err(RQError::from)?;\n        if !d.is_empty() {\n            return Ok(d);\n        }\n        let d: Vec<jce::SvcDevLoginInfo> = rsp.get_by_tag(5).map_err(RQError::from)?;\n        if !d.is_empty() {\n            return Ok(d);\n        }\n        let d: Vec<jce::SvcDevLoginInfo> = rsp.get_by_tag(6).map_err(RQError::from)?;\n        if !d.is_empty() {\n            return Ok(d);\n        }\n        Err(RQError::Decode(\"decode_dev_list_response\".into()))\n    }\n\n    // StatSvc.ReqMSFOffline\n    pub fn decode_msf_force_offline(\n        &self,\n        mut payload: Bytes,\n    ) -> RQResult<jce::RequestMSFForceOffline> {\n        let mut request: jce::RequestPacket =\n            jcers::from_buf(&mut payload).map_err(RQError::from)?;\n        let mut data: jce::RequestDataVersion2 =\n            jcers::from_buf(&mut request.s_buffer).map_err(RQError::from)?;\n        let mut data = data\n            .map\n            .remove(\"RequestMSFForceOffline\")\n            .ok_or_else(|| RQError::Decode(\"missing RequestMSFForceOffline\".into()))?\n            .remove(\"QQService.RequestMSFForceOffline\")\n            .ok_or_else(|| RQError::Decode(\"missing QQService.RequestMSFForceOffline\".into()))?;\n        jcers::from_buf(&mut data).map_err(RQError::from)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/stat_svc/mod.rs",
    "content": "pub mod builder;\npub mod decoder;\n\n#[derive(Debug, Clone)]\npub struct Status {\n    pub online_status: i32,\n    pub ext_online_status: i64,\n    pub custom_status: Option<CustomOnlineStatus>,\n}\n\n#[derive(Debug, Copy, Clone)]\npub enum OnlineStatus {\n    Online = 11,    // 在线\n    Offline = 21,   // 离线\n    Away = 31,      // 离开\n    Invisible = 41, // 隐身\n    Busy = 50,      // 忙\n    Qme = 60,       // Q我吧\n    Dnd = 70,       // 请勿打扰\n}\n\nimpl From<OnlineStatus> for Status {\n    fn from(s: OnlineStatus) -> Self {\n        Self {\n            online_status: s as i32,\n            ext_online_status: 0,\n            custom_status: None,\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone)]\npub enum ExtOnlineStatus {\n    Battery = 1000,       // 当前电量\n    Listening = 1028,     // 听歌中\n    Constellation = 1040, // 星座运势\n    Weather = 1030,       // 今日天气\n    MeetSpring = 1069,    // 遇见春天\n    Timi = 1027,          // Timi中\n    EatChicken = 1064,    // 吃鸡中\n    Loving = 1051,        // 恋爱中\n    WangWang = 1053,      // 汪汪汪\n    CookedRice = 1019,    // 干饭中\n    Study = 1018,         // 学习中\n    StayUp = 1032,        // 熬夜中\n    PlayBall = 1050,      // 打球中\n    Signal = 1011,        // 信号弱\n    StudyOnline = 1024,   // 在线学习\n    Gaming = 1017,        // 游戏中\n    Vacationing = 1022,   // 度假中\n    WatchingTV = 1021,    // 追剧中\n    Fitness = 1020,       // 健身中\n}\n\nimpl From<ExtOnlineStatus> for Status {\n    fn from(s: ExtOnlineStatus) -> Self {\n        Self {\n            online_status: 11,\n            ext_online_status: s as i64,\n            custom_status: None,\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct CustomOnlineStatus {\n    pub face_index: u64,\n    pub wording: String,\n}\n\nimpl From<CustomOnlineStatus> for Status {\n    fn from(s: CustomOnlineStatus) -> Self {\n        Self {\n            online_status: 11,\n            ext_online_status: 2000,\n            custom_status: Some(s),\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/summary_card/builder.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::{BufMut, Bytes, BytesMut};\nuse jcers::JcePut;\n\nuse crate::command::common::{pack_uni_request_data, PbToBytes};\nuse crate::protocol::packet::*;\nuse crate::{jce, pb};\n\nimpl super::super::super::Engine {\n    // SummaryCard.ReqSummaryCard\n    pub fn build_summary_card_request_packet(&self, target: i64) -> Packet {\n        let seq = self.next_seq();\n        let gate = pb::profilecard::GateVaProfileGateReq {\n            u_cmd: Some(3),\n            st_privilege_req: Some(pb::profilecard::GatePrivilegeBaseInfoReq {\n                u_req_uin: Some(target),\n            }),\n            st_gift_req: Some(pb::profilecard::GateGetGiftListReq {\n                uin: Some(target as i32),\n            }),\n            st_vip_care: Some(pb::profilecard::GateGetVipCareReq { uin: Some(target) }),\n            oidb_flag: vec![\n                pb::profilecard::GateOidbFlagInfo {\n                    fieled: Some(42334),\n                    byets_value: None,\n                },\n                pb::profilecard::GateOidbFlagInfo {\n                    fieled: Some(42340),\n                    byets_value: None,\n                },\n                pb::profilecard::GateOidbFlagInfo {\n                    fieled: Some(42344),\n                    byets_value: None,\n                },\n                pb::profilecard::GateOidbFlagInfo {\n                    fieled: Some(42354),\n                    byets_value: None,\n                },\n            ],\n            ..Default::default()\n        }\n        .to_bytes();\n        let business_buf = {\n            let mut w = BytesMut::new();\n            let comm = pb::profilecard::BusiComm {\n                ver: Some(1),\n                seq: Some(seq as i32),\n                fromuin: Some(self.uin()),\n                touin: Some(target),\n                service: Some(16),\n                platform: Some(2),\n                qqver: Some(self.transport.version.build_ver.into()),\n                build: Some(4945),\n                ..Default::default()\n            }\n            .to_bytes();\n            w.put_u8(40);\n            w.put_u32(comm.len() as u32);\n            w.put_u32(gate.len() as u32);\n            w.put_slice(&comm);\n            w.put_slice(&gate);\n            w.put_u8(42);\n            w.freeze()\n        };\n\n        let req = jce::SummaryCardReq {\n            uin: target,\n            come_from: 31,\n            get_control: 69181,\n            add_friend_source: 3001,\n            secure_sig: Bytes::from(vec![0]),\n            req_medal_wall_info: 0,\n            req_0x5eb_field_id: vec![\n                27225, 27224, 42122, 42121, 27236, 27238, 42167, 42172, 40324, 42284, 42326, 42325,\n                42356, 42363, 42361, 42367, 42377, 42425, 42505, 42488,\n            ],\n            req_services: vec![business_buf],\n            req_nearby_god_info: 1,\n            req_extend_card: 1,\n            ..Default::default()\n        };\n        let mut head = jcers::JceMut::new();\n        head.put_i32(2, 0);\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([\n                (\"ReqHead\".to_string(), pack_uni_request_data(&head.freeze())),\n                (\n                    \"ReqSummaryCard\".to_string(),\n                    pack_uni_request_data(&req.freeze()),\n                ),\n            ]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            s_servant_name: \"SummaryCardServantObj\".to_string(),\n            s_func_name: \"ReqSummaryCard\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"SummaryCard.ReqSummaryCard\", pkt.freeze())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/summary_card/decoder.rs",
    "content": "use bytes::{Buf, Bytes};\n\nuse crate::jce::{RespSummaryCard, RespSummaryCardHead};\nuse crate::structs::SummaryCardInfo;\nuse crate::{jce, RQError, RQResult};\n\nimpl super::super::super::Engine {\n    // SummaryCard.ReqSummaryCard\n    pub fn decode_summary_card_response(&self, mut payload: Bytes) -> RQResult<SummaryCardInfo> {\n        let mut request: jce::RequestPacket =\n            jcers::from_buf(&mut payload).map_err(RQError::from)?;\n        let mut data: jce::RequestDataVersion2 =\n            jcers::from_buf(&mut request.s_buffer).map_err(RQError::from)?;\n        let mut head = data\n            .map\n            .remove(\"RespHead\")\n            .ok_or_else(|| RQError::Decode(\"missing RespHead\".into()))?\n            .remove(\"SummaryCard.RespHead\")\n            .ok_or_else(|| RQError::Decode(\"missing SummaryCard.RespHead\".into()))?;\n        head.advance(1);\n        let head: RespSummaryCardHead = jcers::from_buf(&mut head)?;\n        let mut rsp = data\n            .map\n            .remove(\"RespSummaryCard\")\n            .ok_or_else(|| RQError::Decode(\"missing RespSummaryCard\".into()))?\n            .remove(\"SummaryCard_Old.RespSummaryCard\")\n            .ok_or_else(|| RQError::Decode(\"missing SummaryCard_Old.RespSummaryCard\".into()))?;\n        rsp.advance(1);\n        let rsp: RespSummaryCard = jcers::from_buf(&mut rsp)?;\n        let info = SummaryCardInfo {\n            sex: rsp.sex,\n            age: rsp.age,\n            nickname: rsp.nickname,\n            level: rsp.level,\n            city: rsp.city,\n            sign: rsp.sign,\n            mobile: rsp.mobile,\n            uin: rsp.uin,\n            login_days: rsp.login_days,\n            cookie: head.cookie,\n        };\n        // TODO more info\n        Ok(info)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/summary_card/mod.rs",
    "content": "pub mod builder;\npub mod decoder;\n"
  },
  {
    "path": "ricq-core/src/command/visitor_svc/builder.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::Bytes;\nuse jcers::JcePut;\n\nuse crate::command::common::pack_uni_request_data;\nuse crate::jce;\nuse crate::protocol::packet::Packet;\n\nimpl super::super::super::Engine {\n    // VisitorSvc.ReqFavorite\n    pub fn build_send_like_packet(\n        &self,\n        uin: i64,\n        count: i32,\n        source: i32,\n        cookies: Bytes,\n    ) -> Packet {\n        let seq = self.next_seq();\n        let req = jce::ReqFavorite {\n            header: jce::QQServiceReqHead {\n                uin: self.uin(),\n                sh_version: 1,\n                seq: seq as i32,\n                req_type: 1,\n                triggered: 0,\n                cookies,\n            },\n            mid: uin,\n            op_type: 0,\n            source,\n            count,\n        };\n        let buf = jce::RequestDataVersion3 {\n            map: HashMap::from([(\n                \"ReqFavorite\".to_string(),\n                pack_uni_request_data(&req.freeze()),\n            )]),\n        };\n        let pkt = jce::RequestPacket {\n            i_version: 3,\n            s_servant_name: \"VisitorSvc\".to_string(),\n            s_func_name: \"ReqFavorite\".to_string(),\n            s_buffer: buf.freeze(),\n            ..Default::default()\n        };\n        self.uni_packet(\"VisitorSvc.ReqFavorite\", pkt.freeze())\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/visitor_svc/mod.rs",
    "content": "pub mod builder;\n"
  },
  {
    "path": "ricq-core/src/command/wtlogin/builder.rs",
    "content": "use bytes::{BufMut, BytesMut};\n\nuse crate::binary::packet_writer::{CounterWriter, Either, PacketAppender, PacketWriter, WriteLV};\nuse crate::binary::BinaryWriter;\nuse crate::command::wtlogin::builder::utils::*;\nuse crate::command::wtlogin::tlv_writer::*;\nuse crate::protocol::version::Protocol;\nuse crate::protocol::{\n    oicq::{self, EncryptionMethod},\n    packet::{EncryptType, Packet, PacketType},\n};\n\nimpl super::super::super::Engine {\n    // wtlogin.trans_emp\n    pub fn build_qrcode_fetch_request_packet(&self) -> Packet {\n        let transport = &self.transport;\n        let seq = self.next_seq();\n        let req = self.build_oicq_request_packet(0, 0x812, &{\n            let mut w = BytesMut::new();\n            let req_body = build_code2d_request_packet(0, 0, 0x31, &{\n                let mut w = BytesMut::new();\n                w.put_u16(0); // const\n                w.put_u32(16); // app id\n                w.put_u64(0); // const long user\n                w.put_u8(8); // const\n                w.write_short_lv([].as_slice());\n                let tlv_writer = CounterWriter::default()\n                    .append(t16(\n                        transport.version.sso_version,\n                        16, // app id ?\n                        transport.version.sub_app_id,\n                        &transport.sig.guid,\n                        transport.version.apk_id,\n                        transport.version.sort_version_name,\n                        transport.version.apk_sign,\n                    ))\n                    .append(t1b(0, 0, 3, 4, 72, 2, 2))\n                    .append(t1d(transport.version.misc_bitmap))\n                    .append(if matches!(transport.version.protocol, Protocol::MacOS) {\n                        t1f(\n                            false,\n                            \"Mac OSX\",\n                            \"10\",\n                            \"mac carrier\",\n                            &transport.device.apn,\n                            2, // wifi\n                        )\n                    } else {\n                        t1f(\n                            false,\n                            &transport.device.os_type,\n                            \"7.1.2\",\n                            &transport.device.sim_info,\n                            &transport.device.apn,\n                            2, // wifi\n                        )\n                    })\n                    .append(t33(&transport.sig.guid))\n                    .append(t35(\n                        if matches!(transport.version.protocol, Protocol::MacOS) {\n                            5\n                        } else {\n                            8\n                        },\n                    ));\n                w.put_u16(tlv_writer.count as u16);\n                tlv_writer.write(&mut w);\n                w\n            });\n            w.put_u8(0);\n            w.put_u16(req_body.len() as u16);\n            w.put_u32(transport.version.app_id);\n            w.put_u32(114); // const role\n            w.write_hex(\"000000\");\n            w.put_slice(&req_body);\n            w\n        });\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.trans_emp\".into(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.trans_emp\n    pub fn build_qrcode_result_query_request_packet(&self, sig: &[u8]) -> Packet {\n        let seq = self.next_seq();\n        let req = self.build_oicq_request_packet(0, 0x812, &{\n            let mut w = BytesMut::new();\n            let req_body = build_code2d_request_packet(1, 0, 0x12, &{\n                let mut w = Vec::new();\n                w.put_u16(5);\n                w.put_u8(1);\n                w.put_u32(8); // 0x68 ?\n                w.put_u32(16);\n                w.write_short_lv(sig);\n                w.put_u64(0);\n                w.put_u8(8);\n                w.write_short_lv([].as_slice());\n                w.put_u16(0);\n                w\n            });\n            w.put_u8(0);\n            w.put_u16(req_body.len() as u16);\n            w.put_u32(self.transport.version.app_id);\n            w.put_u32(114); // const role\n            w.write_hex(\"000000\");\n            w.put_slice(&req_body);\n            w\n        });\n\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.trans_emp\".into(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.login\n    pub fn build_qrcode_login_packet(\n        &self,\n        tmp_pwd: &[u8],\n        tmp_no_pic_sig: &[u8],\n        tgt_qr: &[u8],\n    ) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let req = self.build_oicq_request_packet(self.uin(), 0x0810, &{\n            let mut w = BytesMut::new();\n            w.put_u16(9);\n\n            let dev_info = transport.device.gen_pb_data();\n            let tlv_writer = CounterWriter::default()\n                .append(t18(16, self.uin() as u32))\n                .append(t1(self.uin() as u32, &transport.device.ip_address))\n                .append(tlv(0x106, tmp_pwd))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ))\n                .append(t100(\n                    transport.version.sso_version,\n                    transport.version.sub_app_id,\n                    transport.version.main_sig_map,\n                ))\n                .append(t107(0))\n                .append(t142(transport.version.apk_id))\n                .append(t144(\n                    &transport.device.imei,\n                    &dev_info,\n                    &transport.device.os_type,\n                    &transport.device.version.release,\n                    &transport.device.sim_info,\n                    &transport.device.apn,\n                    false,\n                    true,\n                    false,\n                    guid_flag(),\n                    &transport.device.model,\n                    &transport.sig.guid,\n                    &transport.device.brand,\n                    &transport.sig.tgtgt_key,\n                ))\n                .append(t145(&transport.sig.guid))\n                .append(t147(\n                    16,\n                    transport.version.sort_version_name,\n                    transport.version.apk_sign,\n                ))\n                .append(t16a(tmp_no_pic_sig))\n                .append(t154(seq))\n                .append(t141(&transport.device.sim_info, &transport.device.apn))\n                .append(t8(2052))\n                .append(t511(vec![\n                    \"tenpay.com\",\n                    \"openmobile.qq.com\",\n                    \"docs.qq.com\",\n                    \"connect.qq.com\",\n                    \"qzone.qq.com\",\n                    \"vip.qq.com\",\n                    \"gamecenter.qq.com\",\n                    \"qun.qq.com\",\n                    \"game.qq.com\",\n                    \"qqweb.qq.com\",\n                    \"office.qq.com\",\n                    \"ti.qq.com\",\n                    \"mail.qq.com\",\n                    \"mma.qq.com\",\n                ]))\n                .append(t187(&transport.device.mac_address))\n                .append(t188(&transport.device.android_id))\n                .append_option(if !transport.device.imsi_md5.is_empty() {\n                    Some(t194(transport.device.imsi_md5.as_slice()))\n                } else {\n                    None\n                })\n                .append(t191(0x00))\n                .append_option(\n                    if !transport.device.wifi_bssid.is_empty()\n                        && !transport.device.wifi_ssid.is_empty()\n                    {\n                        Some(t202(\n                            &transport.device.wifi_bssid,\n                            &transport.device.wifi_ssid,\n                        ))\n                    } else {\n                        None\n                    },\n                )\n                .append(t177(\n                    transport.version.build_time,\n                    transport.version.sdk_version,\n                ))\n                .append(t516())\n                .append(t521(8))\n                .append(t318(tgt_qr));\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n\n            // let v:Vec<u8> = vec![0x01, 0x00];\n            // w.put_slice(&t525(&t536(&v)));\n            w\n        });\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.login\".to_string(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.login\n    pub fn build_device_lock_login_packet(&self) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let req = self.build_oicq_request_packet(self.uin(), 0x0810, &{\n            let mut w = BytesMut::new();\n            w.put_u16(20);\n            let tlv_writer = CounterWriter::default()\n                .append(t8(2052))\n                .append(t104(&transport.sig.t104))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ))\n                .append(t401(&transport.sig.g));\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n            w\n        });\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.login\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.login\n    pub fn build_captcha_packet(&self, result: String, sign: &[u8]) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let req = self.build_oicq_request_packet(self.uin(), 0x810, &{\n            let mut w = BytesMut::new();\n            w.put_u16(2); // sub command\n            let tlv_writer = CounterWriter::default()\n                .append(t2(result, sign))\n                .append(t8(2052))\n                .append(t104(&transport.sig.t104))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ));\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n            w\n        });\n\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.login\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.login\n    pub fn build_sms_request_packet(&self) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let req = self.build_oicq_request_packet(self.uin(), 0x810, &{\n            let mut w = BytesMut::new();\n            w.put_u16(8);\n\n            let tlv_writer = CounterWriter::default()\n                .append(t8(2052))\n                .append(t104(&transport.sig.t104))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ))\n                .append(t174(&transport.sig.t174))\n                .append(t17a(9))\n                .append(t197());\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n            w\n        });\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.login\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.login\n    pub fn build_sms_code_submit_packet(&self, code: &str, sign: &[u8]) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let req = self.build_oicq_request_packet(self.uin(), 0x810, &{\n            let mut w = BytesMut::new();\n            w.put_u16(7);\n            let tlv_writer = CounterWriter::default()\n                .append(t8(2052))\n                .append(t104(&transport.sig.t104))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ))\n                .append(t174(&transport.sig.t174))\n                .append(t17c(code))\n                .append(t401(&transport.sig.g))\n                .append(t198())\n                .append(tlv(0x544, sign));\n\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n            w\n        });\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.login\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.login\n    pub fn build_ticket_submit_packet(&self, ticket: &str, sign: &[u8]) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let req = self.build_oicq_request_packet(self.uin(), 0x810, &{\n            let mut w = BytesMut::new();\n            w.put_u16(2);\n\n            let tlv_writer = CounterWriter::default()\n                .append(t193(ticket))\n                .append(t8(2052))\n                .append(t104(&transport.sig.t104))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ))\n                .append(tlv(0x544, sign))\n                .append(tlv(0x547, &*transport.sig.t547));\n\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n            w\n        });\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.login\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.exchange_emp\n    pub fn build_request_tgtgt_no_pic_sig_packet(&self) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let codec = &self.transport.oicq_codec;\n        let req = {\n            let mut w = BytesMut::new();\n            w.put_u16(15);\n\n            let dev_info = transport.device.gen_pb_data();\n            let tlv_writer = CounterWriter::default()\n                .append(t18(16, self.uin() as u32))\n                .append(t1(self.uin() as u32, &transport.device.ip_address))\n                .append(tlv(0x106, &*transport.sig.encrypted_a1))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ))\n                .append(t100(\n                    transport.version.sso_version,\n                    2,\n                    transport.version.main_sig_map,\n                ))\n                .append(t107(0))\n                .append(t108(&transport.sig.ksid))\n                .append(t144(\n                    &transport.device.android_id,\n                    &dev_info,\n                    &transport.device.os_type,\n                    &transport.device.version.release,\n                    &transport.device.sim_info,\n                    &transport.device.apn,\n                    false,\n                    true,\n                    false,\n                    guid_flag(),\n                    &transport.device.model,\n                    &transport.sig.guid,\n                    &transport.device.brand,\n                    &transport.sig.tgtgt_key,\n                ))\n                .append(t142(transport.version.apk_id))\n                .append(t145(&transport.sig.guid))\n                .append(t16a(&transport.sig.srm_token))\n                .append(t154(seq))\n                .append(t141(&transport.device.sim_info, &transport.device.apn))\n                .append(t8(2052))\n                .append(t511(vec![\n                    \"tenpay.com\",\n                    \"openmobile.qq.com\",\n                    \"docs.qq.com\",\n                    \"connect.qq.com\",\n                    \"qzone.qq.com\",\n                    \"vip.qq.com\",\n                    \"gamecenter.qq.com\",\n                    \"qun.qq.com\",\n                    \"game.qq.com\",\n                    \"qqweb.qq.com\",\n                    \"office.qq.com\",\n                    \"ti.qq.com\",\n                    \"mail.qq.com\",\n                    \"mma.qq.com\",\n                ]))\n                .append(t147(\n                    16,\n                    transport.version.sort_version_name,\n                    transport.version.apk_sign,\n                ))\n                .append(t177(\n                    transport.version.build_time,\n                    transport.version.sdk_version,\n                ))\n                .append(t400(\n                    &transport.sig.g,\n                    self.uin(),\n                    &transport.sig.guid,\n                    &transport.sig.dpwd,\n                    1,\n                    16,\n                    &transport.sig.rand_seed,\n                ))\n                .append(t187(&transport.device.mac_address))\n                .append(t188(&transport.device.android_id))\n                .append(t194(&transport.device.imsi_md5))\n                .append(t202(\n                    &transport.device.wifi_bssid,\n                    &transport.device.wifi_ssid,\n                ))\n                .append(t516())\n                .append(t521(0))\n                .append(t525(t536(&[0x01, 0x00])))\n                .append(if let Some(ref qimei) = transport.device.qimei {\n                    Either::Left(tlv(0x545, qimei.q16.as_bytes()))\n                } else {\n                    Either::Right(tlv(0x545, transport.device.imei.as_bytes()))\n                });\n            // TODO 544\n\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n\n            w.freeze()\n        };\n        let m = oicq::Message {\n            uin: self.uin() as u32,\n            command: 0x810,\n            body: req,\n            encryption_method: EncryptionMethod::ST,\n        };\n        Packet {\n            packet_type: PacketType::Simple,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: codec.encode(m),\n            command_name: \"wtlogin.exchange_emp\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.exchange_emp TODO change d2\n    pub fn build_request_change_sig_packet(&self, main_sig_map: Option<u32>) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let req = self.build_oicq_request_packet(self.uin(), 0x810, &{\n            let mut w = BytesMut::new();\n            w.put_u16(11);\n\n            let dev_info = transport.device.gen_pb_data();\n            let tgtgt_key = md5::compute(&transport.sig.d2key).to_vec();\n            let tlv_writer = CounterWriter::default()\n                .append(t100(\n                    transport.version.sso_version,\n                    100,\n                    main_sig_map.unwrap_or(transport.version.main_sig_map),\n                ))\n                .append(t10a(&transport.sig.tgt))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ))\n                .append(t108(&transport.sig.ksid))\n                .append(t144(\n                    &transport.device.android_id,\n                    &dev_info,\n                    &transport.device.os_type,\n                    &transport.device.version.release,\n                    &transport.device.sim_info,\n                    &transport.device.apn,\n                    false,\n                    true,\n                    false,\n                    guid_flag(),\n                    &transport.device.model,\n                    &transport.sig.guid,\n                    &transport.device.brand,\n                    &tgtgt_key,\n                ))\n                .append(t112(self.uin()))\n                .append(t143(&transport.sig.d2)) // TODO change d2 145\n                .append(t142(transport.version.apk_id))\n                .append(t154(seq))\n                .append(t18(16, self.uin() as u32))\n                .append(t141(&transport.device.sim_info, &transport.device.apn))\n                .append(t8(2052))\n                .append(t147(\n                    16,\n                    transport.version.sort_version_name,\n                    transport.version.apk_sign,\n                ))\n                .append(t177(\n                    transport.version.build_time,\n                    transport.version.sdk_version,\n                ))\n                .append(t187(&transport.device.mac_address))\n                .append(t188(&transport.device.android_id))\n                .append(t194(&transport.device.imsi_md5))\n                .append(t511(vec![\n                    \"tenpay.com\",\n                    \"openmobile.qq.com\",\n                    \"docs.qq.com\",\n                    \"connect.qq.com\",\n                    \"qzone.qq.com\",\n                    \"vip.qq.com\",\n                    \"gamecenter.qq.com\",\n                    \"qun.qq.com\",\n                    \"game.qq.com\",\n                    \"qqweb.qq.com\",\n                    \"office.qq.com\",\n                    \"ti.qq.com\",\n                    \"mail.qq.com\",\n                    \"mma.qq.com\",\n                ]))\n                .append(t202(\n                    &transport.device.wifi_bssid,\n                    &transport.device.wifi_ssid,\n                ));\n            // TODO 544\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n            // w.put_slice(&t202(self.device_info.wifi_bssid.as_bytes(), self.device_info.wifi_ssid.as_bytes()));\n            w\n        });\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.exchange_emp\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n\n    // wtlogin.login\n    pub fn build_login_packet(\n        &self,\n        password_md5: &[u8],\n        sign: &[u8],\n        allow_slider: bool,\n    ) -> Packet {\n        let seq = self.next_seq();\n        let transport = &self.transport;\n        let req = self.build_oicq_request_packet(self.uin(), 0x0810, &{\n            let mut w = BytesMut::new();\n            w.put_u16(9);\n\n            let dev_info = transport.device.gen_pb_data();\n            let tlv_writer = CounterWriter::default()\n                .append(t18(16, self.uin() as u32))\n                .append(t1(self.uin() as u32, &transport.device.ip_address))\n                .append(t106(\n                    self.uin() as u32,\n                    0,\n                    transport.version.app_id,\n                    transport.version.sso_version,\n                    password_md5,\n                    true,\n                    &transport.sig.guid,\n                    &transport.sig.tgtgt_key,\n                    0,\n                ))\n                .append(t116(\n                    transport.version.misc_bitmap,\n                    transport.version.sub_sig_map,\n                ))\n                .append(t100(\n                    transport.version.sso_version,\n                    transport.version.sub_app_id,\n                    transport.version.main_sig_map,\n                ))\n                .append(t107(0))\n                .append(t142(transport.version.apk_id))\n                .append(t144(\n                    &transport.device.imei,\n                    &dev_info,\n                    &transport.device.os_type,\n                    &transport.device.version.release,\n                    &transport.device.sim_info,\n                    &transport.device.apn,\n                    false,\n                    true,\n                    false,\n                    guid_flag(),\n                    &transport.device.model,\n                    &transport.sig.guid,\n                    &transport.device.brand,\n                    &transport.sig.tgtgt_key,\n                ))\n                .append(t145(&transport.sig.guid))\n                .append(t147(\n                    16,\n                    transport.version.sort_version_name,\n                    transport.version.apk_sign,\n                ))\n                .append(t154(seq))\n                .append(t141(&transport.device.sim_info, &transport.device.apn))\n                .append(t8(2052))\n                .append(t511(vec![\n                    \"tenpay.com\",\n                    \"openmobile.qq.com\",\n                    \"docs.qq.com\",\n                    \"connect.qq.com\",\n                    \"qzone.qq.com\",\n                    \"vip.qq.com\",\n                    \"gamecenter.qq.com\",\n                    \"qun.qq.com\",\n                    \"game.qq.com\",\n                    \"qqweb.qq.com\",\n                    \"office.qq.com\",\n                    \"ti.qq.com\",\n                    \"mail.qq.com\",\n                    \"mma.qq.com\",\n                ]))\n                .append(t187(&transport.device.mac_address))\n                .append(t188(&transport.device.android_id))\n                .append(t194(&transport.device.imsi_md5))\n                .append_option(if allow_slider { Some(t191(0x82)) } else { None })\n                .append(t202(\n                    &transport.device.wifi_bssid,\n                    &transport.device.wifi_ssid,\n                ))\n                .append(t177(\n                    transport.version.build_time,\n                    transport.version.sdk_version,\n                ))\n                .append(t516())\n                .append(t521(0))\n                .append(t525(t536(&[0x01, 0x00])))\n                .append(tlv(0x544, sign))\n                .append(if let Some(ref qimei) = transport.device.qimei {\n                    Either::Left(tlv(0x545, qimei.q16.as_bytes()))\n                } else {\n                    Either::Right(tlv(0x545, transport.device.imei.as_bytes()))\n                });\n            w.put_u16(tlv_writer.count as u16);\n            tlv_writer.write(&mut w);\n\n            w.freeze()\n        });\n        Packet {\n            packet_type: PacketType::Login,\n            encrypt_type: EncryptType::EmptyKey,\n            seq_id: seq as i32,\n            body: req,\n            command_name: \"wtlogin.login\".into(),\n            uin: self.uin(),\n            ..Default::default()\n        }\n    }\n}\n\nmod utils {\n    use bytes::{BufMut, Bytes, BytesMut};\n    use std::time::UNIX_EPOCH;\n\n    use crate::command::common::PbToBytes;\n    use crate::pb;\n    use crate::protocol::device::Device;\n\n    pub fn build_code2d_request_packet(seq: u32, j: u64, cmd: u16, body: &[u8]) -> Bytes {\n        let mut w = BytesMut::new();\n        w.put_u32(UNIX_EPOCH.elapsed().unwrap().as_secs() as u32);\n        w.put_u8(2);\n        w.put_u16((43 + body.len() + 1) as u16);\n        w.put_u16(cmd);\n        w.put_slice(&[0; 21]);\n        w.put_u8(3);\n        w.put_u16(0);\n        w.put_u16(50);\n        w.put_u32(seq);\n        w.put_u64(j);\n        w.put_slice(body);\n        w.put_u8(3);\n        w.into()\n    }\n\n    pub trait DeviceToPb {\n        fn gen_pb_data(&self) -> Bytes;\n    }\n\n    impl DeviceToPb for Device {\n        fn gen_pb_data(&self) -> Bytes {\n            pb::DeviceInfo {\n                bootloader: self.bootloader.to_owned(),\n                proc_version: self.proc_version.to_owned(),\n                codename: self.version.codename.to_owned(),\n                incremental: self.version.incremental.to_owned(),\n                fingerprint: self.finger_print.to_owned(),\n                boot_id: self.boot_id.to_owned(),\n                android_id: self.android_id.to_owned(),\n                base_band: self.base_band.to_owned(),\n                inner_version: self.version.incremental.to_owned(),\n            }\n            .to_bytes()\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/wtlogin/decoder.rs",
    "content": "use bytes::{Buf, Bytes};\n\nuse crate::binary::BinaryReader;\nuse crate::command::wtlogin::{LoginResponse, QRCodeConfirmed, QRCodeImageFetch, QRCodeState};\nuse crate::{RQError, RQResult};\n\nimpl super::super::super::Engine {\n    pub fn decode_trans_emp_response(&self, mut payload: Bytes) -> RQResult<QRCodeState> {\n        if payload.len() < 48 {\n            return Err(RQError::Decode(\"invalid payload length\".into()));\n        }\n        payload.advance(5); // trans req head\n        payload.get_u8();\n        payload.get_u16();\n        let cmd = payload.get_u16();\n        payload.advance(21);\n        payload.get_u8();\n        payload.get_u16();\n        payload.get_u16();\n        payload.get_i32();\n        payload.get_i64();\n        let len = payload.remaining() - 1;\n        let mut body = payload.copy_to_bytes(len);\n        if cmd == 0x31 {\n            body.get_u16();\n            body.get_i32();\n            let code = body.get_u8();\n            if code != 0 {\n                return Err(RQError::Decode(format!(\"body code: {code}\")));\n            }\n            let sig = body.read_bytes_short();\n            body.get_u16();\n            let mut m = body.read_tlv_map(2);\n            if m.contains_key(&0x17) {\n                return Ok(QRCodeState::ImageFetch(QRCodeImageFetch {\n                    image_data: m\n                        .remove(&0x17)\n                        .ok_or_else(|| RQError::Decode(\"missing 0x17\".into()))?,\n                    sig,\n                }));\n            }\n        }\n        if cmd == 0x12 {\n            let mut a_var_len = body.get_u16();\n            if a_var_len != 0 {\n                a_var_len -= 1; // 阴间的位移操作\n                if body.get_u8() == 2 {\n                    body.get_i64(); //uin?\n                    a_var_len -= 8;\n                }\n            }\n            if a_var_len > 0 {\n                body.advance(a_var_len as usize);\n            }\n            body.get_i32();\n            let code = body.get_u8();\n            if code != 0 {\n                return match code {\n                    0x30 => Ok(QRCodeState::WaitingForScan),\n                    0x35 => Ok(QRCodeState::WaitingForConfirm),\n                    0x36 => Ok(QRCodeState::Canceled),\n                    0x11 => Ok(QRCodeState::Timeout),\n                    _ => Err(RQError::Decode(\"invalid body code\".to_string())),\n                };\n            }\n            let uin = body.get_i64();\n            body.get_i32(); // sig create time\n            body.get_u16();\n            let mut m = body.read_tlv_map(2);\n            return Ok(QRCodeState::Confirmed(QRCodeConfirmed {\n                uin,\n                tmp_pwd: m\n                    .remove(&0x18)\n                    .ok_or_else(|| RQError::Decode(\"missing 0x18\".into()))?,\n                tmp_no_pic_sig: m\n                    .remove(&0x19)\n                    .ok_or_else(|| RQError::Decode(\"missing 0x19\".into()))?,\n                tgt_qr: m\n                    .remove(&0x65)\n                    .ok_or_else(|| RQError::Decode(\"missing 0x65\".into()))?,\n                tgtgt_key: m\n                    .remove(&0x1e)\n                    .ok_or_else(|| RQError::Decode(\"missing 0x1e\".into()))?,\n            }));\n        }\n        Err(RQError::Decode(\n            \"decode_trans_emp_response unknown error\".to_string(),\n        ))\n    }\n\n    pub fn decode_login_response(&self, mut reader: Bytes) -> RQResult<LoginResponse> {\n        let _sub_command = reader.get_u16(); // sub command\n        let status = reader.get_u8();\n        // TODO status=213 不能执行下面的步骤 panic\n        reader.get_u16();\n        let tlv_map = reader.read_tlv_map(2);\n        LoginResponse::decode(status, tlv_map, &self.transport.sig.tgtgt_key)\n    }\n\n    pub fn decode_exchange_emp_response(&self, mut payload: Bytes) -> RQResult<LoginResponse> {\n        let sub_command = payload.get_u16();\n        let status = payload.get_u8();\n        payload.get_u16();\n        let tlv_map = payload.read_tlv_map(2);\n        if status != 0 {\n            return Err(RQError::Decode(format!(\n                \"decode_exchange_emp_response status: {status}\"\n            )));\n        }\n        let encrypt_key = if sub_command == 11 {\n            md5::compute(&self.transport.sig.d2key).to_vec()\n        } else {\n            self.transport.sig.tgtgt_key.to_vec()\n        };\n\n        LoginResponse::decode(status, tlv_map, &encrypt_key)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/wtlogin/mod.rs",
    "content": "#![allow(clippy::large_enum_variant)]\nuse std::collections::HashMap;\nuse std::time::UNIX_EPOCH;\n\nuse bytes::{Buf, BufMut, Bytes, BytesMut};\nuse rsa::BigUint;\n\nuse crate::binary::{BinaryReader, BinaryWriter};\nuse crate::command::wtlogin::tlv_reader::*;\nuse crate::{RQError, RQResult};\n\nmod builder;\nmod decoder;\npub mod tlv_reader;\npub mod tlv_writer;\n\n#[derive(Debug, Clone)]\npub enum QRCodeState {\n    ImageFetch(QRCodeImageFetch),\n    WaitingForScan,\n    WaitingForConfirm,\n    Timeout,\n    Confirmed(QRCodeConfirmed),\n    Canceled,\n}\n\n#[derive(Debug, Clone)]\npub struct QRCodeImageFetch {\n    pub image_data: Bytes,\n    pub sig: Bytes,\n}\n\n#[derive(Debug, Clone)]\npub struct QRCodeConfirmed {\n    pub uin: i64,\n    pub tmp_pwd: Bytes,\n    pub tmp_no_pic_sig: Bytes,\n    pub tgt_qr: Bytes,\n    pub tgtgt_key: Bytes,\n}\n\n#[derive(Debug, Clone)]\npub struct ImageCaptcha {\n    pub sign: Bytes,\n    pub image: Bytes,\n}\n\n#[derive(Debug, Clone)]\npub enum LoginResponse {\n    Success(LoginSuccess),\n    // slider or image captcha\n    NeedCaptcha(LoginNeedCaptcha),\n    AccountFrozen,\n    // sms or qrcode\n    DeviceLocked(LoginDeviceLocked),\n    TooManySMSRequest,\n    // More login packet needed\n    DeviceLockLogin(LoginDeviceLockLogin),\n    UnknownStatus(LoginUnknownStatus),\n}\n\n#[derive(Debug, Clone)]\npub struct LoginSuccess {\n    pub rollback_sig: Option<T161>,\n    pub rand_seed: Option<Bytes>,\n    pub ksid: Option<Bytes>,\n    pub account_info: Option<T11A>,\n    pub t512: Option<T512>,\n    // 不知道有没有 t402\n    pub t402: Option<Bytes>,\n    pub wt_session_ticket_key: Option<Bytes>,\n    pub srm_token: Option<Bytes>,\n    pub t133: Option<Bytes>,\n    pub encrypt_a1: Option<Bytes>,\n    pub tgt: Option<Bytes>,\n    pub tgt_key: Option<Bytes>,\n    pub user_st_key: Option<Bytes>,\n    pub user_st_web_sig: Option<Bytes>,\n    pub s_key: Option<Bytes>,\n    pub s_key_expired_time: i64,\n    pub d2: Option<Bytes>,\n    pub d2key: Option<Bytes>,\n    pub device_token: Option<Bytes>,\n}\n\n#[derive(Debug, Clone)]\npub struct LoginNeedCaptcha {\n    pub t104: Option<Bytes>,\n    pub verify_url: Option<String>,\n    pub image_captcha: Option<ImageCaptcha>,\n    pub t547: Option<Bytes>,\n}\n\n#[derive(Debug, Clone)]\npub struct LoginDeviceLocked {\n    pub t104: Option<Bytes>,\n    pub t174: Option<Bytes>,\n    pub t402: Option<Bytes>,\n    pub sms_phone: Option<String>,\n    pub verify_url: Option<String>,\n    pub message: Option<String>,\n    pub rand_seed: Option<Bytes>,\n}\n\n#[derive(Debug, Clone)]\npub struct LoginDeviceLockLogin {\n    pub t104: Option<Bytes>,\n    pub t402: Option<Bytes>,\n    pub rand_seed: Option<Bytes>,\n}\n\n#[derive(Debug, Clone)]\npub struct LoginUnknownStatus {\n    pub status: u8,\n    pub tlv_map: HashMap<u16, Bytes>,\n    pub message: String,\n}\n\nimpl LoginResponse {\n    pub fn decode(\n        status: u8,\n        mut tlv_map: HashMap<u16, Bytes>,\n        encrypt_key: &[u8],\n    ) -> RQResult<Self> {\n        let resp = match status {\n            0 => {\n                let mut t119 = tlv_map\n                    .remove(&0x119)\n                    .map(|v| decode_t119(&v, encrypt_key))\n                    .ok_or_else(|| RQError::Decode(\"missing 0x119\".to_string()))?;\n                LoginResponse::Success(LoginSuccess {\n                    rollback_sig: tlv_map.remove(&0x161).map(decode_t161),\n                    rand_seed: tlv_map.remove(&0x403),\n                    ksid: t119.remove(&0x108),\n                    account_info: t119.remove(&0x11a).map(read_t11a),\n                    t512: t119.remove(&0x512).map(read_t512),\n                    t402: tlv_map.remove(&0x402),\n                    wt_session_ticket_key: t119.remove(&0x134),\n                    srm_token: t119.remove(&0x16a),\n                    t133: t119.remove(&0x133),\n                    encrypt_a1: t119.remove(&0x106),\n                    tgt: t119.remove(&0x10a),\n                    tgt_key: t119.remove(&0x10d),\n                    user_st_key: t119.remove(&0x10e),\n                    user_st_web_sig: t119.remove(&0x103),\n                    s_key: t119.remove(&0x120),\n                    s_key_expired_time: UNIX_EPOCH.elapsed().unwrap().as_secs() as i64 + 21600,\n                    d2: t119.remove(&0x143),\n                    d2key: t119.remove(&0x305),\n                    device_token: t119.remove(&0x322),\n                })\n            }\n            2 => LoginResponse::NeedCaptcha(LoginNeedCaptcha {\n                t104: tlv_map.remove(&0x104),\n                verify_url: tlv_map\n                    .remove(&0x192)\n                    .map(|v| String::from_utf8_lossy(&v).into_owned()),\n                image_captcha: tlv_map.remove(&0x165).map(|mut img_data| {\n                    let sign_len = img_data.get_u16();\n                    img_data.get_u16();\n                    let image_sign = img_data.copy_to_bytes(sign_len as usize);\n                    ImageCaptcha {\n                        sign: image_sign,\n                        image: img_data,\n                    }\n                }),\n                t547: tlv_map.remove(&0x546).map(t546_to_t547),\n            }),\n            40 => LoginResponse::AccountFrozen,\n            160 | 239 => {\n                let t174 = tlv_map.remove(&0x174);\n                let t178 = tlv_map.remove(&0x178);\n                let sms_phone = if t174.is_some() {\n                    t178.map(|mut v| {\n                        let country_code = v.read_string_short();\n                        let phone_number = v.read_string_short();\n                        format!(\"+{} {}\", country_code, phone_number)\n                    })\n                } else {\n                    None\n                };\n                LoginResponse::DeviceLocked(LoginDeviceLocked {\n                    sms_phone,\n                    verify_url: tlv_map\n                        .remove(&0x204)\n                        .map(|v| String::from_utf8_lossy(&v).into_owned()),\n                    message: tlv_map\n                        .remove(&0x17e)\n                        .map(|v| String::from_utf8_lossy(&v).into_owned()),\n                    rand_seed: tlv_map.remove(&0x403),\n                    t104: tlv_map.remove(&0x104),\n                    t174,\n                    t402: tlv_map.remove(&0x402),\n                })\n            }\n            162 => LoginResponse::TooManySMSRequest,\n            204 => LoginResponse::DeviceLockLogin(LoginDeviceLockLogin {\n                t104: tlv_map.remove(&0x104),\n                t402: tlv_map.remove(&0x402),\n                rand_seed: tlv_map.remove(&0x403),\n            }),\n            _ => {\n                // status=1 可能是密码错误\n                let mut _title = \"\".into();\n                let mut message = \"\".into();\n                if let Some(mut v) = tlv_map.remove(&0x146) {\n                    v.advance(4);\n                    _title = v.read_string_short();\n                    message = v.read_string_short();\n                }\n                LoginResponse::UnknownStatus(LoginUnknownStatus {\n                    status,\n                    tlv_map,\n                    message,\n                })\n            }\n        };\n        Ok(resp)\n    }\n}\n\npub fn t546_to_t547(mut data: Bytes) -> Bytes {\n    let a = data.get_u8();\n    let typ = data.get_u8();\n    let c = data.get_u8();\n    let mut ok = data.get_u8() != 0;\n    let e = data.get_u16();\n    let f = data.get_u16();\n    let src = data.read_bytes_short();\n    let tgt = data.read_bytes_short().to_vec();\n    let cpy = data.read_bytes_short();\n    let mut cnt = 0;\n    let mut dst = Vec::new();\n    let mut elp = 0;\n    if typ == 2 && tgt.len() == 32 {\n        let start = std::time::SystemTime::now();\n        let mut tmp = BigUint::from_bytes_be(&src);\n        use sha2::{Digest, Sha256};\n        let mut hash = Sha256::digest(tmp.to_bytes_be()).to_vec();\n        while hash != tgt {\n            tmp += 1u8;\n            hash = Sha256::digest(tmp.to_bytes_be()).to_vec();\n            cnt += 1;\n        }\n        ok = true;\n        dst = tmp.to_bytes_be();\n        elp = start.elapsed().unwrap().as_millis() as u32;\n    }\n    let mut w = BytesMut::new();\n    w.put_u8(a);\n    w.put_u8(typ);\n    w.put_u8(c);\n    w.put_u8(if ok { 1 } else { 0 });\n    w.put_u16(e);\n    w.put_u16(f);\n    w.write_bytes_short(&src);\n    w.write_bytes_short(&tgt);\n    w.write_bytes_short(&cpy);\n    if ok {\n        w.write_bytes_short(&dst);\n        w.put_u32(elp);\n        w.put_u32(cnt);\n    }\n    w.freeze()\n}\n"
  },
  {
    "path": "ricq-core/src/command/wtlogin/tlv_reader.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::{Buf, BufMut, Bytes, BytesMut};\n\nuse crate::binary::BinaryReader;\nuse crate::crypto::qqtea_decrypt;\n\n#[derive(Debug, Clone)]\npub struct T161 {\n    // 172\n    pub rollback_sig: Option<Bytes>,\n    // 173\n    // 17f\n}\n#[derive(Debug, Clone)]\npub struct T113 {\n    pub uin: i32,\n}\n#[derive(Debug, Clone)]\npub struct T125 {\n    pub open_id: Bytes,\n    pub open_key: Bytes,\n}\n#[derive(Debug, Clone, Default)]\npub struct T11A {\n    pub face: u16,\n    pub gender: u8,\n    pub age: u8,\n    pub nick: String,\n}\n#[derive(Debug, Clone)]\npub struct T199 {\n    pub open_id: Bytes,\n    pub pay_token: Bytes,\n}\n#[derive(Debug, Clone)]\npub struct T200 {\n    pub pf: Bytes,\n    pub pf_key: Bytes,\n}\n#[derive(Debug, Clone)]\npub struct T512 {\n    pub ps_key_map: HashMap<String, Bytes>,\n    pub pt4_token_map: HashMap<String, Bytes>,\n}\n#[derive(Debug, Clone)]\npub struct T531 {\n    pub a1: Bytes,\n    pub no_pic_sig: Bytes,\n}\n\npub fn decode_t161(mut data: Bytes) -> T161 {\n    data.advance(2);\n    let mut m = data.read_tlv_map(2);\n    T161 {\n        rollback_sig: m.remove(&0x172),\n    }\n}\n\npub fn decode_t119(data: &[u8], ek: &[u8]) -> HashMap<u16, Bytes> {\n    let mut reader = Bytes::from(qqtea_decrypt(data, ek));\n    reader.advance(2);\n    reader.read_tlv_map(2)\n}\n\npub fn decode_t113(mut data: Bytes) -> T113 {\n    T113 {\n        uin: data.get_i32(),\n    }\n}\n\npub fn decode_t186(_: &[u8]) {}\n\n// not used\npub fn read_t125(data: &[u8]) -> T125 {\n    let mut reader = Bytes::from(data.to_owned());\n    let open_id = reader.read_bytes_short();\n    let open_key = reader.read_bytes_short();\n    T125 { open_id, open_key }\n}\n\npub fn read_t11a(mut data: Bytes) -> T11A {\n    let face = data.get_u16();\n    let age = data.get_u8();\n    let gender = data.get_u8();\n    let limit = data.get_u8() as usize;\n    let nick = data.read_string_limit(limit);\n    T11A {\n        face,\n        age,\n        gender,\n        nick,\n    }\n}\n\npub fn read_t199(mut data: Bytes) -> T199 {\n    let open_id = data.read_bytes_short();\n    let pay_token = data.read_bytes_short();\n    T199 { open_id, pay_token }\n}\n\npub fn read_t200(mut data: Bytes) -> T200 {\n    let pf = data.read_bytes_short();\n    let pf_key = data.read_bytes_short();\n    T200 { pf, pf_key }\n}\n\npub fn read_t512(mut reader: Bytes) -> T512 {\n    let length = reader.get_u16() as usize;\n\n    let mut ps_key_map: HashMap<String, Bytes> = HashMap::with_capacity(length);\n    let mut pt4_token_map: HashMap<String, Bytes> = HashMap::with_capacity(length);\n\n    for _ in 0..length {\n        let domain = reader.read_string_short();\n        let ps_key = reader.read_bytes_short();\n        let ps4_token = reader.read_bytes_short();\n\n        if !ps_key.is_empty() {\n            ps_key_map.insert(domain.clone(), ps_key);\n        }\n\n        if !ps4_token.is_empty() {\n            pt4_token_map.insert(domain, ps4_token);\n        }\n    }\n    T512 {\n        ps_key_map,\n        pt4_token_map,\n    }\n}\n\npub fn read_t531(mut data: Bytes) -> T531 {\n    let mut m = data.read_tlv_map(2);\n    let mut a1 = BytesMut::new();\n    let mut no_pic_sig = Bytes::new();\n    if [0x16a, 0x16a, 0x10c].iter().all(|v| m.contains_key(v)) {\n        a1.put_slice(&m.remove(&0x106).expect(\"0x106 not found\"));\n        a1.put_slice(&m.remove(&0x10c).expect(\"0x10c not found\"));\n        no_pic_sig = m.remove(&0x16a).expect(\"0x16a not found\");\n    }\n    T531 {\n        a1: a1.freeze(),\n        no_pic_sig,\n    }\n}\n\npub fn select(a: Option<&Bytes>, b: &[u8]) -> Bytes {\n    match a {\n        None => Bytes::from(b.to_owned()),\n        Some(a) => Bytes::from(a.to_vec()),\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/command/wtlogin/tlv_writer.rs",
    "content": "#![allow(clippy::too_many_arguments)]\n\nuse bytes::{BufMut, BytesMut};\nuse std::time::UNIX_EPOCH;\n\nuse crate::binary::packet_writer::{PacketWriter, WriteLV};\nuse crate::binary::BinaryWriter;\n\npub fn tlv<'a, B: BufMut + WriteLV, W: PacketWriter<B> + 'a>(\n    tag: u16,\n    body_writer: W,\n) -> impl PacketWriter<B> + 'a {\n    move |buf: &mut B| {\n        buf.put_u16(tag);\n        buf.write_short_lv(body_writer);\n    }\n}\n\npub fn t1<B: BufMut + WriteLV>(uin: u32, ip: &[u8]) -> impl PacketWriter<B> + '_ {\n    if ip.len() != 4 {\n        panic!(\"invalid ip\")\n    }\n    tlv(0x01, move |w: &mut B| {\n        w.put_u16(1);\n        w.put_u32(rand::random());\n        w.put_u32(uin);\n        w.put_u32(UNIX_EPOCH.elapsed().unwrap().as_secs() as u32);\n        w.put_slice(ip);\n        w.put_u16(0);\n    })\n}\n\npub fn t1b<B: BufMut + WriteLV>(\n    micro: u32,\n    version: u32,\n    size: u32,\n    margin: u32,\n    dpi: u32,\n    ec_level: u32,\n    hint: u32,\n) -> impl PacketWriter<B> {\n    tlv(0x1b, move |w: &mut B| {\n        w.put_u32(micro);\n        w.put_u32(version);\n        w.put_u32(size);\n        w.put_u32(margin);\n        w.put_u32(dpi);\n        w.put_u32(ec_level);\n        w.put_u32(hint);\n        w.put_u16(0);\n    })\n}\n\npub fn t1d<B: BufMut + WriteLV>(misc_bitmap: u32) -> impl PacketWriter<B> {\n    tlv(0x1d, move |w: &mut B| {\n        w.put_u8(1);\n        w.put_u32(misc_bitmap);\n        w.put_u32(0);\n        w.put_u8(0);\n        w.put_u32(0);\n    })\n}\n\npub fn t1f<'a, B: BufMut + WriteLV>(\n    is_root: bool,\n    os_name: &'a str,\n    os_version: &'a str,\n    sim_operator_name: &'a str,\n    apn: &'a str,\n    network_type: u16,\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x1f, move |w: &mut B| {\n        w.put_u8(if is_root { 1 } else { 0 });\n        w.write_bytes_short(os_name.as_bytes());\n        w.write_bytes_short(os_version.as_bytes());\n        w.put_u16(network_type);\n        w.write_bytes_short(sim_operator_name.as_bytes());\n        w.write_bytes_short(&[]);\n        w.write_bytes_short(apn.as_bytes());\n    })\n}\n\npub fn t2<B: BufMut + WriteLV>(result: String, sign: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x02, move |w: &mut B| {\n        w.put_u16(0);\n        w.write_bytes_short(result.as_bytes());\n        w.write_bytes_short(sign);\n    })\n}\n\npub fn t8<B: BufMut + WriteLV>(local_id: u32) -> impl PacketWriter<B> {\n    tlv(0x08, move |w: &mut B| {\n        w.put_u16(0);\n        w.put_u32(local_id);\n        w.put_u16(0);\n    })\n}\n\npub fn t10a<B: BufMut + WriteLV>(arr: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x10A, arr)\n}\n\npub fn t16<'a, B: BufMut + WriteLV>(\n    sso_version: u32,\n    app_id: u32,\n    sub_app_id: u32,\n    guid: &'a [u8],\n    apk_id: &'a str,\n    apk_version_name: &'a str,\n    apk_sign: &'a [u8],\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x16, move |w: &mut B| {\n        w.put_u32(sso_version);\n        w.put_u32(app_id);\n        w.put_u32(sub_app_id);\n        w.put_slice(guid);\n        w.write_bytes_short(apk_id.as_bytes());\n        w.write_bytes_short(apk_version_name.as_bytes());\n        w.write_bytes_short(apk_sign);\n    })\n}\n\npub fn t16a<B: BufMut + WriteLV>(arr: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x16A, arr)\n}\n\npub fn t16e<B: BufMut + WriteLV>(build_model: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x16E, build_model)\n}\n\npub fn t17a<B: BufMut + WriteLV>(value: i32) -> impl PacketWriter<B> {\n    tlv(0x17a, move |buf: &mut B| buf.put_u32(value as u32))\n}\n\npub fn t17c<B: BufMut + WriteLV>(code: &str) -> impl PacketWriter<B> + '_ {\n    tlv(0x17c, move |w: &mut B| {\n        w.write_short_lv(code.as_bytes());\n    })\n}\n\npub fn t18<B: BufMut + WriteLV>(app_id: u32, uin: u32) -> impl PacketWriter<B> {\n    tlv(0x18, move |w: &mut B| {\n        w.put_u16(1);\n        w.put_u32(1536);\n        w.put_u32(app_id);\n        w.put_u32(0);\n        w.put_u32(uin);\n        w.put_u16(0);\n        w.put_u16(0);\n    })\n}\n\npub fn t33<B: BufMut + WriteLV>(guid: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x33, guid)\n}\n\npub fn t35<B: BufMut + WriteLV>(product_type: u32) -> impl PacketWriter<B> {\n    tlv(0x35, move |buf: &mut B| buf.put_u32(product_type))\n}\n\npub fn t52d<B: BufMut + WriteLV>(dev_info: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x52d, dev_info)\n}\n\npub fn t100<B: BufMut + WriteLV>(\n    sso_version: u32,\n    protocol: u32,\n    main_sig_map: u32,\n) -> impl PacketWriter<B> {\n    tlv(0x100, move |w: &mut B| {\n        w.put_u16(1);\n        w.put_u32(sso_version);\n        w.put_u32(16);\n        w.put_u32(protocol);\n        w.put_u32(0); // App client version\n        w.put_u32(main_sig_map); // 34869472\n    })\n}\n\npub fn t104<B: BufMut + WriteLV>(data: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x104, data)\n}\n\npub fn t106<'a, B: BufMut + WriteLV>(\n    uin: u32,\n    salt: u32,\n    app_id: u32,\n    sso_ver: u32,\n    password_md5: &'a [u8],\n    guid_available: bool,\n    guid: &'a [u8],\n    tgtgt_key: &'a [u8],\n    wtf: u32,\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x106, move |w: &mut B| {\n        let key = md5::compute(&{\n            let mut v = BytesMut::new();\n            v.put_slice(password_md5);\n            v.put_slice(&[0; 4]);\n            v.put_u32(if salt != 0 { salt } else { uin });\n            v\n        })\n        .to_vec();\n        w.encrypt_and_write(&key, &{\n            let mut w = BytesMut::new();\n            w.put_u16(4);\n            w.put_u32(rand::random::<u32>());\n            w.put_u32(sso_ver);\n            w.put_u32(16); // appId\n            w.put_u32(0); // app client version\n            w.put_u64(if uin == 0 { salt as u64 } else { uin as u64 });\n            w.put_u32(UNIX_EPOCH.elapsed().unwrap().as_secs() as u32);\n            w.put_slice(&[0x00, 0x00, 0x00, 0x00]); // fake ip\n            w.put_u8(0x01);\n            w.put_slice(password_md5);\n            w.put_slice(tgtgt_key);\n            w.put_u32(wtf);\n            w.put_u8(if guid_available { 1 } else { 0 });\n            if guid.is_empty() {\n                for _ in 0..4 {\n                    w.put_u32(rand::random::<u32>());\n                }\n            } else {\n                w.put_slice(guid)\n            }\n            w.put_u32(app_id);\n            w.put_u32(1); // password login\n            w.write_short_lv((uin as i64).to_string().as_bytes());\n            w.put_u16(0);\n            w.freeze()\n        });\n    })\n}\n\npub fn t107<B: BufMut + WriteLV>(pic_type: u16) -> impl PacketWriter<B> {\n    tlv(0x107, move |w: &mut B| {\n        w.put_u16(pic_type);\n        w.put_u8(0x00);\n        w.put_u16(0);\n        w.put_u8(0x01);\n    })\n}\n\npub fn t108<B: BufMut + WriteLV>(ksid: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x108, ksid)\n}\n\npub fn t109<B: BufMut + WriteLV>(android_id: &str) -> impl PacketWriter<B> + '_ {\n    tlv(0x109, move |buf: &mut B| {\n        buf.put_slice(md5::compute(android_id.as_bytes()).as_ref());\n    })\n}\n\npub fn t112<B: BufMut + WriteLV>(uin: i64) -> impl PacketWriter<B> {\n    tlv(0x112, move |w: &mut B| {\n        w.put_slice(uin.to_string().as_bytes())\n    })\n}\n\npub fn t116<B: BufMut + WriteLV>(misc_bitmap: u32, sub_sig_map: u32) -> impl PacketWriter<B> {\n    tlv(0x116, move |w: &mut B| {\n        w.put_u8(0x00);\n        w.put_u32(misc_bitmap);\n        w.put_u32(sub_sig_map);\n        w.put_u8(0x01);\n        w.put_u32(1600000226); // app id list\n    })\n}\n\npub fn t124<'a, B: BufMut + WriteLV>(\n    os_type: &'a str,\n    os_version: &'a str,\n    sim_info: &'a str,\n    apn: &'a str,\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x124, move |w: &mut B| {\n        w.write_tlv_limited_size(os_type.as_bytes(), 16);\n        w.write_tlv_limited_size(os_version.as_bytes(), 16);\n        w.put_u16(2);\n        w.write_tlv_limited_size(sim_info.as_bytes(), 16);\n        w.write_tlv_limited_size(&[], 16);\n        w.write_tlv_limited_size(apn.as_bytes(), 16);\n    })\n}\n\npub fn t128<'a, B: BufMut + WriteLV>(\n    is_guid_from_file_null: bool,\n    is_guid_available: bool,\n    is_guid_changed: bool,\n    guid_flag: u32,\n    build_model: &'a str,\n    guid: &'a [u8],\n    build_brand: &'a str,\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x128, move |w: &mut B| {\n        w.put_u16(0);\n        w.put_u8(if is_guid_from_file_null { 1 } else { 0 });\n        w.put_u8(if is_guid_available { 1 } else { 0 });\n        w.put_u8(if is_guid_changed { 1 } else { 0 });\n        w.put_u32(guid_flag);\n        w.write_tlv_limited_size(build_model.as_bytes(), 32);\n        w.write_tlv_limited_size(guid, 16);\n        w.write_tlv_limited_size(build_brand.as_bytes(), 16); // app id list\n    })\n}\n\npub fn t141<'a, B: BufMut + WriteLV>(sim_info: &'a str, apn: &'a str) -> impl PacketWriter<B> + 'a {\n    tlv(0x141, move |w: &mut B| {\n        w.put_u16(1);\n        w.write_bytes_short(sim_info.as_bytes());\n        w.put_u16(2);\n        w.write_bytes_short(apn.as_bytes());\n    })\n}\n\npub fn t142<B: BufMut + WriteLV>(apk_id: &str) -> impl PacketWriter<B> + '_ {\n    tlv(0x142, move |w: &mut B| {\n        w.put_u16(0);\n        w.write_tlv_limited_size(apk_id.as_bytes(), 32);\n    })\n}\n\npub fn t143<B: BufMut + WriteLV>(arr: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x143, arr)\n}\n\npub fn t144<'a, B: BufMut + WriteLV>(\n    imei: &'a str,\n    dev_info: &'a [u8],\n    os_type: &'a str,\n    os_version: &'a str,\n    sim_info: &'a str,\n    apn: &'a str,\n    is_guid_from_file_null: bool,\n    is_guid_available: bool,\n    is_guid_changed: bool,\n    guid_flag: u32,\n    build_model: &'a str,\n    guid: &'a [u8],\n    build_brand: &'a str,\n    tgtgt_key: &'a [u8],\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x144, move |w: &mut B| {\n        w.encrypt_and_write(tgtgt_key, &{\n            let mut w = Vec::new();\n            w.put_u16(5);\n            t109(imei).write(&mut w);\n            t52d(dev_info).write(&mut w);\n            t124(os_type, os_version, sim_info, apn).write(&mut w);\n            t128(\n                is_guid_from_file_null,\n                is_guid_available,\n                is_guid_changed,\n                guid_flag,\n                build_model,\n                guid,\n                build_brand,\n            )\n            .write(&mut w);\n            t16e(build_model.as_bytes()).write(&mut w);\n            w\n        });\n    })\n}\n\npub fn t145<B: BufMut + WriteLV>(guid: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x145, guid)\n}\n\npub fn t147<'a, B: BufMut + WriteLV>(\n    app_id: u32,\n    apk_version_name: &'a str,\n    apk_signature_md5: &'a [u8],\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x147, move |w: &mut B| {\n        w.put_u32(app_id);\n        w.write_tlv_limited_size(apk_version_name.as_bytes(), 32);\n        w.write_tlv_limited_size(apk_signature_md5, 32);\n    })\n}\n\npub fn t154<B: BufMut + WriteLV>(seq: u16) -> impl PacketWriter<B> {\n    tlv(0x154, move |buf: &mut B| buf.put_u32(seq as u32))\n}\n\npub fn t166<B: BufMut + WriteLV>(image_type: u8) -> impl PacketWriter<B> {\n    tlv(0x166, move |buf: &mut B| buf.put_u8(image_type))\n}\n\npub fn t174<B: BufMut + WriteLV>(data: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x174, data)\n}\n\npub fn t177<B: BufMut + WriteLV>(build_time: u32, sdk_version: &str) -> impl PacketWriter<B> + '_ {\n    tlv(0x177, move |w: &mut B| {\n        w.put_u8(0x01);\n        w.put_u32(build_time);\n        w.write_short_lv(sdk_version.as_bytes());\n    })\n}\n\npub fn t187<B: BufMut + WriteLV>(mac_address: &str) -> impl PacketWriter<B> + '_ {\n    tlv(0x187, move |buf: &mut B| {\n        buf.put_slice(md5::compute(mac_address.as_bytes()).as_ref())\n    })\n}\n\npub fn t188<B: BufMut + WriteLV>(android_id: &str) -> impl PacketWriter<B> + '_ {\n    tlv(0x188, move |buf: &mut B| {\n        buf.put_slice(md5::compute(android_id.as_bytes()).as_ref())\n    })\n}\n\npub fn t191<B: BufMut + WriteLV>(k: u8) -> impl PacketWriter<B> {\n    tlv(0x191, move |buf: &mut B| buf.put_u8(k))\n}\n\npub fn t193<B: BufMut + WriteLV>(ticket: &str) -> impl PacketWriter<B> + '_ {\n    tlv(0x193, ticket.as_bytes())\n}\n\npub fn t194<B: BufMut + WriteLV>(imsi_md5: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x194, imsi_md5)\n}\n\npub fn t197<B: BufMut + WriteLV>() -> impl PacketWriter<B> {\n    tlv(0x197, [0u8].as_slice())\n}\n\npub fn t198<B: BufMut + WriteLV>() -> impl PacketWriter<B> {\n    tlv(0x198, [0u8].as_slice())\n}\n\npub fn t202<'a, B: BufMut + WriteLV>(\n    wifi_bssid: &'a str,\n    wifi_ssid: &'a str,\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x202, move |w: &mut B| {\n        w.write_tlv_limited_size(wifi_bssid.as_bytes(), 16);\n        w.write_tlv_limited_size(wifi_ssid.as_bytes(), 32);\n    })\n}\n\npub fn t318<B: BufMut + WriteLV>(tgt_qr: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x318, tgt_qr)\n}\n\npub fn t400<'a, B: BufMut + WriteLV>(\n    g: &'a [u8],\n    uin: i64,\n    guid: &'a [u8],\n    dpwd: &'a [u8],\n    j2: i64,\n    j3: i64,\n    rand_seed: &'a [u8],\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x400, move |w: &mut B| {\n        w.encrypt_and_write(g, &{\n            let mut ww = Vec::new();\n            ww.put_u16(1);\n            ww.put_u64(uin as u64);\n            ww.put_slice(guid);\n            ww.put_slice(dpwd);\n            ww.put_u32(j2 as u32);\n            ww.put_u32(j3 as u32);\n            ww.put_u32(UNIX_EPOCH.elapsed().unwrap().as_millis() as u32);\n            ww.put_slice(rand_seed);\n            ww\n        });\n    })\n}\n\npub fn t401<B: BufMut + WriteLV>(d: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x401, d)\n}\n\npub fn t511<B: BufMut + WriteLV>(domains: Vec<&str>) -> impl PacketWriter<B> + '_ {\n    tlv(0x511, move |w: &mut B| {\n        let mut arr2 = Vec::new();\n        for d in domains {\n            if !d.is_empty() {\n                arr2.push(d)\n            }\n        }\n        w.put_u16(arr2.len() as u16);\n        for d in arr2 {\n            let index_of = match d.find('(') {\n                None => -1,\n                Some(i) => i as isize,\n            };\n            let index_of2 = match d.find(')') {\n                None => -1,\n                Some(i) => i as isize,\n            };\n            if index_of != 0 || index_of2 <= 0 {\n                w.put_u8(0x01);\n                w.write_short_lv(d.as_bytes())\n            } else {\n                let mut b: u8;\n                let z: bool;\n                if let Ok(i) = d[(index_of + 1) as usize..index_of2 as usize].parse::<i32>() {\n                    let z2 = (1048576 & i) > 0;\n                    z = (i & 134217728) > 0;\n                    if z2 {\n                        b = 1\n                    } else {\n                        b = 0\n                    }\n                    if z {\n                        b |= 2\n                    }\n                    w.put_u8(b);\n                    w.write_short_lv(d[(index_of2 + 1) as usize..].as_bytes());\n                }\n            }\n        }\n    })\n}\n\npub fn t516<B: BufMut + WriteLV>() -> impl PacketWriter<B> {\n    tlv(0x516, |buf: &mut B| buf.put_u32(0))\n}\n\npub fn t521<B: BufMut + WriteLV>(i: u32) -> impl PacketWriter<B> {\n    tlv(0x521, move |w: &mut B| {\n        w.put_u32(i);\n        w.put_u16(0);\n    })\n}\n\npub fn t525<'a, B: BufMut + WriteLV, T: PacketWriter<B> + 'a>(\n    t536: T,\n) -> impl PacketWriter<B> + 'a {\n    tlv(0x525, move |w: &mut B| {\n        w.put_u16(1);\n        t536.write(w);\n    })\n}\n\npub fn t536<B: BufMut + WriteLV>(login_extra_data: &[u8]) -> impl PacketWriter<B> + '_ {\n    tlv(0x526, login_extra_data)\n}\n\npub fn guid_flag() -> u32 {\n    let mut flag: u32 = 0;\n    flag |= 1 << 24 & 0xFF000000;\n    flag |= 0; // flag |= 0 << 8 & 0xFF00;\n    flag\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::command::wtlogin::tlv_writer::*;\n\n    const GUID: [u8; 16] = [\n        142, 27, 163, 177, 172, 31, 181, 137, 118, 115, 8, 126, 24, 49, 54, 169,\n    ];\n    const TGTGT_KEY: [u8; 16] = [\n        199, 12, 183, 107, 3, 28, 81, 148, 116, 20, 229, 112, 0, 64, 152, 255,\n    ];\n    const UIN: u32 = 349195854;\n    const OS_NAME: &str = \"android\";\n    const OS_VERSION: &str = \"7.1.2\";\n    const SIM_INFO: &str = \"T-Mobile\";\n    const IMEI: &str = \"468356291846738\";\n    const IMEI_MD5: &[u8] = \"9792b1bba1867318bf782af418306ef8\".as_bytes();\n    const WIFI_BSSID: &str = \"00:50:56:C0:00:08\";\n    const WIFI_SSID: &str = \"<unknown ssid>\";\n    const APN: &str = \"wifi\";\n    const APK_SIGN: [u8; 16] = [\n        0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6,\n        0x8D,\n    ];\n    const APK_ID: &str = \"com.tencent.mobileqq\";\n    const APP_ID: u32 = 537066738;\n    const SUB_APP_ID: u32 = 537066738;\n    const SSO_VERSION: u32 = 15;\n    const SDK_VERSION: &str = \"6.0.0.2454\";\n    const MISC_BITMAP: u32 = 184024956;\n    const SUB_SIG_MAP: u32 = 0x10400;\n    const MAIN_SIG_MAP: u32 = 34869472;\n    const MAC_ADDRESS: &str = \"00:50:56:C0:00:08\";\n    const IS_ROOT: bool = false;\n    const ANDROID_ID: &str = \"QKQ1.191117.002\";\n    const APK_VERSION_NAME: &str = \"2.0.5\";\n    const DEV_INFO: &[u8] = \"dev_info_dev_info_dev_info_dev_info_dev_info_\".as_bytes();\n    const BUILD_MODEL: &str = \"mirai\";\n    const BUILD_BRAND: &str = \"mamoe\";\n    const OS_TYPE: &str = \"android\";\n\n    #[test]\n    fn test_param() {\n        println!(\"{GUID:?}\");\n        println!(\"{:?}\", \"test param\");\n    }\n    fn get_buf<W: PacketWriter<Vec<u8>>>(w: W) -> Vec<u8> {\n        let mut buf = vec![];\n        w.write(&mut buf);\n        buf\n    }\n    #[test]\n    fn test_t1() {\n        let result = t1(UIN, &[192, 168, 1, 1]);\n        let result = get_buf(result);\n        println!(\"{:?}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t1b() {\n        let result = t1b(0, 0, 3, 4, 72, 2, 2);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t1d() {\n        let result = t1d(MISC_BITMAP);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t1f() {\n        let result = t1f(IS_ROOT, OS_NAME, OS_VERSION, \"China Mobile GSM\", APN, 2);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t2() {\n        let result = t2(\"result\".to_string(), \"sign\".as_ref());\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t8() {\n        let result = t8(123456);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t10a() {\n        let result = t10a(IMEI.as_bytes());\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t16() {\n        let result = t16(\n            SSO_VERSION,\n            APP_ID,\n            SUB_APP_ID,\n            &GUID,\n            APK_ID,\n            APK_VERSION_NAME,\n            &APK_SIGN,\n        );\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t16a() {\n        let result = t16a(IMEI.as_bytes());\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t16e() {\n        let result = t16e(IMEI.as_bytes());\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t17a() {\n        let result = t17a(UIN as i32);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t17c() {\n        let result = t17c(IMEI);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t18() {\n        let result = t18(APP_ID, UIN);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t33() {\n        let result = t33(&GUID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t35() {\n        let product_type = 8;\n        let result = t35(product_type);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t52d() {\n        let result = t52d(DEV_INFO);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t100() {\n        let result = t100(SSO_VERSION, 2, MAIN_SIG_MAP);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t104() {\n        let result = t104(&GUID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t106() {\n        let result = t106(\n            UIN,\n            0,\n            APP_ID,\n            SSO_VERSION,\n            &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            true,\n            &GUID,\n            &TGTGT_KEY,\n            0,\n        );\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\")\n    }\n\n    #[test]\n    fn test_t107() {\n        let result = t107(3);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t108() {\n        let result = t108(IMEI.as_bytes());\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t109() {\n        let result = t109(ANDROID_ID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t116() {\n        let result = t116(MAIN_SIG_MAP, SUB_SIG_MAP);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t124() {\n        let result = t124(OS_TYPE, OS_VERSION, SIM_INFO, APN);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t128() {\n        let result = t128(false, true, false, 16, BUILD_MODEL, &GUID, BUILD_BRAND);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t141() {\n        let result = t141(SIM_INFO, APN);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t142() {\n        let result = t142(APK_ID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t143() {\n        let result = t143(&[1, 2, 3]);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t144() {\n        let result = t144(\n            IMEI,\n            DEV_INFO,\n            OS_TYPE,\n            OS_VERSION,\n            SIM_INFO,\n            APN,\n            false,\n            true,\n            false,\n            16,\n            BUILD_MODEL,\n            &GUID,\n            BUILD_BRAND,\n            &TGTGT_KEY,\n        );\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t145() {\n        let result = t145(&GUID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t147() {\n        let result = t147(16, APK_VERSION_NAME, &APK_SIGN);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t154() {\n        let seq = (0x3635 + 1) & 0x7FFF;\n        println!(\"{seq}\");\n        let result = t154(seq);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t166() {\n        let result = t166(1);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t174() {\n        let result = t174(&GUID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t177() {\n        let result = t177(MISC_BITMAP, SDK_VERSION);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t187() {\n        let result = t187(MAC_ADDRESS);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t188() {\n        let result = t188(ANDROID_ID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t191() {\n        let result = t191(127_u8);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t193() {\n        let result = t193(\"some ticket\");\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t194() {\n        let result = t194(IMEI_MD5);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t197() {\n        let result = t197();\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t198() {\n        let result = t198();\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t202() {\n        let result = t202(WIFI_BSSID, WIFI_SSID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t400() {\n        let result = t400(&GUID, UIN as i64, &GUID, &GUID, 2, 2, &GUID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t401() {\n        let result = t401(&GUID);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t511() {\n        let result = t511(vec![\n            \"tenpay.com\",\n            \"openmobile.qq.com\",\n            \"docs.qq.com\",\n            \"connect.qq.com\",\n            \"qzone.qq.com\",\n            \"vip.qq.com\",\n            \"gamecenter.qq.com\",\n            \"qun.qq.com\",\n            \"game.qq.com\",\n            \"qqweb.qq.com\",\n            \"office.qq.com\",\n            \"ti.qq.com\",\n            \"mail.qq.com\",\n            \"mma.qq.com\",\n        ]);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t516() {\n        let result = t516();\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t521() {\n        let result = t521(6);\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_t525() {\n        let result = t525(t536(&GUID));\n        let result = get_buf(result);\n        println!(\"{}\", result.len());\n        println!(\"{result:?}\");\n    }\n\n    #[test]\n    fn test_tlv() {\n        let result = guid_flag();\n        println!(\"{result:?}\");\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/common.rs",
    "content": "use std::net::{IpAddr, Ipv4Addr, SocketAddr};\n\npub fn group_code2uin(code: i64) -> i64 {\n    let mut left = code / 1000000;\n    if (0..=10).contains(&left) {\n        left += 202\n    } else if (11..=19).contains(&left) {\n        left += 469\n    } else if (20..=66).contains(&left) {\n        left += 2080\n    } else if (67..=156).contains(&left) {\n        left += 1943\n    } else if (157..=209).contains(&left) {\n        left += 1990\n    } else if (210..=309).contains(&left) {\n        left += 3890\n    } else if (310..=335).contains(&left) {\n        left += 3490\n    } else if (336..=386).contains(&left) {\n        //335 336不确定\n        left += 2265\n    } else if (387..=499).contains(&left) {\n        left += 3490\n    }\n    left * 1000000 + code % 1000000\n}\n\npub fn group_uin2code(uin: i64) -> i64 {\n    let mut left = uin / 1000000;\n    if (202..=212).contains(&left) {\n        left -= 202\n    } else if (480..=488).contains(&left) {\n        left -= 469\n    } else if (2100..=2146).contains(&left) {\n        left -= 2080\n    } else if (2010..=2099).contains(&left) {\n        left -= 1943\n    } else if (2147..=2199).contains(&left) {\n        left -= 1990\n    } else if (2600..=2651).contains(&left) {\n        left -= 2265\n    } else if (3800..=3989).contains(&left) {\n        left -= 3490\n    } else if (4100..=4199).contains(&left) {\n        left -= 3890\n    }\n    left * 1000000 + uin % 1000000\n}\n\n#[derive(Debug, Clone, Copy, Default)]\npub struct RQAddr(pub u32, pub u16);\n\nimpl From<RQAddr> for SocketAddr {\n    fn from(addr: RQAddr) -> Self {\n        let mut ip: [u8; 4] = addr.0.to_be_bytes();\n        ip.reverse();\n        SocketAddr::new(Ipv4Addr::from(ip).into(), addr.1)\n    }\n}\n\nimpl From<SocketAddr> for RQAddr {\n    fn from(addr: SocketAddr) -> Self {\n        let IpAddr::V4(ip) = addr.ip() else { panic!(\"is not ipv4\") };\n        // ip.octets() returns little-endian\n        Self(u32::from_le_bytes(ip.octets()), addr.port())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_group_code2uin() {\n        let uin = group_code2uin(335783090);\n        assert_eq!(uin, 3825783090);\n    }\n    #[test]\n    fn test_group_uin2code() {\n        let code = group_uin2code(3825783090);\n        assert_eq!(code, 335783090);\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/crypto/encrypt.rs",
    "content": "use bytes::{BufMut, Bytes};\n// use openssl::bn::{BigNum, BigNumContext};\n// use openssl::ec::{EcGroup, EcPoint, EcKey, PointConversionForm};\n// use openssl::nid::Nid;\nuse super::qqtea_encrypt;\nuse crate::binary::BinaryWriter;\nuse crate::hex::decode_hex;\nuse p256::{ecdh::EphemeralSecret, EncodedPoint, PublicKey};\n\npub trait IEncryptMethod {\n    fn id(&self) -> u8;\n    fn do_encrypt(&self, data: &[u8], key: &[u8]) -> Vec<u8>;\n}\n\n#[derive(Debug)]\npub struct EncryptECDH {\n    pub initial_share_key: Bytes,\n    pub public_key: Bytes,\n    pub public_key_ver: u16,\n}\n\nimpl Default for EncryptECDH {\n    fn default() -> Self {\n        let mut ecdh = EncryptECDH {\n            initial_share_key: Bytes::new(),\n            public_key: Bytes::new(),\n            public_key_ver: 1,\n        };\n        ecdh.generate_key(\"04EBCA94D733E399B2DB96EACDD3F69A8BB0F74224E2B44E3357812211D2E62EFBC91BB553098E25E33A799ADC7F76FEB208DA7C6522CDB0719A305180CC54A82E\");\n        ecdh\n    }\n}\n\nimpl EncryptECDH {\n    pub fn generate_key(&mut self, s_pub_key: &str) {\n        let s_pub_key = decode_hex(s_pub_key).expect(\"failed to decode ecdh hex\"); // decode pub key\n        let secret = EphemeralSecret::random(rand::thread_rng()); // gen private key\n        let pub_key = PublicKey::from_sec1_bytes(&s_pub_key).expect(\"failed to get s_pub_key\"); // gen public key\n\n        let share = secret.diffie_hellman(&pub_key); // count public share\n        let share_x = &share.as_bytes()[0..16];\n        self.initial_share_key = Bytes::copy_from_slice(&md5::compute(share_x).0);\n\n        let self_public_key = secret.public_key();\n        let point = EncodedPoint::from(self_public_key);\n        self.public_key = Bytes::copy_from_slice(point.as_bytes());\n    }\n}\n\nimpl IEncryptMethod for EncryptECDH {\n    fn id(&self) -> u8 {\n        0x87\n    }\n\n    fn do_encrypt(&self, data: &[u8], key: &[u8]) -> Vec<u8> {\n        let mut w = Vec::new();\n        w.put_u8(0x02);\n        w.put_u8(0x01);\n        w.put_slice(key);\n        w.put_u16(0x01_31);\n        w.put_u16(self.public_key_ver);\n        w.put_u16(self.public_key.len() as u16);\n        w.put_slice(&self.public_key);\n        w.encrypt_and_write(&self.initial_share_key, data);\n        w\n    }\n}\n\npub struct EncryptSession {\n    t133: Vec<u8>,\n}\n\nimpl EncryptSession {\n    pub fn new(t133: &[u8]) -> EncryptSession {\n        EncryptSession {\n            t133: t133.to_vec(),\n        }\n    }\n}\n\nimpl IEncryptMethod for EncryptSession {\n    fn id(&self) -> u8 {\n        69\n    }\n\n    fn do_encrypt(&self, data: &[u8], key: &[u8]) -> Vec<u8> {\n        let encrypt = qqtea_encrypt(data, key);\n        let mut w = Vec::new();\n        w.put_u16(self.t133.len() as u16);\n        w.put_slice(&self.t133);\n        w.put_slice(&encrypt);\n        w\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::crypto::EncryptECDH;\n\n    #[test]\n    fn test_ecdh_generate_key() {\n        let mut e = EncryptECDH::default();\n        e.generate_key(\"04EBCA94D733E399B2DB96EACDD3F69A8BB0F74224E2B44E3357812211D2E62EFBC91BB553098E25E33A799ADC7F76FEB208DA7C6522CDB0719A305180CC54A82E\");\n        println!(\"{:?}\", e.initial_share_key);\n        println!(\"{:?}\", e.public_key);\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/crypto/mod.rs",
    "content": "mod encrypt;\nmod qqtea;\n\npub use self::encrypt::{EncryptECDH, EncryptSession, IEncryptMethod};\npub use self::qqtea::{qqtea_decrypt, qqtea_encrypt};\n"
  },
  {
    "path": "ricq-core/src/crypto/qqtea.rs",
    "content": "// copy from https://github.com/zkonge/rtea\nuse byteorder::{BigEndian, ByteOrder};\nuse rand::{thread_rng, RngCore};\n\nuse tea::{GenericArray, Tea16};\n\npub fn qqtea_encrypt(text: &[u8], key: &[u8]) -> Vec<u8> {\n    let fill_count = 9 - (text.len() + 1) % 8;\n\n    let mut plaintext = vec![0u8; 1 + fill_count + text.len() + 7];\n    let plaintext_len = plaintext.len();\n\n    plaintext[0] = (fill_count as u8 - 2) | 0xF8;\n    if cfg!(debug_assertions) {\n        //这里是为了和pytea对拍，填充220\n        plaintext[1..fill_count + 1].fill(220);\n    } else {\n        thread_rng().fill_bytes(&mut plaintext[1..fill_count + 1]);\n    }\n    plaintext[fill_count + 1..plaintext_len - 7].copy_from_slice(text);\n\n    let mut work_block: Vec<u64> = vec![0; plaintext.len() / 8];\n\n    BigEndian::read_u64_into(&plaintext, &mut work_block);\n\n    let mut iv1 = 0u64;\n    let mut iv2 = 0u64;\n    let mut holder: u64;\n\n    let cipher = Tea16::new(GenericArray::from_slice(key));\n\n    for block in work_block.iter_mut() {\n        holder = *block ^ iv1;\n\n        iv1 = cipher.encrypt(holder);\n\n        iv1 ^= iv2;\n\n        iv2 = holder;\n\n        *block = iv1;\n    }\n\n    BigEndian::write_u64_into(&work_block, &mut plaintext);\n\n    plaintext\n}\n\npub fn qqtea_decrypt(text: &[u8], key: &[u8]) -> Vec<u8> {\n    let mut work_block: Vec<u64> = vec![0; text.len() / 8];\n\n    BigEndian::read_u64_into(text, &mut work_block);\n\n    let mut iv1 = 0u64;\n    let mut iv2 = 0u64;\n    let mut holder: u64;\n    let mut tmp_block: u64;\n\n    let cipher = Tea16::new(GenericArray::from_slice(key));\n\n    for block in work_block.iter_mut() {\n        tmp_block = *block ^ iv2;\n\n        tmp_block = cipher.decrypt(tmp_block);\n\n        iv2 = tmp_block;\n\n        holder = tmp_block ^ iv1;\n\n        iv1 = *block;\n\n        *block = holder;\n    }\n\n    let mut result = vec![0u8; text.len()];\n\n    BigEndian::write_u64_into(&work_block, &mut result);\n\n    let begin_pos = ((result[0] as usize) & 7) + 3;\n    let end_pos = result.len() - 7;\n\n    result[begin_pos..end_pos].to_owned()\n}\n\nmod tea {\n    use byteorder::{BigEndian, ByteOrder};\n    pub use generic_array::{\n        typenum::{U16, U8},\n        GenericArray,\n    };\n\n    const TEA_DELTA: u32 = 0x9E3779B9;\n\n    pub struct Tea16 {\n        key: [u32; 4],\n    }\n\n    impl Tea16 {\n        #[inline]\n        pub fn encrypt(&self, n: u64) -> u64 {\n            let mut sum: u32 = 0;\n            let (mut x, mut y) = ((n >> 32) as u32, n as u32);\n            let k0 = self.key[0];\n            let k1 = self.key[1];\n            let k2 = self.key[2];\n            let k3 = self.key[3];\n\n            for _ in 0..16 {\n                sum = sum.wrapping_add(TEA_DELTA);\n                x = x.wrapping_add(\n                    k0.wrapping_add(y << 4) ^ y.wrapping_add(sum) ^ k1.wrapping_add(y >> 5),\n                );\n                y = y.wrapping_add(\n                    k2.wrapping_add(x << 4) ^ x.wrapping_add(sum) ^ k3.wrapping_add(x >> 5),\n                );\n            }\n\n            ((x as u64) << 32) | y as u64\n        }\n\n        #[inline]\n        pub fn decrypt(&self, n: u64) -> u64 {\n            let mut sum: u32 = TEA_DELTA << 4;\n            let (mut x, mut y) = ((n >> 32) as u32, n as u32);\n            let k0 = self.key[0];\n            let k1 = self.key[1];\n            let k2 = self.key[2];\n            let k3 = self.key[3];\n\n            for _ in 0..16 {\n                y = y.wrapping_sub(\n                    k2.wrapping_add(x << 4) ^ x.wrapping_add(sum) ^ k3.wrapping_add(x >> 5),\n                );\n                x = x.wrapping_sub(\n                    k0.wrapping_add(y << 4) ^ y.wrapping_add(sum) ^ k1.wrapping_add(y >> 5),\n                );\n                sum = sum.wrapping_sub(TEA_DELTA);\n            }\n\n            ((x as u64) << 32) | y as u64\n        }\n\n        #[inline]\n        pub fn new(key: &GenericArray<u8, U16>) -> Self {\n            Self {\n                key: [\n                    BigEndian::read_u32(&key[0..4]),\n                    BigEndian::read_u32(&key[4..8]),\n                    BigEndian::read_u32(&key[8..12]),\n                    BigEndian::read_u32(&key[12..16]),\n                ],\n            }\n        }\n    }\n\n    #[allow(dead_code)]\n    pub fn tea16_encrypt(text: &mut [u8], key: &[u8]) {\n        let key: &GenericArray<u8, U16> = GenericArray::from_slice(key);\n\n        let mut n = BigEndian::read_u64(text);\n\n        n = Tea16::new(key).encrypt(n);\n\n        BigEndian::write_u64(text, n);\n    }\n\n    #[allow(dead_code)]\n    pub fn tea16_decrypt(text: &mut [u8], key: &[u8]) {\n        let key: &GenericArray<u8, U16> = GenericArray::from_slice(key);\n\n        let mut n = BigEndian::read_u64(text);\n\n        n = Tea16::new(key).decrypt(n);\n\n        BigEndian::write_u64(text, n);\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/error.rs",
    "content": "use std::io;\n\nuse thiserror::Error;\n\npub type RQResult<T> = Result<T, RQError>;\n\n#[derive(Error, Debug)]\npub enum RQError {\n    #[error(\"other error {0}\")]\n    Other(String),\n\n    #[error(\"failed to decode, {0}\")]\n    Decode(String),\n\n    #[error(\"failed to decode_prost, {0}\")]\n    PbDecode(#[from] prost::DecodeError),\n\n    #[error(\"empty field, {0}\")]\n    EmptyField(&'static str),\n\n    #[error(\"From utf-8 error {0}\")]\n    Utf8(#[from] std::string::FromUtf8Error),\n\n    #[error(\"command_name mismatch, expected {0} get {1}\")]\n    CommandNameMismatch(String, String),\n\n    #[error(\"timeout error\")]\n    Timeout,\n\n    #[error(\"network error\")]\n    Network,\n\n    #[error(\"jce error, {0}\")]\n    Jce(#[from] jcers::JceError),\n    #[error(\"io error, {0}\")]\n    IO(#[from] io::Error),\n\n    #[error(\"unknown flag {0}\")]\n    UnknownFlag(u8),\n\n    #[error(\"unknown encrypt type\")]\n    UnknownEncryptType,\n\n    #[error(\"invalid packet type\")]\n    InvalidPacketType,\n    #[error(\"invalid encrypt type\")]\n    InvalidEncryptType,\n    #[error(\"packet dropped\")]\n    PacketDropped,\n    #[error(\"session expired\")]\n    SessionExpired,\n    #[error(\"unsuccessful ret code: {0}\")]\n    UnsuccessfulRetCode(i32),\n\n    #[error(\"Token login failed\")]\n    TokenLoginFailed,\n    #[error(\"failed to get file count\")]\n    GetFileCountFailed,\n    #[error(\"failed to get file list: {0}\")]\n    GetFileListFailed(String),\n    #[error(\"crypto invalid length: {0}\")]\n    CryptoInvalidLength(#[from] crypto_common::InvalidLength),\n    #[error(\"serde_json error: {0}\")]\n    Serde(#[from] serde_json::Error),\n    #[error(\"block unpad error: {0}\")]\n    BlockUnpad(#[from] block_padding::UnpadError),\n    #[error(\"spki error: {0}\")]\n    Spki(#[from] spki::Error),\n    #[error(\"qimei error code: {0}\")]\n    QimeiError(i64),\n    #[error(\"base64 decode error: {0}\")]\n    Base64Decode(#[from] base64::DecodeError),\n    #[error(\"rsa error: {0}\")]\n    RSA(#[from] rsa::Error),\n}\n"
  },
  {
    "path": "ricq-core/src/hex.rs",
    "content": "use std::fmt::Write;\nuse std::num::ParseIntError;\n\npub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {\n    (0..s.len())\n        .step_by(2)\n        .map(|i| u8::from_str_radix(&s[i..i + 2], 16))\n        .collect()\n}\n\npub fn encode_hex(bytes: &[u8]) -> String {\n    let mut s = String::with_capacity(bytes.len() * 2);\n    for &b in bytes {\n        write!(s, \"{b:02x}\").expect(\"failed to encode_hex\");\n    }\n    s\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_decode() {\n        let h = decode_hex(\"04EBCA94D733E399B2DB96EACDD3F69A8BB0F74224E2B44E3357812211D2E62EFBC91BB553098E25E33A799ADC7F76FEB208DA7C6522CDB0719A305180CC54A82E\");\n        println!(\"{h:?}\")\n    }\n\n    #[test]\n    fn test_encode() {\n        let h = encode_hex(&[1, 2, 3]);\n        println!(\"{h}\")\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/highway/mod.rs",
    "content": "use std::net::IpAddr;\nuse std::sync::atomic::{AtomicI32, Ordering};\n\nuse bytes::Bytes;\nuse prost::Message;\n\nuse crate::command::common::PbToBytes;\nuse crate::{pb, RQResult};\n\n#[derive(Default)]\npub struct Session {\n    pub uin: i64,\n    pub app_id: i32,\n    pub sig_session: Bytes,\n    pub session_key: Bytes,\n    pub sso_addr: Vec<IpAddr>,\n    pub seq: AtomicI32,\n}\n\n#[derive(Default, Debug, Clone)]\npub struct BdhInput {\n    // 1-friend, 2-group, 299-groupPtt\n    pub command_id: i32,\n    pub ticket: Vec<u8>,\n    pub ext: Vec<u8>,\n    pub encrypt: bool,\n    pub chunk_size: usize,\n    pub send_echo: bool,\n}\n\nimpl Session {\n    fn next_seq(&self) -> i32 {\n        self.seq.fetch_add(2, Ordering::Relaxed)\n    }\n\n    pub fn build_basehead(\n        &self,\n        command: String,\n        dataflag: i32,\n        command_id: i32,\n        locale_id: i32,\n    ) -> pb::DataHighwayHead {\n        pb::DataHighwayHead {\n            version: 1,\n            uin: self.uin.to_string(),\n            command,\n            seq: self.next_seq(),\n            appid: self.app_id,\n            dataflag,\n            command_id,\n            locale_id,\n            ..Default::default()\n        }\n    }\n\n    pub fn build_seghead(\n        &self,\n        filesize: i64,\n        dataoffset: i64,\n        chunk: &[u8],\n        ticket: Vec<u8>,\n        file_md5: Vec<u8>,\n    ) -> pb::SegHead {\n        pb::SegHead {\n            filesize,\n            dataoffset,\n            datalength: chunk.len() as i32,\n            serviceticket: ticket,\n            md5: md5::compute(chunk).to_vec(),\n            file_md5,\n            ..Default::default()\n        }\n    }\n\n    pub fn build_bdh_head(\n        &self,\n        command_id: i32,\n        filesize: i64,\n        chunk: &[u8],\n        dataoffset: i64,\n        ticket: Vec<u8>,\n        file_md5: Vec<u8>,\n    ) -> Bytes {\n        pb::ReqDataHighwayHead {\n            msg_basehead: Some(self.build_basehead(\"PicUp.DataUp\".into(), 4096, command_id, 2052)),\n            msg_seghead: Some(pb::SegHead {\n                filesize,\n                dataoffset,\n                datalength: chunk.len() as i32,\n                serviceticket: ticket,\n                md5: md5::compute(chunk).to_vec(),\n                file_md5,\n                ..Default::default()\n            }),\n            ..Default::default()\n        }\n        .to_bytes()\n    }\n\n    pub fn decode_rsp_head(&self, payload: Bytes) -> RQResult<pb::RspDataHighwayHead> {\n        pb::RspDataHighwayHead::decode(&*payload).map_err(Into::into)\n    }\n\n    pub fn build_heartbreak(&self) -> Bytes {\n        pb::ReqDataHighwayHead {\n            msg_basehead: Some(self.build_basehead(\"PicUp.Echo\".into(), 4096, 0, 2052)),\n            ..Default::default()\n        }\n        .to_bytes()\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/jce/mod.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::Bytes;\nuse jcers::{JceGet, JcePut};\n\nmacro_rules! JceStruct {\n    ($struct_name: ident {$($tag: expr => $field: ident: $field_t: ty,)*}) => {\n        #[derive(Debug, Clone, PartialEq, Eq, JceGet, JcePut, Default)]\n        pub struct $struct_name {\n            $(#[jce($tag)]\n            pub $field: $field_t),*\n        }\n    };\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct RequestPacket {\n    #[jce(1)]\n    pub i_version: i16,\n    #[jce(2)]\n    pub c_packet_type: u8,\n    #[jce(3)]\n    pub i_message_type: i32,\n    #[jce(4)]\n    pub i_request_id: i32,\n    #[jce(5)]\n    pub s_servant_name: String,\n    #[jce(6)]\n    pub s_func_name: String,\n    #[jce(7)]\n    pub s_buffer: Bytes,\n    #[jce(8)]\n    pub i_timeout: i32,\n    #[jce(9)]\n    pub context: HashMap<String, String>,\n    #[jce(10)]\n    pub status: HashMap<String, String>,\n}\n\nJceStruct!(RequestDataVersion3 {\n    0 => map: HashMap<String,Bytes>,\n});\n\nJceStruct!(RequestDataVersion2 {\n    0 => map: HashMap<String,HashMap<String,Bytes>>,\n});\n\nJceStruct!(HttpServerListRes {\n    2 => sso_server_infos: Vec<SsoServerInfo>,\n});\n\nJceStruct!(SsoServerInfo {\n    1 => server: String,\n    2 => port: i32,\n    8 => location: String,\n});\n\nJceStruct!(FileStoragePushFSSvcList {\n    0  => upload_list: Vec<FileStorageServerInfo>,\n    1  => pic_download_list: Vec<FileStorageServerInfo>,\n    2  => g_pic_download_list: Vec<FileStorageServerInfo>,\n    3  => q_zone_proxy_service_list: Vec<FileStorageServerInfo>,\n    4  => url_encode_service_list: Vec<FileStorageServerInfo>,\n    5  => big_data_channel: BigDataChannel,\n    6  => vip_emotion_list: Vec<FileStorageServerInfo>,\n    7  => c2c_pic_down_list: Vec<FileStorageServerInfo>,\n    // 8  => fmt_ip_info: FmtIpInfo,\n    // 9  => domain_ip_channel: DomainIpChannel,\n    10 => ptt_list: Bytes,\n});\n\nJceStruct!(FileStorageServerInfo {\n    1 => server: String,\n    2 => port: i32,\n});\n\nJceStruct!(BigDataChannel {\n    0 => ip_lists: Vec<BigDataIPList>,\n    1 => sig_session: Bytes,\n    2 => key_session: Bytes,\n    3 => sig_uin: i64,\n    4 => connect_flag: i32,\n    5 => pb_buf: Bytes,\n});\n\nJceStruct!(BigDataIPList {\n    0 => service_type: i64,\n    1 => ip_list: Vec<BigDataIPInfo>,\n    3 => fragment_size: i64,\n});\n\nJceStruct!(BigDataIPInfo {\n    0 => r#type: i64,\n    1 => server: String,\n    2 => port: i64,\n});\n\nJceStruct!(SvcReqPullGroupMsgSeq {\n    0 => group_info: Vec<PullGroupSeqParam>,\n    1 => verify_type: u8,\n    2 => filter: i32,\n});\n\nJceStruct!(SvcReqRegister {\n    0  => uin: i64,\n    1  => bid: i64,\n    2  => conn_type: u8,\n    3  => other: String,\n    4  => status: i32,\n    5  => online_push: u8,\n    6  => is_online: u8,\n    7  => is_show_online: u8,\n    8  => kick_pc: u8,\n    9  => kick_weak: u8,\n    10 => timestamp: i64,\n    11 => ios_version: i64,\n    12 => net_type: u8,\n    13 => build_ver: String,\n    14 => reg_type: u8,\n    15 => dev_param: Bytes ,\n    16 => guid: Bytes,\n    17 => locale_id: i32,\n    18 => silent_push: u8,\n    19 => dev_name: String,\n    20 => dev_type: String,\n    21 => os_ver: String,\n    22 => open_push: u8,\n    23 => large_seq: i64,\n    24 => last_watch_start_time: i64,\n    26 => old_sso_ip: i64,\n    27 => new_sso_ip: i64,\n    28 => channel_no: String,\n    29 => cpid: i64,\n    30 => vendor_name: String,\n    31 => vendor_os_name: String,\n    32 => ios_idfa: String,\n    33 => b769: Bytes,\n    34 => is_set_status: u8,\n    35 => server_buf: Bytes,\n    36 => set_mute: u8,\n    38 => ext_online_status: i64,\n    39 => battery_status: i32,\n    40 => tim_active_flag:u8,\n    41 => bind_uin_notify_switch:u8,\n    // 42 => stVendorPushInfo:struct,\n    43 => vendor_dev_id:i64,\n    45 => custom_status: Bytes, // 自定义状态 protobuf\n});\n\nJceStruct!(SvcRespRegister {\n    0  => uin: i64,\n    1  => bid: i64,\n    2  => reply_code: u8,\n    3  => result: String,\n    4  => server_time: i64,\n    5  => log_qq: u8,\n    6  => need_kik: u8,\n    7  => update_flag: u8,\n    8  => timestamp: i64,\n    9  => crash_flag: u8,\n    10 => client_ip: String,\n    11 => client_port: i32,\n    12 => hello_interval: i32,\n    13 => large_seq: i32,\n    14 => large_seq_update: u8,\n    15 => d769_rsp_body: Bytes,\n    16 => status: i32,\n    17 => ext_online_status: i64,\n    18 => client_battery_get_interval: i64,\n    19 => client_auto_status_interval: i64,\n});\n\nJceStruct!(SvcReqRegisterNew {\n    0  => request_optional: i64,\n    1  => c2c_msg: SvcReqGetMsgV2,\n    2  => group_msg: SvcReqPullGroupMsgSeq,\n    14 => dis_group_msg_filter: u8,\n    15 => group_mask: u8,\n    16 => end_seq: i64,\n    20 => _0769_body: Bytes,\n});\n\nJceStruct!(SvcReqGetMsgV2 {\n    0 => uin: i64,\n    1 => date_time: i32,\n    4 => recive_pic: u8,\n    6 => ability: i16,\n    9 => channel: u8,\n    16 => inst: u8,\n    17 => channel_ex: u8,\n    18 => sync_cookie: Bytes,\n    19 => sync_flag: i32,\n    20 => ramble_flag: u8,\n    26 => general_abi: i64,\n    27 => pub_account_cookie: Bytes,\n});\n\nJceStruct!(PullGroupSeqParam {\n    0 => group_code: i64,\n    1 => last_seq_id: i64,\n});\n\nJceStruct!(SvcRespParam {\n    0 => pc_stat: i32,\n    1 => is_support_c2c_roam_msg: i32,\n    2 => is_support_data_line: i32,\n    3 => is_support_printable: i32,\n    4 => is_support_view_p_c_file: i32,\n    5 => pc_version: i32,\n    6 => roam_flag: i64,\n    7 => online_infos: Vec<OnlineInfo>,\n    8 => pc_client_type: i32,\n});\n\nJceStruct!(RequestPushNotify {\n    0 => uin: i64,\n    1 => r#type: u8,\n    2 => service: String,\n    3 => cmd: String,\n    4 => notify_cookie: Bytes,\n    5 => msg_type: i32,\n    6 => user_active: i32,\n    7 => general_flag: i32,\n    8 => binded_uin: i64,\n});\n\nJceStruct!(OnlineInfo {\n    0 => instance_id: i32,\n    1 => client_type: i32,\n    2 => online_status: i32,\n    3 => platform_id: i32,\n    4 => sub_platform: Bytes,\n    5 => u_client_type: i64,\n});\n\nJceStruct!(SvcReqMSFLoginNotify {\n    0 => app_id: i64,\n    1 => status: u8,\n    2 => tablet: u8,\n    3 => platform: i64,\n    4 => title: String,\n    5 => info: String,\n    6 => product_type: i64,\n    7 => client_type: i64,\n    8 => instance_list: Vec<InstanceInfo>,\n});\n\nJceStruct!(InstanceInfo {\n    0 => app_id: i32,\n    1 => tablet: u8,\n    2 => platform: i64,\n    3 => product_type: i64,\n    4 => client_type: i64,\n});\n\nJceStruct!(PushMessageInfo {\n    0 => from_uin: i64,\n    1 => msg_time: i64,\n    2 => msg_type: i16,\n    3 => msg_seq: i16,\n    4 => msg: String,\n    5 => real_msg_time: i32,\n    6 => v_msg: Bytes,\n    7 => app_share_id: i64,\n    8 => msg_cookies: Bytes,\n    9 => app_share_cookie: Bytes,\n    10 => msg_uid: i64,\n    11 => last_change_time: i64,\n    14 => from_inst_id: i64,\n    15 => remark_of_sender: Bytes,\n    16 => from_mobile: String,\n    17 => from_name: String,\n});\n\nJceStruct!(SvcRespPushMsg {\n    0 => uin: i64,\n    1 => del_infos: Vec<DelMsgInfo>,\n    2 => svrip: i32,\n    3 => push_token: Bytes,\n    4 => service_type: i32,\n});\n\nJceStruct!(SvcReqGetDevLoginInfo {\n    0 => guid: Bytes,\n    1 => app_name: String,\n    2 => login_type: i64,\n    3 => timestamp: i64,\n    4 => next_item_index: i64,\n    5 => require_max: i64,\n    6 => get_dev_list_type: i64, // 1: getLoginDevList 2: getRecentLoginDevList 4: getAuthLoginDevList\n});\n\nJceStruct!(SvcDevLoginInfo {\n    0 => app_id: i64,\n    1 => guid: Bytes,\n    2 => login_time: i64,\n    3 => login_platform: i64,\n    4 => login_location: String,\n    5 => device_name: String,\n    6 => device_type_info: String,\n    8 => ter_type: i64,\n    9 => product_type: i64,\n    10 => can_be_kicked: i64,\n});\n\nJceStruct!(DelMsgInfo {\n    0 => from_uin: i64,\n    1 => msg_time: i64,\n    2 => msg_seq: i16,\n    3 => msg_cookies: Bytes,\n    4 => cmd: i16,\n    5 => msg_type: i64,\n    6 => app_id: i64,\n    7 => send_time: i64,\n    8 => sso_seq: i32,\n    9 => sso_ip: i32,\n    10 => client_ip: i32,\n});\n\n// 下面是生成的\n//\n// JceStruct!(RequestPacket {\n//     1 => i_version: i16\n//     2 => c_packet_type: u8\n//     3 => i_message_type: i32\n//     4 => i_request_id: i32\n//     5 => s_servant_name: String\n//     6 => s_func_name: String\n//     7 => s_buffer: Bytes\n//     8 => i_timeout: i32\n//     9 => context: map[String]String\n//     10 => status: map[String]String\n// });\n//\n// JceStruct!(RequestDataVersion3 {\n// \t\t0 => map: map[String]Bytes\n// \t});\n//\n// JceStruct!(RequestDataVersion2 {\n// \t\t0 => map: map[String]map[String]Bytes\n// \t});\n//\n// JceStruct!(SsoServerInfo {\n// \t\t1 => server: String\n// \t\t2 => port: i32\n// \t\t8 => location: String\n// \t});\n//\n// JceStruct!(FileStoragePushFSSvcList {\n// \t\t0 => upload_list: []FileStorageServerInfo\n// \t\t1 => pic_download_list: []FileStorageServerInfo\n// \t\t2 => g_pic_download_list: []FileStorageServerInfo\n// \t\t3 => q_zone_proxy_service_list: []FileStorageServerInfo\n// \t\t4 => url_encode_service_list: []FileStorageServerInfo\n// \t\t5 => big_data_channel: BigDataChannel\n// \t\t6 => vip_emotion_list: []FileStorageServerInfo\n// \t\t7 => c2CPicDownList: []FileStorageServerInfo\n// \t\t10 => ptt_list: Bytes\n// \t});\n//\n// JceStruct!(FileStorageServerInfo {\n// \t\t1 => server: String\n// \t\t2 => port: i32\n// \t});\n//\n// JceStruct!(BigDataChannel {\n// \t\t0 => i_p_lists: []BigDataIPList\n// \t\t1 => sig_session: Bytes\n// \t\t2 => key_session: Bytes\n// \t\t3 => sig_uin: i64\n// \t\t4 => connect_flag: i32\n// \t\t5 => pb_buf: Bytes\n// \t});\n//\n// JceStruct!(BigDataIPList {\n// \t\t0 => service_type: i64\n// \t\t1 => i_p_list: []BigDataIPInfo\n// \t\t3 => fragment_size: i64\n// \t});\n//\n// JceStruct!(BigDataIPInfo {\n// \t\t0 => type: i64\n// \t\t1 => server: String\n// \t\t2 => port: i64\n// \t});\n//\n// JceStruct!(SvcReqRegister {\n//\n// \t\t0 => uin: i64\n// \t\t1 => bid: i64\n// \t\t2 => conn_type: u8\n// \t\t3 => other: String\n// \t\t4 => status: i32\n// \t\t5 => online_push: u8\n// \t\t6 => is_online: u8\n// \t\t7 => is_show_online: u8\n// \t\t8 => kick_p_c: u8\n// \t\t9 => kick_weak: u8\n// \t\t10 => timestamp: i64\n// \t\t11 => i_o_s_version: i64\n// \t\t12 => net_type: u8\n// \t\t13 => build_ver: String\n// \t\t14 => reg_type: u8\n// \t\t15 => dev_param: Bytes\n// \t\t16 => guid: Bytes\n// \t\t17 => locale_id: i32\n// \t\t18 => silent_push: u8\n// \t\t19 => dev_name: String\n// \t\t20 => dev_type: String\n// \t\t21 => o_s_ver: String\n// \t\t22 => open_push: u8\n// \t\t23 => large_seq: i64\n// \t\t24 => last_watch_start_time: i64\n// \t\t26 => old_s_s_o_ip: i64\n// \t\t27 => new_s_s_o_ip: i64\n// \t\t28 => channel_no: String\n// \t\t29 => c_p_i_d: i64\n// \t\t30 => vendor_name: String\n// \t\t31 => vendor_o_s_name: String\n// \t\t32 => i_o_s_idfa: String\n// \t\t33 => b769: Bytes\n// \t\t34 => is_set_status: u8\n// \t\t35 => server_buf: Bytes\n// \t\t36 => set_mute: u8\n// \t\t38 => ext_online_status: i64\n// \t\t39 => battery_status: i32\n// \t});\n//\n// JceStruct!(SvcRespRegister {\n// \t\t0 => uin: i64\n// \t\t1 => bid: i64\n// \t\t2 => reply_code: u8\n// \t\t3 => result: String\n// \t\t4 => server_time: i64\n// \t\t5 => log_q_q: u8\n// \t\t6 => need_kik: u8\n// \t\t7 => update_flag: u8\n// \t\t8 => timestamp: i64\n// \t\t9 => crash_flag: u8\n// \t\t10 => client_ip: String\n// \t\t11 => client_port: i32\n// \t\t12 => hello_interval: i32\n// \t\t13 => large_seq: i32\n// \t\t14 => large_seq_update: u8\n// \t\t15 => d769RspBody: Bytes\n// \t\t16 => status: i32\n// \t\t17 => ext_online_status: i64\n// \t\t18 => client_battery_get_interval: i64\n// \t\t19 => client_auto_status_interval: i64\n// \t});\n//\n// JceStruct!(SvcReqRegisterNew {\n//\n// \t\t0 => request_optional: i64\n// \t\t1 => c2CMsg:  // SvcReqGetMsgV2\n// \t\t2 => group_msg:  // SvcReqPullGroupMsgSeq\n// \t\t14 => dis_group_msg_filter: u8\n// \t\t15 => group_mask: u8\n// \t\t16 => end_seq: i64\n// \t\t20 => o769Body: Bytes\n// \t});\n//\n// JceStruct!(SvcReqGetMsgV2 {\n//\n// \t\t0 => uin: i64\n// \t\t1 => date_time: i32\n// \t\t4 => recive_pic: u8\n// \t\t6 => ability: i16\n// \t\t9 => channel: u8\n// \t\t16 => inst: u8\n// \t\t17 => channel_ex: u8\n// \t\t18 => sync_cookie: Bytes\n// \t\t19 => sync_flag: int\n// \t\t20 => ramble_flag: u8\n// \t\t26 => general_abi: i64\n// \t\t27 => pub_account_cookie: Bytes\n// \t});\n//\n// JceStruct!(SvcReqPullGroupMsgSeq {\n//\n// \t\t0 => group_info: [] // PullGroupSeqParam\n// \t\t1 => verify_type: u8\n// \t\t2 => filter: i32\n// \t});\n//\n// JceStruct!(PullGroupSeqParam {\n//\n// \t\t0 => group_code: i64\n// \t\t1 => last_seq_id: i64\n// \t});\n//\n// JceStruct!(SvcRespParam {\n// \t\t0 => p_c_stat: i32\n// \t\t1 => is_support_c2CRoamMsg: i32\n// \t\t2 => is_support_data_line: i32\n// \t\t3 => is_support_printable: i32\n// \t\t4 => is_support_view_p_c_file: i32\n// \t\t5 => pc_version: i32\n// \t\t6 => roam_flag: i64\n// \t\t7 => online_infos: []OnlineInfo\n// \t\t8 => p_c_client_type: i32\n// \t});\n//\n// JceStruct!(RequestPushNotify {\n// \t\t0 => uin: i64\n// \t\t1 => type: u8\n// \t\t2 => service: String\n// \t\t3 => cmd: String\n// \t\t4 => notify_cookie: Bytes\n// \t\t5 => msg_type: i32\n// \t\t6 => user_active: i32\n// \t\t7 => general_flag: i32\n// \t\t8 => binded_uin: i64\n// \t});\n//\n// JceStruct!(OnlineInfo {\n// \t\t0 => instance_id: i32\n// \t\t1 => client_type: i32\n// \t\t2 => online_status: i32\n// \t\t3 => platform_id: i32\n// \t\t4 => sub_platform: String\n// \t\t5 => u_client_type: i64\n// \t});\n//\n// JceStruct!(SvcReqMSFLoginNotify {\n// \t\t0 => app_id: i64\n// \t\t1 => status: u8\n// \t\t2 => tablet: u8\n// \t\t3 => platform: i64\n// \t\t4 => title: String\n// \t\t5 => info: String\n// \t\t6 => product_type: i64\n// \t\t7 => client_type: i64\n// \t\t8 => instance_list: []InstanceInfo\n// \t});\n//\n// JceStruct!(InstanceInfo {\n// \t\t0 => app_id: i32\n// \t\t1 => tablet: u8\n// \t\t2 => platform: i64\n// \t\t3 => product_type: i64\n// \t\t4 => client_type: i64\n// \t});\n//\n// JceStruct!(PushMessageInfo {\n// \t\t0 => from_uin: i64\n// \t\t1 => msg_time: i64\n// \t\t2 => msg_type: i16\n// \t\t3 => msg_seq: i16\n// \t\t4 => msg: String\n// \t\t5 => real_msg_time: i32\n// \t\t6 => v_msg: Bytes\n// \t\t7 => app_share_i_d: i64\n// \t\t8 => msg_cookies: Bytes\n// \t\t9 => app_share_cookie: Bytes\n// \t\t10 => msg_uid: i64\n// \t\t11 => last_change_time: i64\n// \t\t14 => from_inst_id: i64\n// \t\t15 => remark_of_sender: Bytes\n// \t\t16 => from_mobile: String\n// \t\t17 => from_name: String\n// \t});\n//\n// JceStruct!(SvcRespPushMsg {\n//\n// \t\t0 => uin: i64\n// \t\t1 => del_infos: []\n// \t\t2 => svrip: i32\n// \t\t3 => push_token: Bytes\n// \t\t4 => service_type: i32\n// \t});\n//\n// JceStruct!(SvcReqGetDevLoginInfo {\n//\n// \t\t0 => guid: Bytes\n// \t\t1 => app_name: String\n// \t\t2 => login_type: i64\n// \t\t3 => timestamp: i64\n// \t\t4 => next_item_index: i64\n// \t\t5 => require_max: i64\n// \t\t6 => get_dev_list_type: i64 // 1: getLoginDevList 2: getRecentLoginDevList 4: getAuthLoginDevList\n// \t});\n//\n// JceStruct!(SvcDevLoginInfo {\n// \t\tAppId          i64\n// \t\tGuid           Bytes\n// \t\tLoginTime      i64\n// \t\tLoginPlatform  i64\n// \t\tLoginLocation  String\n// \t\tDeviceName     String\n// \t\tDeviceTypeInfo String\n// \t\tTerType        i64\n// \t\tProductType    i64\n// \t\tCanBeKicked    i64\n// \t});\n//\n// JceStruct!(DelMsgInfo {\n//\n// \t\t0 => from_uin: i64\n// \t\t1 => msg_time: i64\n// \t\t2 => msg_seq: i16\n// \t\t3 => msg_cookies: Bytes\n// \t\t4 => cmd: i16\n// \t\t5 => msg_type: i64\n// \t\t6 => app_id: i64\n// \t\t7 => send_time: i64\n// \t\t8 => sso_seq: i32\n// \t\t9 => sso_ip: i32\n// \t\t10 => client_ip: i32\n// \t});\n//\nJceStruct!(FriendListRequest {\n    0 => reqtype: i32,\n    1 => if_reflush: u8,\n    2 => uin: i64,\n    3 => start_index: i16,\n    4 => friend_count: i16,\n    5 => group_id: u8,\n    6 => if_get_group_info: u8,\n    7 => group_start_index: u8,\n    8 => group_count: u8,\n    9 => if_get_msf_group: u8,\n    10 => if_show_term_type: u8,\n    11 => version: i64,\n    12 => uin_list: Vec<i64>,\n    13 => app_type: i32,\n    14 => if_get_dov_id: u8,\n    15 => if_get_both_flag: u8,\n    16 => d50: Bytes,\n    17 => d6b: Bytes,\n    18 => sns_type_list: Vec<i64>,\n});\n\n/// 获取好友列表 response\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct FriendListResponse {\n    #[jce(5)]\n    pub total_friend_count: i16,\n    #[jce(7)]\n    pub friend_info_list: Vec<FriendInfo>,\n    #[jce(14)]\n    pub group_info_list: Vec<FriendListGroupInfo>,\n    #[jce(17)]\n    pub online_friend_count: i16,\n}\n\n/// 好友列表分组信息\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct FriendListGroupInfo {\n    #[jce(0)]\n    pub group_id: u8,\n    #[jce(1)]\n    pub group_name: String,\n    #[jce(2)]\n    pub friend_count: i32,\n    #[jce(3)]\n    pub online_friend_count: i32,\n    #[jce(4)]\n    pub seq_id: u8,\n}\n\n/// 好友列表-修改分组请求\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct FriendListSetGroupReq {\n    #[jce(0)]\n    pub req_type: i32,\n    #[jce(1)]\n    pub uin: i64,\n    #[jce(2)]\n    pub body: Bytes,\n}\n\n/// 获取签名request\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct GetRichSigReq {\n    #[jce(1)]\n    pub req_rich_infos: Vec<ReqRichInfo>,\n    #[jce(2)]\n    pub check_update: bool, // false\n    #[jce(3)]\n    pub show_date_sig: bool, // false\n    #[jce(4)]\n    pub get_large_tlv: bool, // true\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct ReqRichInfo {\n    #[jce(1)]\n    pub uin: i64,\n    #[jce(2)]\n    pub dw_time: i64, // 0\n}\n\n/// 获取签名response\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct GetRichSigRes {\n    #[jce(1)]\n    pub result: u8,\n    #[jce(2)]\n    pub sig_infos: Vec<ResRichSigInfo>,\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct ResRichSigInfo {\n    #[jce(1)]\n    pub status: u8,\n    #[jce(2)]\n    pub uin: i64,\n    #[jce(3)]\n    pub dw_time: i64,\n    #[jce(4)]\n    pub sig_info: Bytes,\n}\n\nJceStruct!(FriendInfo {\n    0 => friend_uin: i64,\n    1 => group_id: u8,\n    2 => face_id: i16,\n    3 => remark: String,\n    4 => qq_type: u8,\n    5 => status: u8,\n    6 => member_level: u8,\n    7 => is_mqq_online: u8,\n    8 => qq_online_state: u8,\n    9 => is_iphone_online: u8,\n    10 => detail_status_flag: u8,\n    11 => qq_online_state_v2: u8,\n    12 => show_name: String,\n    13 => is_remark: u8,\n    14 => nick: String,\n    15 => special_flag: u8,\n    16 => im_group_id: Bytes,\n    17 => msf_group_id: Bytes,\n    18 => term_type: i32,\n    20 => network: u8,\n    21 => ring: Bytes,\n    22 => abi_flag: i64,\n    23 => face_addon_id: i64,\n    24 => network_type: i32,\n    25 => vip_font: i64,\n    26 => icon_type: i32,\n    27 => term_desc: String,\n    28 => color_ring: i64,\n    29 => apollo_flag: u8,\n    30 => apollo_timestamp: i64,\n    31 => sex: u8,\n    32 => founder_font: i64,\n    33 => eim_id: String,\n    34 => eim_mobile: String,\n    35 => olympic_torch: u8,\n    36 => apollo_sign_time: i64,\n    37 => lavi_uin: i64,\n    38 => tag_update_time: i64,\n    39 => game_last_login_time: i64,\n    40 => game_app_id: i64,\n    41 => card_id: Bytes,\n    42 => bit_set: i64,\n    43 => king_of_glory_flag: u8,\n    44 => king_of_glory_rank: i64,\n    45 => master_uin: String,\n    46 => last_medal_update_time: i64,\n    47 => face_store_id: i64,\n    48 => font_effect: i64,\n    49 => d_ov_id: String,\n    50 => both_flag: i64,\n    51 => centi_show_3d_flag: u8,\n    52 => intimate_info: Bytes,\n    53 => show_nameplate: u8,\n    54 => new_lover_diamond_flag: u8,\n    55 => ext_sns_frd_data: Bytes,\n    56 => mutual_mark_data: Bytes,\n});\n\nJceStruct!(TroopListRequest {\n    0 => uin: i64,\n    1 => get_msf_msg_flag: u8,\n    2 => cookies: Bytes,\n    3 => group_info: Vec<i64>,\n    4 => group_flag_ext: u8,\n    5 => version: i32,\n    6 => company_id: i64,\n    7 => version_num: i64,\n    8 => get_long_group_name: u8,\n});\n\nJceStruct!(TroopNumber {\n    0 => group_uin: i64,\n    1 => group_code: i64,\n    2 => flag: u8,\n    3 => group_info_seq: i64,\n    4 => group_name: String,\n    5 => group_memo: String,\n    6 => group_flag_ext: i64,\n    7 => group_rank_seq: i64,\n    8 => certification_type: i64,\n    9 => shut_up_timestamp: i64,\n    10 => my_shut_up_timestamp: i64,\n    11 => cmd_uin_uin_flag: i64,\n    12 => additional_flag: i64,\n    13 => group_type_flag: i64,\n    14 => group_sec_type: i64,\n    15 => group_sec_type_info: i64,\n    16 => group_class_ext: i64,\n    17 => app_privilege_flag: i64,\n    18 => subscription_uin: i64,\n    19 => member_num: i64,\n    20 => member_num_seq: i64,\n    21 => member_card_seq: i64,\n    22 => group_flag_ext3: i64,\n    23 => group_owner_uin: i64,\n    24 => is_conf_group: u8,\n    25 => is_modify_conf_group_face: u8,\n    26 => is_modify_conf_group_name: u8,\n    27 => cmd_uin_join_time: i64,\n    28 => company_id: i64,\n    29 => max_group_member_num: i64,\n    30 => cmd_uin_group_mask: i64,\n    31 => guild_app_id: i64,\n    32 => guild_sub_type: i64,\n    33 => cmd_uin_ringtone_i_d: i64,\n    34 => cmd_uin_flag_ex2: i64,\n});\n\nJceStruct!(TroopMemberListRequest {\n    0 => uin: i64,\n    1 => group_code: i64,\n    2 => next_uin: i64,\n    3 => group_uin: i64,\n    4 => version: i64,\n    5 => req_type: i64,\n    6 => get_list_appoint_time: i64,\n    7 => rich_card_name_ver: u8,\n});\n\nJceStruct!(TroopMemberInfo {\n    0 => member_uin: i64,\n    1 => face_id: i16,\n    2 => age: u8,\n    3 => gender: u8,\n    4 => nick: String,\n    5 => status: u8,\n    6 => show_name: String,\n    8 => name: String,\n    12 => memo: String,\n    13 => auto_remark: String,\n    14 => member_level: i64,\n    15 => join_time: i64,\n    16 => last_speak_time: i64,\n    17 => credit_level: i64,\n    18 => flag: i64,\n    19 => flag_ext: i64,\n    20 => point: i64,\n    21 => concerned: u8,\n    22 => shielded: u8,\n    23 => special_title: String,\n    24 => special_title_expire_time: i64,\n    25 => job: String,\n    26 => apollo_flag: u8,\n    27 => apollo_timestamp: i64,\n    28 => global_group_level: i64,\n    29 => title_id: i64,\n    30 => shut_up_timestap: i64,\n    31 => global_group_point: i64,\n    33 => rich_card_name_ver: u8,\n    34 => vip_type: i64,\n    35 => vip_level: i64,\n    36 => big_club_level: i64,\n    37 => big_club_flag: i64,\n    38 => nameplate: i64,\n    39 => group_honor: Bytes,\n});\n\nJceStruct!(ModifyGroupCardRequest {\n    0 => zero: i64,\n    1 => group_code: i64,\n    2 => new_seq: i64,\n    3 => uin_info: Vec<UinInfo>,\n});\n\nJceStruct!(UinInfo {\n    0 => uin: i64,\n    1 => flag: i64,\n    2 => name: String,\n    3 => gender: u8,\n    4 => phone: String,\n    5 => email: String,\n    6 => remark: String,\n});\n\nJceStruct!(SummaryCardReq {\n    0 => uin: i64,\n    1 => come_from: i32,\n    2 => qzone_feed_timestamp: i64,\n    3 => is_friend: u8,\n    4 => group_code: i64,\n    5 => group_uin: i64,\n    8 => get_control: i64,\n    9 => add_friend_source: i32,\n    10 => secure_sig: Bytes,\n    14 => req_services: Vec<Bytes>, // todo\n    15 => tiny_id: i64,\n    16 => like_source: i64,\n    18 => req_medal_wall_info: u8,\n    19 => req_0x5eb_field_id: Vec<i64>,\n    20 => req_nearby_god_info: u8,\n    22 => req_extend_card: u8,\n});\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct RespSummaryCard {\n    #[jce(1)]\n    pub sex: u8,\n    #[jce(2)]\n    pub age: u8,\n    #[jce(3)]\n    pub nickname: String,\n    #[jce(5)]\n    pub level: i32,\n    #[jce(7)]\n    pub city: String,\n    #[jce(8)]\n    pub sign: String,\n    #[jce(11)]\n    pub mobile: String,\n    #[jce(23)]\n    pub uin: i64,\n    #[jce(36)]\n    pub login_days: i64,\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct RespSummaryCardHead {\n    #[jce(0)]\n    pub sex: i32,\n    #[jce(1)]\n    pub age: i32,\n    #[jce(2)]\n    pub err_str: String,\n    #[jce(3)]\n    pub cookie: Bytes,\n}\n\nJceStruct!(SummaryCardReqSearch {\n    0 => keyword: String,\n    1 => country_code: String,\n    2 => version: i32,\n    3 => req_services:  Vec<Bytes>, // todo // busi\n});\n\nJceStruct!(DelFriendReq {\n    0 => uin: i64,\n    1 => del_uin: i64,\n    2 => del_type: u8,\n    3 => version: i32,\n});\n\nJceStruct!(DelFriendResp{\n    0 => uin : i64,\n    1 => del_uin : i64,\n    2 => result : i32,\n    3 => error_code : i16,\n});\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct QQServiceReqHead {\n    #[jce(0)]\n    pub uin: i64,\n    #[jce(1)]\n    pub sh_version: i16,\n    #[jce(2)]\n    pub seq: i32,\n    #[jce(3)]\n    pub req_type: u8,\n    #[jce(4)]\n    pub triggered: u8,\n    #[jce(5)]\n    pub cookies: Bytes,\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct ReqFavorite {\n    #[jce(0)]\n    pub header: QQServiceReqHead,\n    #[jce(1)]\n    pub mid: i64,\n    #[jce(2)]\n    pub op_type: i32,\n    #[jce(3)]\n    pub source: i32,\n    #[jce(4)]\n    pub count: i32,\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct MsgType0x210 {\n    #[jce(0)]\n    pub sub_msg_type: i64,\n    #[jce(10)]\n    pub v_protobuf: Bytes,\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct RequestPushForceOffline {\n    #[jce(0)]\n    pub uin: i64,\n    #[jce(1)]\n    pub title: String,\n    #[jce(2)]\n    pub tips: String,\n    #[jce(3)]\n    pub same_device: u8,\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct RequestMSFForceOffline {\n    #[jce(0)]\n    pub uin: i64,\n    #[jce(1)]\n    pub seq_no: i64,\n    #[jce(2)]\n    pub kick_type: u8,\n    #[jce(3)]\n    pub info: String,\n    #[jce(4)]\n    pub title: String,\n    #[jce(5)]\n    pub sig_kick: u8,\n    #[jce(6)]\n    pub sig_kick_data: Bytes,\n    #[jce(7)]\n    pub same_device: u8,\n}\n\n#[derive(Debug, Clone, JceGet, JcePut, Default)]\npub struct RspMSFForceOffline {\n    #[jce(0)]\n    pub uin: i64,\n    #[jce(1)]\n    pub seq_no: i64,\n    #[jce(2)]\n    pub const_zero: u8,\n}\n\n#[cfg(test)]\nmod tests {\n    use bytes::*;\n\n    use crate::crypto::qqtea_decrypt;\n    use crate::hex::decode_hex;\n\n    use super::*;\n\n    #[test]\n    fn sso_address_resp_decode() {\n        static SSO_ADDRESS_RESP: &str = \"6e477b1c09e193f1d6084a1e8a052c259f6cf4e608e614d5db262ba96fceb6e8a5d19d72860d0db5dfac554cb7de435e4400f9cd0e8b14f98d4020962c496393e6947f85adcaf00656782b9512713177b41d8489ddf8952766c9850639e329905911aa989d618be1e14b133d228f187efd0ae3a8bdec00c6028078862e965940ad8acc937c5522abf967de737632f19d4f27b5bdf34a2003b11d8547cd4f82c7f1fcb8ea8219ada296ca5ed38bc38b4bbb475f51974dffb85daf1a12b3be2d853ff7877eba722148612abe85535492dd955ae9ff2b6cd9bb570711acc0869cdab62f0aa7fb1caa9862abd1e3aca39a96a9b45116cc92a065c2736420cab691e540534307dcc2d872da82c35b03f7a94b0a9bd6fbf73caa002702c10c9af38616ed9e6c54912de021ace4d969ca264d7f9d94ea4913a1a2184e77b9a1bc850c38d7de55b82e21c2f0e45e0e12ab602c54641b20409d1013245f79ec151e1ee773b9cca9f6d172084d1a125b9ff0e3d5efac8f0ad9e4cdec94f9366e6346274b6c9994ae62f35060c961948b5aa23eb2402c008603fa2e416d7803c9e466a9658d5e3470abda44c9b00c985ee28ae01e2f837666b8b8c5fb5fdfd263fa4173c948cf282efcd779fc15e21dbc29d4fa826d92d75bd7ebf19036c621d2fb96e6940fcab78ebea48da089893374b67d8341a10d86c33a182a9354268a7a7e168bd5c4a33e8b6110cd7e838c9bf53b8869ddbe747daf94183627e8dab6f71c67f71c621eddce055eb20803c962433baa1c7b0d1caf8a8b8b10ed3d429adaa38afaf1effab224ea16454e6e99b35dbcc55bfa972b8511ca1dee7af4485e45a4c0b5a6940168678dc0471420e4d9c401a514f12423972d6deb28192e7e9f74987282a67c4e1ea7e987ebbac989a72c85faac5d2648a284dfdb9374dfd17887c4b31b3e676436bbd47c5a4258406552187f2ca3586c58b1187c979c9303561fd4308646f30908d8abb19bae79d493100c034e6b34b2cd810ddb700c1a2d5418f556a8c46ed8912b1ea507d289c350e0ac86e2b80cee31263c10f431353d049890e7bbb732fed29353e75c4172193f45c7260157ee6f5cf84f11c3183f9889e30e239cf2fae16f1d65f14b519f407f848bd014c7b95d61ef6393b137b6989979641af5325f53411090c54164148ce3f732e0cdaac01de3b8585594d1d2e8f76723d995497d2c314f377efbc3974363d031e5de9fa4799a42b4acc28f7b2834203a1f3fa00510604801c7777ae1f62e89e08b6a0248d46a2c055e93458498077b175a2f08313ee373d42b861a727935be574838cdc15e654c6a6d01143d23d072bf7ea93e1094d4c5330a1ee3ea421f78127b60ea65dc9628a4e2eb440f21daaa514d30dde9ba5eea69c19a8ae8c65b60bad64859692dcf9c947b99333c31b21418e2984b178ee61011c0a3cec9d97abc4511ee96599e49fa8dc7b27d38d1cac74071c0b840db24d6ad4f2923622f7482ca0de4dc35698b69bbb3692d85f698f629e1476ac54a35502098ebf607ec1ad0d053745c8685e5fa69f91694163ab89c3391b7e0856239486a3206465e521364b4de7d5e5e2124e8c4a2bf9ca140f2af2c65b5e5c0a29f2cbcaaf0cea569250c1ce9c9356f1ad0ace6c9f729319cd61b701d5b8ccdd4509bfed9c2dbdcb7f6e47c97570b6914b43fd14798337dd997ce6d3d520d3db2a1fb0d50730f89a96df64d5f383c7889c405bb28e261f56a4f22c8dd8d5c95ac5d8653bdaa67e826d0e4214559d70e5f82ce0846970399b11e875c08571165b16029c5c575bb21316c19269ffac6db84efb2aa35b1c877bd824e8d00d18aaf4a7dfec14d8bfa3a92029936aea0dd4eb9c3c8b3c633d2b59339ea5865997c5e52facb546a222fa27e9ec00063a620ab44550241f3a115fc6251ad8b244a3588d400ee18ca0769d2ae64e09b8a6c7e439a856093087930e442d036e6bcfeee0c7b6fa908ab1c6906552cf0cd6341ac14bc04ef45c4bd558d45fa51b0ca3465a9c681fbe24fec1d231a73080b067caa5ac0641e6da3e3d105bf6491752e308c026e94e616582150d3a8a52a7bac25d3880c8324c18a92bcdc73c1dfa7f3591754f8944ac6c32d86163edff93de1d3e435367714c390bcb6ee741032e1981450112a1a1303969008c5170c0774dd7a61952778ff2bd6abab52834b7e5fa1d4c2ae47026e4072bea7d4fa359c25b36658c0372b11ad1c5723fb9308ce31ec4a4e0c38244fdfe4918079\";\n        // static DE_DATA: &'static str = \"0000063710032c3c4c560a436f6e666967487474706611487474705365727665724c6973745265737d000106050800010611487474705365727665724c6973745265731d000105ea0a100129000b0a160e3130392e3234342e3139382e3134211f9030014c5c600870018602737a96066f74686572730b0a160f3138302e3130322e3131312e313035211f9030014c5c6008700186027368960374656c0b0a160c3131332e39362e31322e3835211f9030014c5c600870018602737a960374656c0b0a160b31342e32322e332e3132322101bb30014c5c600870018602737a960374656c0b0a160d34322e38312e3139332e323530205030014c5c600870018602746a960374656c0b0a160e3131342e3232312e3134382e34392136b030014c5c6008700186027368960374656c0b0a160c3131332e39362e31332e39352101bb30014c5c600870018602737a960374656c0b0a160d34322e38312e3139322e323131211f9030014c5c600870018602746a960374656c0b0a160c3130312e39312e34322e3938205030014c5c6008700186027368960374656c0b0a16116d7366776966692e33672e71712e636f6d211f9030014c5c60087c86066f746865727396066f74686572730b0a160d34322e38312e3137322e323037205030014c5c600870018602746a960374656c0b39000b0a160e3130392e3234342e3139382e3134211f9030014c5c600870018602737a96066f74686572730b0a160f3138302e3130322e3131312e313035211f9030014c5c6008700186027368960374656c0b0a160c3131332e39362e31322e3835211f9030014c5c600870018602737a960374656c0b0a160b31342e32322e332e3132322101bb30014c5c600870018602737a960374656c0b0a160d34322e38312e3139332e323530205030014c5c600870018602746a960374656c0b0a160e3131342e3232312e3134382e34392136b030014c5c6008700186027368960374656c0b0a160c3131332e39362e31332e39352101bb30014c5c600870018602737a960374656c0b0a160d34322e38312e3139322e323131211f9030014c5c600870018602746a960374656c0b0a160c3130312e39312e34322e3938205030014c5c6008700186027368960374656c0b0a16116d7366776966692e33672e71712e636f6d211f9030014c5c60087c86066f746865727396066f74686572730b0a160d34322e38312e3137322e323037205030014c5c600870018602746a960374656c0b426155ae2b5138406c7c80029005acbcc900080a160e3130392e3234342e3132392e3135205030014c500360087c8602737a96066f74686572730b0a160e3131342e3232312e3134342e3232205030014c500360087c86027368960374656c0b0a160c3131332e39362e31332e3434205030014c500360087c8602737a960374656c0b0a160e3131392e3134372e3139302e3337205030014c500360087c8602737a960374656c0b0a160d34322e38312e3139332e323432205030014c500360087c8602746a960374656c0b0a160d3138302e3130322e35392e3530205030014c500360087c86027368960374656c0b0a160d34322e38312e3136392e313035205030014c500360087c8602746a960374656c0b0a160d34322e38312e3136392e313035205030014c500360087c8602746a960374656c0bd900080a160e3130392e3234342e3132392e3135205030014c500360087c8602737a96066f74686572730b0a160e3131342e3232312e3134342e3232205030014c500360087c86027368960374656c0b0a160c3131332e39362e31332e3434205030014c500360087c8602737a960374656c0b0a160e3131392e3134372e3139302e3337205030014c500360087c8602737a960374656c0b0a160d34322e38312e3139332e323432205030014c500360087c8602746a960374656c0b0a160d3138302e3130322e35392e3530205030014c500360087c86027368960374656c0b0a160d34322e38312e3136392e313035205030014c500360087c8602746a960374656c0b0a160d34322e38312e3136392e313035205030014c500360087c8602746a960374656c0bed000cf90f0cf9100cf9110cf01202f113ff38f61428323032312d30392d33302031363a33313a33392064656c6976657279696e67206120706f6c696379fc150b8c980ca80c\";\n\n        let key = decode_hex(\"F0441F5FF42DA58FDCF7949ABA62D411\").expect(\"failed to decode hex\");\n\n        let data = decode_hex(SSO_ADDRESS_RESP).expect(\"failed to decode_hex\");\n        let mut de_rsp = Bytes::from(qqtea_decrypt(&data, &key));\n\n        de_rsp.advance(4);\n        let mut request_packet: RequestPacket =\n            jcers::from_buf(&mut de_rsp).expect(\"failed to decode RequestPacket\");\n\n        let mut request_data_version3: RequestDataVersion3 =\n            jcers::from_buf(&mut request_packet.s_buffer)\n                .expect(\"failed to decode RequestDataVersion3\");\n\n        let sso_server_infos: HttpServerListRes = jcers::from_buf(\n            request_data_version3\n                .map\n                .get_mut(\"HttpServerListRes\")\n                .expect(\"failed to get HttpServerListRes\"),\n        )\n        .expect(\"failed to parse jce\");\n        for s in sso_server_infos.sso_server_infos {\n            println!(\"Get Addrs server:{} port:{}\", s.server, s.port);\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/lib.rs",
    "content": "#![feature(impl_trait_in_assoc_type)]\n#![feature(type_alias_impl_trait)]\n#![feature(return_position_impl_trait_in_trait)]\n\nuse std::sync::atomic::{AtomicI32, AtomicI64, AtomicU16, Ordering};\n\nuse bytes::Bytes;\nuse rand::Rng;\n\npub use error::{RQError, RQResult};\nuse protocol::device::Device;\nuse protocol::oicq;\nuse protocol::transport::Transport;\nuse protocol::version::Version;\n\npub use crate::token::Token;\n\npub mod binary;\npub mod command;\npub mod common;\npub mod crypto;\npub mod error;\npub mod hex;\npub mod highway;\npub mod jce;\npub mod msg;\npub mod pb;\npub mod protocol;\npub mod structs;\npub mod token;\nmod utils;\npub mod wtlogin;\n\n// build_packet: param -> bytes\n// decode_packet: bytes -> struct\n// this should be wrapped in a rwlock (readonly after login)\n// TODO: build library for other language\n// no async and await\npub struct Engine {\n    pub uin: AtomicI64,\n    pub transport: Transport,\n    pub seq_id: AtomicU16,\n    pub request_packet_request_id: AtomicI32,\n    pub group_seq: AtomicI32,\n    pub friend_seq: AtomicI32,\n    pub group_data_trans_seq: AtomicI32,\n    pub highway_apply_up_seq: AtomicI32,\n}\n\nimpl Engine {\n    pub fn new(device: Device, version: Version) -> Self {\n        Self {\n            uin: AtomicI64::new(0),\n            transport: Transport::new(device, version),\n            seq_id: AtomicU16::new(0x3635),\n            request_packet_request_id: AtomicI32::new(1921334513),\n            group_seq: AtomicI32::new(rand::thread_rng().gen_range(0..20000)),\n            friend_seq: AtomicI32::new(rand::thread_rng().gen_range(0..20000)),\n            group_data_trans_seq: AtomicI32::new(rand::thread_rng().gen_range(0..20000)),\n            highway_apply_up_seq: AtomicI32::new(rand::thread_rng().gen_range(0..20000)),\n        }\n    }\n\n    pub fn uin(&self) -> i64 {\n        self.uin.load(Ordering::Relaxed)\n    }\n\n    pub fn next_seq(&self) -> u16 {\n        self.seq_id.fetch_add(1, Ordering::Relaxed)\n    }\n\n    pub fn next_packet_seq(&self) -> i32 {\n        self.request_packet_request_id\n            .fetch_add(2, Ordering::Relaxed)\n    }\n\n    pub fn next_group_seq(&self) -> i32 {\n        self.group_seq.fetch_add(2, Ordering::Relaxed)\n    }\n\n    pub fn next_friend_seq(&self) -> i32 {\n        self.friend_seq.fetch_add(2, Ordering::Relaxed)\n    }\n\n    pub fn next_group_data_trans_seq(&self) -> i32 {\n        self.group_data_trans_seq.fetch_add(2, Ordering::Relaxed)\n    }\n\n    pub fn next_highway_apply_seq(&self) -> i32 {\n        self.highway_apply_up_seq.fetch_add(2, Ordering::Relaxed)\n    }\n\n    pub fn gen_token(&self) -> Token {\n        Token {\n            uin: self.uin(),\n            d2: self.transport.sig.d2.to_vec(),\n            d2key: self.transport.sig.d2key.to_vec(),\n            tgt: self.transport.sig.tgt.to_vec(),\n            srm_token: self.transport.sig.srm_token.to_vec(),\n            t133: self.transport.sig.t133.to_vec(),\n            encrypted_a1: self.transport.sig.encrypted_a1.to_vec(),\n            out_packet_session_id: self.transport.sig.out_packet_session_id.to_vec(),\n            tgtgt_key: self.transport.sig.tgtgt_key.to_vec(),\n            wt_session_ticket_key: self.transport.oicq_codec.wt_session_ticket_key.to_vec(),\n        }\n    }\n\n    pub fn load_token(&mut self, token: Token) {\n        self.uin.store(token.uin, Ordering::Relaxed);\n        self.transport.sig.d2 = Bytes::from(token.d2);\n        self.transport.sig.d2key = Bytes::from(token.d2key);\n        self.transport.sig.tgt = Bytes::from(token.tgt);\n        self.transport.sig.srm_token = Bytes::from(token.srm_token);\n        self.transport.sig.t133 = Bytes::from(token.t133);\n        self.transport.sig.encrypted_a1 = Bytes::from(token.encrypted_a1);\n        self.transport.sig.out_packet_session_id = Bytes::from(token.out_packet_session_id);\n        self.transport.sig.tgtgt_key = Bytes::from(token.tgtgt_key);\n        self.transport.oicq_codec.wt_session_ticket_key = Bytes::from(token.wt_session_ticket_key);\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/msg/elem/anonymous.rs",
    "content": "use crate::msg::{MessageChainBuilder, MessageElem, PushBuilder};\nuse crate::pb::msg;\nuse crate::pb::msg::AnonymousGroupMessage;\n\n#[derive(Default, Debug, Clone)]\npub struct Anonymous {\n    // 用于禁言\n    pub anon_id: Vec<u8>,\n    pub nick: String,\n    pub portrait_index: i32,\n    pub bubble_index: i32,\n    pub expire_time: i32,\n    pub color: String,\n}\n\nimpl From<Anonymous> for MessageElem {\n    fn from(e: Anonymous) -> Self {\n        MessageElem::AnonGroupMsg(msg::AnonymousGroupMessage {\n            flags: Some(2),\n            anon_id: None,\n            anon_nick: Some(e.nick.into_bytes()),\n            head_portrait: Some(e.portrait_index),\n            expire_time: Some(e.expire_time),\n            bubble_id: Some(e.bubble_index),\n            rank_color: Some(e.color.into_bytes()),\n        })\n    }\n}\n\nimpl PushBuilder for Anonymous {\n    fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {\n        builder.elems.insert(0, elem.into());\n    }\n}\n\nimpl From<msg::AnonymousGroupMessage> for Anonymous {\n    fn from(e: AnonymousGroupMessage) -> Self {\n        Self {\n            anon_id: e.anon_id.unwrap_or_default(),\n            nick: String::from_utf8_lossy(&e.anon_nick.unwrap_or_default()).into_owned(),\n            portrait_index: e.head_portrait.unwrap_or_default(),\n            bubble_index: e.bubble_id.unwrap_or_default(),\n            expire_time: e.expire_time.unwrap_or_default(),\n            color: String::from_utf8_lossy(&e.rank_color.unwrap_or_default()).into_owned(),\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/msg/elem/at.rs",
    "content": "use std::fmt;\n\nuse bytes::{Buf, BufMut};\n\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n#[derive(Default, Debug, Clone)]\npub struct At {\n    pub target: i64,\n    pub display: String,\n}\n\nimpl At {\n    pub fn new(target: i64) -> Self {\n        Self {\n            target,\n            display: format!(\"@{target}\"),\n        }\n    }\n}\n\nimpl PushElem for At {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::Text(msg::Text {\n            attr6_buf: Some({\n                let mut w = Vec::new();\n                w.put_u16(1);\n                w.put_u16(0);\n                w.put_u16(elem.display.chars().count() as u16);\n                w.put_u8(if elem.target == 0 { 1 } else { 0 });\n                w.put_u32(elem.target as u32);\n                w.put_u16(0);\n                w\n            }),\n            str: Some(elem.display),\n            ..Default::default()\n        }));\n    }\n}\n\nimpl From<msg::Text> for At {\n    fn from(e: msg::Text) -> Self {\n        let (_, mut attr6) = e.attr6_buf().split_at(7);\n        let target = attr6.get_u32() as i64;\n        Self {\n            target,\n            display: e.str.unwrap_or_default(),\n        }\n    }\n}\n\nimpl fmt::Display for At {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"[{}]\", self.display)\n    }\n}\n\nto_elem_vec_impl!(At);\npush_builder_impl!(At);\n"
  },
  {
    "path": "ricq-core/src/msg/elem/face.rs",
    "content": "use std::fmt;\n\nuse prost::Message;\n\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n#[derive(Default, Debug, Clone)]\npub struct Face {\n    pub index: i32,\n    pub name: String,\n}\n\nimpl Face {\n    pub fn new(id: i32) -> Self {\n        Self {\n            index: id,\n            name: Self::name(id).into(),\n        }\n    }\n\n    pub fn name(id: i32) -> &'static str {\n        face_id_map(id).unwrap_or(\"未知表情\")\n    }\n\n    pub fn new_from_name(name: &str) -> Option<Self> {\n        face_name_map(name).map(Self::new)\n    }\n}\n\nimpl PushElem for Face {\n    fn push_to(e: Self, vec: &mut Vec<MessageElem>) {\n        let elem = if e.index >= 260 {\n            let text = format!(\"/{}\", e.name).as_bytes().to_vec();\n            let elem = msg::MsgElemInfoServtype33 {\n                index: Some(e.index as u32),\n                text: Some(text.clone()),\n                compat: Some(text),\n                buf: None,\n            }\n            .encode_to_vec();\n            msg::elem::Elem::CommonElem(msg::CommonElem {\n                service_type: Some(33),\n                pb_elem: Some(elem),\n                business_type: Some(1),\n            })\n        } else {\n            msg::elem::Elem::Face(msg::Face {\n                index: Some(e.index),\n                old: Some(((0x1445 - 4 + e.index) as u16).to_be_bytes().to_vec()),\n                buf: Some(vec![0x00, 0x01, 0x00, 0x04, 0x52, 0xCC, 0xF5, 0xD0]),\n            })\n        };\n        vec.push(elem);\n    }\n}\n\nimpl From<msg::Face> for Face {\n    fn from(e: msg::Face) -> Self {\n        Self::new(e.index())\n    }\n}\n\nimpl From<msg::MsgElemInfoServtype33> for Face {\n    fn from(e: msg::MsgElemInfoServtype33) -> Self {\n        Self::new(e.index() as i32)\n    }\n}\n\nimpl fmt::Display for Face {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"[{}]\", self.name)\n    }\n}\n\nto_elem_vec_impl!(Face);\npush_builder_impl!(Face);\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test() {\n        let name = Face::name(1);\n        println!(\"{name:?}\")\n    }\n}\n\n// pub fn face_id_map(key: i32) -> Option<&'static str> {\n//     match key {\n//         14 => Some(\"微笑\"),\n//         _ => None,\n//     }\n// }\n\n// pub fn face_name_map(key: &str) -> Option<i32> {\n//     match key {\n//         \"微笑\" => Some(14),\n//         _ => None,\n//     }\n// }\n\nmacro_rules! faces_map {\n    ($(($id: expr, $name: expr)),*) => {\n        pub fn face_id_map(id: i32) -> Option<&'static str> {\n            match id {\n                $(\n                    $id => Some($name),\n                )*\n                _ => None,\n            }\n        }\n        pub fn face_name_map(name: &str) -> Option<i32> {\n            match name {\n                $(\n                    $name => Some($id),\n                )*\n                _ => None,\n            }\n        }\n    };\n}\n\nfaces_map!(\n    (14, \"微笑\"),\n    (1, \"撇嘴\"),\n    (2, \"色\"),\n    (3, \"发呆\"),\n    (4, \"得意\"),\n    (6, \"害羞\"),\n    (7, \"闭嘴\"),\n    (8, \"睡\"),\n    (9, \"大哭\"),\n    (5, \"流泪\"),\n    (10, \"尴尬\"),\n    (11, \"发怒\"),\n    (12, \"调皮\"),\n    (13, \"呲牙\"),\n    (0, \"惊讶\"),\n    (15, \"难过\"),\n    (16, \"酷\"),\n    (96, \"冷汗\"),\n    (18, \"抓狂\"),\n    (19, \"吐\"),\n    (20, \"偷笑\"),\n    (21, \"可爱\"),\n    (22, \"白眼\"),\n    (23, \"傲慢\"),\n    (24, \"饥饿\"),\n    (25, \"困\"),\n    (26, \"惊恐\"),\n    (27, \"流汗\"),\n    (28, \"憨笑\"),\n    (29, \"悠闲\"),\n    (30, \"奋斗\"),\n    (31, \"咒骂\"),\n    (32, \"疑问\"),\n    (33, \"嘘\"),\n    (34, \"晕\"),\n    (35, \"折磨\"),\n    (36, \"衰\"),\n    (37, \"骷髅\"),\n    (38, \"敲打\"),\n    (39, \"再见\"),\n    (97, \"擦汗\"),\n    (98, \"抠鼻\"),\n    (99, \"鼓掌\"),\n    (100, \"糗大了\"),\n    (101, \"坏笑\"),\n    (102, \"左哼哼\"),\n    (103, \"右哼哼\"),\n    (104, \"哈欠\"),\n    (105, \"鄙视\"),\n    (106, \"委屈\"),\n    (107, \"快哭了\"),\n    (108, \"阴险\"),\n    (305, \"右亲亲\"),\n    (109, \"左亲亲\"),\n    (110, \"吓\"),\n    (111, \"可怜\"),\n    (172, \"眨眼睛\"),\n    (182, \"笑哭\"),\n    (179, \"doge\"),\n    (173, \"泪奔\"),\n    (174, \"无奈\"),\n    (212, \"托腮\"),\n    (175, \"卖萌\"),\n    (178, \"斜眼笑\"),\n    (177, \"喷血\"),\n    (180, \"惊喜\"),\n    (181, \"骚扰\"),\n    (176, \"小纠结\"),\n    (183, \"我最美\"),\n    (245, \"加油必胜\"),\n    (246, \"加油抱抱\"),\n    (247, \"口罩护体\"),\n    (260, \"搬砖中\"),\n    (261, \"忙到飞起\"),\n    (262, \"脑阔疼\"),\n    (263, \"沧桑\"),\n    (264, \"捂脸\"),\n    (265, \"辣眼睛\"),\n    (266, \"哦哟\"),\n    (267, \"头秃\"),\n    (268, \"问号脸\"),\n    (269, \"暗中观察\"),\n    (270, \"emm\"),\n    (271, \"吃瓜\"),\n    (272, \"呵呵哒\"),\n    (277, \"汪汪\"),\n    (307, \"喵喵\"),\n    (306, \"牛气冲天\"),\n    (281, \"无眼笑\"),\n    (282, \"敬礼\"),\n    (283, \"狂笑\"),\n    (284, \"面无表情\"),\n    (285, \"摸鱼\"),\n    (293, \"摸锦鲤\"),\n    (286, \"魔鬼笑\"),\n    (287, \"哦\"),\n    (288, \"请\"),\n    (289, \"睁眼\"),\n    (294, \"期待\"),\n    (295, \"拿到红包\"),\n    (296, \"真好\"),\n    (297, \"拜谢\"),\n    (298, \"元宝\"),\n    (299, \"牛啊\"),\n    (300, \"胖三斤\"),\n    (301, \"好闪\"),\n    (303, \"右拜年\"),\n    (302, \"左拜年\"),\n    (304, \"红包包\"),\n    (322, \"拒绝\"),\n    (323, \"嫌弃\"),\n    (311, \"打call\"),\n    (312, \"变形\"),\n    (313, \"嗑到了\"),\n    (314, \"仔细分析\"),\n    (315, \"加油\"),\n    (316, \"我没事\"),\n    (317, \"菜汪\"),\n    (318, \"崇拜\"),\n    (319, \"比心\"),\n    (320, \"庆祝\"),\n    (321, \"老色痞\"),\n    (324, \"吃糖\"),\n    (325, \"惊吓\"),\n    (326, \"生气\"),\n    (53, \"蛋糕\"),\n    (114, \"篮球\"),\n    (327, \"加一\"),\n    (328, \"错号\"),\n    (329, \"对号\"),\n    (330, \"完成\"),\n    (331, \"明白\"),\n    (49, \"拥抱\"),\n    (66, \"爱心\"),\n    (63, \"玫瑰\"),\n    (64, \"凋谢\"),\n    (187, \"幽灵\"),\n    (146, \"爆筋\"),\n    (116, \"示爱\"),\n    (67, \"心碎\"),\n    (60, \"咖啡\"),\n    (185, \"羊驼\"),\n    (192, \"红包\"),\n    (137, \"鞭炮\"),\n    (138, \"灯笼\"),\n    (136, \"双喜\"),\n    (76, \"赞\"),\n    (124, \"OK\"),\n    (118, \"抱拳\"),\n    (78, \"握手\"),\n    (119, \"勾引\"),\n    (79, \"胜利\"),\n    (120, \"拳头\"),\n    (121, \"差劲\"),\n    (77, \"踩\"),\n    (122, \"爱你\"),\n    (123, \"NO\"),\n    (201, \"点赞\"),\n    (203, \"托脸\"),\n    (204, \"吃\"),\n    (202, \"无聊\"),\n    (200, \"拜托\"),\n    (194, \"不开心\"),\n    (193, \"大笑\"),\n    (197, \"冷漠\"),\n    (211, \"我不看\"),\n    (210, \"飙泪\"),\n    (198, \"呃\"),\n    (199, \"好棒\"),\n    (207, \"花痴\"),\n    (205, \"送花\"),\n    (206, \"害怕\"),\n    (208, \"小样儿\"),\n    (308, \"求红包\"),\n    (309, \"谢红包\"),\n    (310, \"新年烟花\"),\n    (290, \"敲开心\"),\n    (291, \"震惊\"),\n    (292, \"让我康康\"),\n    (226, \"拍桌\"),\n    (215, \"糊脸\"),\n    (237, \"偷看\"),\n    (214, \"啵啵\"),\n    (235, \"颤抖\"),\n    (222, \"抱抱\"),\n    (217, \"扯一扯\"),\n    (221, \"顶呱呱\"),\n    (225, \"撩一撩\"),\n    (241, \"生日快乐\"),\n    (227, \"拍手\"),\n    (238, \"扇脸\"),\n    (240, \"喷脸\"),\n    (229, \"干杯\"),\n    (216, \"拍头\"),\n    (218, \"舔一舔\"),\n    (233, \"掐一掐\"),\n    (219, \"蹭一蹭\"),\n    (244, \"扔狗\"),\n    (232, \"佛系\"),\n    (243, \"甩头\"),\n    (223, \"暴击\"),\n    (279, \"打脸\"),\n    (280, \"击掌\"),\n    (231, \"哼\"),\n    (224, \"开枪\"),\n    (278, \"汗\"),\n    (236, \"啃头\"),\n    (228, \"恭喜\"),\n    (220, \"拽炸天\"),\n    (239, \"原谅\"),\n    (242, \"头撞击\"),\n    (230, \"嘲讽\"),\n    (234, \"惊呆\"),\n    (273, \"我酸了\"),\n    (75, \"月亮\"),\n    (74, \"太阳\"),\n    (46, \"猪头\"),\n    (112, \"菜刀\"),\n    (56, \"刀\"),\n    (169, \"手枪\"),\n    (171, \"茶\"),\n    (59, \"便便\"),\n    (144, \"喝彩\"),\n    (147, \"棒棒糖\"),\n    (89, \"西瓜\"),\n    (61, \"饭\"),\n    (148, \"喝奶\"),\n    (274, \"太南了\"),\n    (113, \"啤酒\"),\n    (140, \"K歌\"),\n    (188, \"蛋\"),\n    (55, \"炸弹\"),\n    (184, \"河蟹\"),\n    (158, \"钞票\"),\n    (54, \"闪电\"),\n    (69, \"礼物\"),\n    (190, \"菊花\"),\n    (151, \"飞机\"),\n    (145, \"祈祷\"),\n    (117, \"瓢虫\"),\n    (168, \"药\"),\n    (115, \"乒乓\"),\n    (57, \"足球\"),\n    (41, \"发抖\"),\n    (125, \"转圈\"),\n    (42, \"爱情\"),\n    (43, \"跳跳\"),\n    (86, \"怄火\"),\n    (129, \"挥手\"),\n    (85, \"飞吻\"),\n    (126, \"磕头\"),\n    (128, \"跳绳\"),\n    (130, \"激动\"),\n    (127, \"回头\"),\n    (132, \"献吻\"),\n    (134, \"右太极\"),\n    (133, \"左太极\"),\n    (131, \"街舞\"),\n    (276, \"辣椒酱\")\n);\n"
  },
  {
    "path": "ricq-core/src/msg/elem/flash_image.rs",
    "content": "use std::fmt;\n\nuse crate::command::common::PbToBytes;\nuse crate::msg::elem::{FriendImage, GroupImage};\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n#[derive(Debug, Clone)]\npub enum FlashImage {\n    FriendImage(FriendImage),\n    GroupImage(GroupImage),\n}\n\nimpl FlashImage {\n    pub fn url(&self) -> String {\n        match self {\n            FlashImage::FriendImage(i) => i.url(),\n            FlashImage::GroupImage(i) => i.url(),\n        }\n    }\n}\n\nimpl PushElem for FlashImage {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        let flash = {\n            match elem {\n                FlashImage::FriendImage(image) => msg::MsgElemInfoServtype3 {\n                    flash_c2c_pic: Some(image.into()),\n                    ..Default::default()\n                },\n                FlashImage::GroupImage(image) => msg::MsgElemInfoServtype3 {\n                    flash_troop_pic: Some(image.into()),\n                    ..Default::default()\n                },\n            }\n        }\n        .to_bytes();\n\n        vec.push(MessageElem::CommonElem(msg::CommonElem {\n            service_type: Some(3),\n            pb_elem: Some(flash.to_vec()),\n            ..Default::default()\n        }));\n        vec.push(MessageElem::Text(msg::Text {\n            str: Some(\"[闪照]请使用新版手机QQ查看闪照。\".to_owned()),\n            ..Default::default()\n        }));\n    }\n}\n\nimpl From<FriendImage> for FlashImage {\n    fn from(e: FriendImage) -> Self {\n        Self::FriendImage(e)\n    }\n}\n\nimpl From<GroupImage> for FlashImage {\n    fn from(e: GroupImage) -> Self {\n        Self::GroupImage(e)\n    }\n}\n\nimpl fmt::Display for FlashImage {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            FlashImage::FriendImage(i) => {\n                write!(f, \"[FlashImage(friend): {}]\", i.url())\n            }\n            FlashImage::GroupImage(i) => {\n                write!(f, \"[FlashImage(group): {}]\", i.url())\n            }\n        }\n    }\n}\n\nto_elem_vec_impl!(FlashImage);\npush_builder_impl!(FlashImage);\n"
  },
  {
    "path": "ricq-core/src/msg/elem/friend_image.rs",
    "content": "use std::fmt;\n\nuse serde::{Deserialize, Serialize};\n\nuse crate::msg::elem::flash_image::FlashImage;\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n#[derive(Default, Debug, Clone, Serialize, Deserialize)]\npub struct FriendImage {\n    pub res_id: String,\n    pub file_path: String,\n    pub md5: Vec<u8>,\n    pub size: u32,\n    pub width: u32,\n    pub height: u32,\n    pub image_type: i32,\n    pub orig_url: String,\n    pub download_path: String,\n}\n\nimpl FriendImage {\n    pub fn flash(self) -> FlashImage {\n        FlashImage::from(self)\n    }\n\n    pub fn url(&self) -> String {\n        if !self.orig_url.is_empty() {\n            return format!(\"https://c2cpicdw.qpic.cn{}\", self.orig_url);\n        }\n        format!(\n            \"https://c2cpicdw.qpic.cn/offpic_new/0/{}/0?term=3\",\n            if !self.download_path.is_empty() {\n                self.download_path.clone()\n            } else {\n                self.res_id.clone()\n            }\n        )\n    }\n}\n\nimpl From<FriendImage> for msg::NotOnlineImage {\n    fn from(e: FriendImage) -> Self {\n        msg::NotOnlineImage {\n            file_path: Some(e.file_path),\n            res_id: Some(e.res_id),\n            old_pic_md5: Some(false),\n            pic_md5: Some(e.md5),\n            download_path: Some(e.download_path),\n            original: Some(0), // 是否原图，如果是0需要写 24，25\n            file_len: Some(e.size),\n            img_type: Some(e.image_type),\n            pic_width: Some(e.width),\n            pic_height: Some(e.height),\n            biz_type: Some(0),\n            show_len: Some(0),\n            download_len: Some(0),\n            pb_reserve: Some(vec![0x78, 0x02]),\n            ..Default::default()\n        }\n    }\n}\n\nimpl PushElem for FriendImage {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::NotOnlineImage(elem.into()));\n    }\n}\n\nimpl From<msg::NotOnlineImage> for FriendImage {\n    fn from(e: msg::NotOnlineImage) -> Self {\n        Self {\n            file_path: e.file_path.unwrap_or_default(),\n            size: e.file_len.unwrap_or_default(),\n            width: e.pic_width.unwrap_or_default(),\n            height: e.pic_height.unwrap_or_default(),\n            md5: e.pic_md5.unwrap_or_default(),\n            orig_url: e.orig_url.unwrap_or_default(),\n            res_id: e.res_id.unwrap_or_default(),\n            download_path: e.download_path.unwrap_or_default(),\n            image_type: e.img_type.unwrap_or_default(),\n        }\n    }\n}\n\nimpl fmt::Display for FriendImage {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"[FriendImage: {}]\", self.url())\n    }\n}\n\nto_elem_vec_impl!(FriendImage);\npush_builder_impl!(FriendImage);\n"
  },
  {
    "path": "ricq-core/src/msg/elem/group_image.rs",
    "content": "use std::fmt;\n\nuse serde::{Deserialize, Serialize};\n\nuse crate::hex::encode_hex;\nuse crate::msg::elem::flash_image::FlashImage;\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::pb::msg::CustomFace;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n#[derive(Default, Debug, Clone, Serialize, Deserialize)]\npub struct GroupImage {\n    pub file_path: String,\n    // hex(md5).jpg\n    pub file_id: i64,\n    pub size: u32,\n    pub width: u32,\n    pub height: u32,\n    pub md5: Vec<u8>,\n    pub orig_url: Option<String>,\n    pub image_type: i32,\n    pub signature: Vec<u8>,\n    // highway session key\n    pub server_ip: u32,\n    pub server_port: u32,\n}\n\nimpl GroupImage {\n    pub fn flash(self) -> FlashImage {\n        FlashImage::from(self)\n    }\n\n    pub fn url(&self) -> String {\n        if let Some(orig_url) = &self.orig_url {\n            format!(\"https://gchat.qpic.cn{orig_url}\")\n        } else {\n            format!(\n                \"https://gchat.qpic.cn/gchatpic_new/0/0-0-{}/0?term=2\",\n                encode_hex(&self.md5).to_uppercase()\n            )\n        }\n    }\n}\n\nimpl From<GroupImage> for msg::CustomFace {\n    fn from(e: GroupImage) -> Self {\n        msg::CustomFace {\n            file_type: Some(66),\n            useful: Some(1),\n            biz_type: Some(5), // 0?\n            width: Some(e.width),\n            height: Some(e.height),\n            file_id: Some(e.file_id as i32),\n            file_path: Some(e.file_path),\n            md5: Some(e.md5),\n            image_type: Some(e.image_type),\n            size: Some(e.size),\n            flag: Some(vec![0; 4]),\n            signature: Some(e.signature),\n            server_ip: Some(e.server_ip),\n            server_port: Some(e.server_port),\n            source: Some(200),     // 200\n            origin: Some(0),       // 是否原图 0/1，设为1不需要29，30\n            show_len: Some(0),     // ?\n            download_len: Some(0), // ?\n            ..Default::default()\n        }\n    }\n}\n\nimpl PushElem for GroupImage {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::CustomFace(elem.into()));\n    }\n}\n\nimpl From<msg::CustomFace> for GroupImage {\n    fn from(custom_face: CustomFace) -> Self {\n        if custom_face.md5().is_empty() {\n            return Self::default();\n        }\n        // guild image todo\n        return Self {\n            file_id: custom_face.file_id() as i64,\n            file_path: custom_face.file_path().to_owned(),\n            size: custom_face.size(),\n            width: custom_face.width(),\n            height: custom_face.height(),\n            orig_url: custom_face.orig_url,\n            md5: custom_face.md5.unwrap_or_default(),\n            image_type: custom_face.image_type.unwrap_or(1000),\n            signature: custom_face.signature.unwrap_or_default(),\n            server_ip: custom_face.server_ip.unwrap_or_default(),\n            server_port: custom_face.server_port.unwrap_or_default(),\n        };\n    }\n}\n\nfn to_uuid(md5: &str) -> String {\n    format!(\n        \"{}-{}-{}-{}-{}\",\n        &md5[0..8],\n        &md5[8..12],\n        &md5[12..16],\n        &md5[16..20],\n        &md5[20..32],\n    )\n}\n\npub fn calculate_image_resource_id(md5: &[u8]) -> String {\n    format!(\"{{{}}}.png\", to_uuid(&encode_hex(md5)))\n}\n\nimpl fmt::Display for GroupImage {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"[GroupImage: {}]\", self.url())\n    }\n}\n\nto_elem_vec_impl!(GroupImage);\npush_builder_impl!(GroupImage);\n"
  },
  {
    "path": "ricq-core/src/msg/elem/light_app.rs",
    "content": "use std::fmt;\nuse std::io::{Read, Write};\n\nuse flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};\n\nuse super::fmt_extract_attr;\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n// Some of the share card message will be a LightApp with pkg id `com.tencent.structmsg`\n#[derive(Default, Debug, Clone)]\npub struct LightApp {\n    pub content: String,\n}\n\nimpl LightApp {\n    pub fn new(content: String) -> Self {\n        Self { content }\n    }\n}\n\nimpl PushElem for LightApp {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::LightApp(msg::LightApp {\n            data: Some({\n                let mut encoder = ZlibEncoder::new(vec![1], Compression::default());\n                encoder.write_all(elem.content.as_bytes()).ok();\n                encoder.finish().unwrap_or_default()\n            }),\n            ..Default::default()\n        }));\n    }\n}\n\nimpl From<msg::LightApp> for LightApp {\n    fn from(e: msg::LightApp) -> Self {\n        let data = e.data.unwrap_or_default();\n        if data.len() > 1 {\n            let content = if data[0] == 0 {\n                data[1..].to_vec()\n            } else {\n                let mut uncompressed = Vec::new();\n                ZlibDecoder::new(&data[1..])\n                    .read_to_end(&mut uncompressed)\n                    .ok();\n                uncompressed\n            };\n            if !content.is_empty() && content.len() < 1024 ^ 3 {\n                return Self {\n                    content: String::from_utf8_lossy(&content).into_owned(),\n                };\n            }\n        }\n        Self::default()\n    }\n}\n\nimpl fmt::Display for LightApp {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(\"[LightApp:\")?;\n        fmt_extract_attr(f, &self.content, \"app\", r#\"\"app\":\"\"#, \"\\\"\")?;\n        fmt_extract_attr(f, &self.content, \"prompt\", r#\"\"prompt\":\"\"#, \"\\\"\")?;\n        fmt_extract_attr(f, &self.content, \"desc\", r#\"\"desc\":\"\"#, \"\\\"\")?;\n        fmt_extract_attr(f, &self.content, \"url\", r#\"\"jumpUrl\":\"\"#, \"\\\"\")?;\n        fmt_extract_attr(f, &self.content, \"title\", r#\"\"title\":\"\"#, \"\\\"\")?;\n        fmt_extract_attr(f, &self.content, \"tag\", r#\"\"tag\":\"\"#, \"\\\"\")?;\n        f.write_str(\"]\")\n    }\n}\n\nto_elem_vec_impl!(LightApp);\npush_builder_impl!(LightApp);\n"
  },
  {
    "path": "ricq-core/src/msg/elem/market_face.rs",
    "content": "use derivative;\n\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n// 不需要实现 Display，因为后面一定会跟 Text\n#[derive(Default, Debug, Clone)]\npub struct MarketFace {\n    pub name: String,\n    pub face_id: Vec<u8>,\n    pub tab_id: i32,\n    pub item_type: i32,\n    pub sub_type: i32,\n    pub media_type: i32,\n    pub encrypt_key: Vec<u8>,\n    pub magic_value: String,\n}\n\nimpl PushElem for MarketFace {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::MarketFace(msg::MarketFace {\n            face_name: Some(elem.name.as_bytes().to_vec()),\n            item_type: Some(elem.item_type as u32),\n            face_info: Some(1),\n            face_id: Some(elem.face_id),\n            tab_id: Some(elem.tab_id as u32),\n            sub_type: Some(elem.sub_type as u32),\n            key: Some(elem.encrypt_key),\n            media_type: Some(elem.media_type as u32),\n            image_width: Some(200),\n            image_height: Some(200),\n            mobileparam: Some(elem.magic_value.as_bytes().to_vec()),\n            ..Default::default()\n        }));\n        vec.push(MessageElem::Text(msg::Text {\n            str: Some(elem.name),\n            ..Default::default()\n        }));\n    }\n}\n\nimpl From<msg::MarketFace> for MarketFace {\n    fn from(e: msg::MarketFace) -> Self {\n        Self {\n            name: String::from_utf8(e.face_name.unwrap_or_default()).unwrap_or_default(),\n            face_id: e.face_id.unwrap_or_default(),\n            tab_id: e.tab_id.unwrap_or_default() as i32,\n            item_type: e.item_type.unwrap_or_default() as i32,\n            sub_type: e.sub_type.unwrap_or_default() as i32,\n            media_type: e.media_type.unwrap_or_default() as i32,\n            encrypt_key: e.key.unwrap_or_default(),\n            magic_value: String::from_utf8(e.mobileparam.unwrap_or_default()).unwrap_or_default(),\n        }\n    }\n}\n\n#[derive(Default, Debug, Clone)]\npub struct Dice {\n    pub value: i32, // range: [1, 6]\n}\n\nimpl Dice {\n    pub fn new(value: i32) -> Self {\n        Self { value }\n    }\n}\n\nimpl From<Dice> for MarketFace {\n    fn from(e: Dice) -> Self {\n        Self {\n            name: \"[骰子]\".into(),\n            face_id: vec![\n                72, 35, 211, 173, 177, 93, 240, 128, 20, 206, 93, 103, 150, 183, 110, 225,\n            ],\n            tab_id: 11464,\n            item_type: 6,\n            sub_type: 3,\n            media_type: 0,\n            encrypt_key: vec![\n                52, 48, 57, 101, 50, 97, 54, 57, 98, 49, 54, 57, 49, 56, 102, 57,\n            ],\n            magic_value: format!(\"rscType?1;value={}\", e.value - 1),\n        }\n    }\n}\n\nimpl From<MarketFace> for Dice {\n    fn from(e: MarketFace) -> Self {\n        Self {\n            value: e\n                .magic_value\n                .split_once('=')\n                .map_or(1, |v| v.1.parse::<i32>().unwrap_or_default() + 1), // 有一种没点数的奇怪骰子\n        }\n    }\n}\n\nimpl PushElem for Dice {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        MarketFace::push_to(MarketFace::from(elem), vec);\n    }\n}\n\n#[derive(Debug, Clone, derivative::Derivative)]\n#[derivative(Default)]\npub enum FingerGuessing {\n    #[derivative(Default)]\n    Rock,\n    Scissors,\n    Paper,\n}\n\nimpl From<FingerGuessing> for MarketFace {\n    fn from(e: FingerGuessing) -> Self {\n        let value = match e {\n            FingerGuessing::Rock => 0,\n            FingerGuessing::Scissors => 1,\n            FingerGuessing::Paper => 2,\n        };\n        MarketFace {\n            name: \"[猜拳]\".into(),\n            face_id: vec![\n                131, 200, 162, 147, 174, 101, 202, 20, 15, 52, 129, 32, 167, 116, 72, 238,\n            ],\n            tab_id: 11415,\n            item_type: 6,\n            sub_type: 3,\n            media_type: 0,\n            encrypt_key: vec![\n                55, 100, 101, 51, 57, 102, 101, 98, 99, 102, 52, 53, 101, 54, 100, 98,\n            ],\n            magic_value: format!(\"rscType?1;value={value}\"),\n        }\n    }\n}\n\nimpl PushElem for FingerGuessing {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        MarketFace::push_to(MarketFace::from(elem), vec);\n    }\n}\n\nimpl From<MarketFace> for FingerGuessing {\n    fn from(e: MarketFace) -> Self {\n        let value = e.magic_value.split('=').collect::<Vec<&str>>()[1]\n            .parse::<i32>()\n            .unwrap_or_default();\n        match value {\n            0 => Self::Rock,\n            1 => Self::Scissors,\n            2 => Self::Paper,\n            _ => Self::Rock,\n        }\n    }\n}\n\nto_elem_vec_impl!(MarketFace);\npush_builder_impl!(MarketFace);\n\nto_elem_vec_impl!(Dice);\npush_builder_impl!(Dice);\n\nto_elem_vec_impl!(FingerGuessing);\npush_builder_impl!(FingerGuessing);\n"
  },
  {
    "path": "ricq-core/src/msg/elem/mod.rs",
    "content": "use std::fmt;\n\nuse prost::Message;\n\npub use group_image::calculate_image_resource_id;\npub(crate) use text::flush_builder;\n\npub use crate::msg::elem::{\n    anonymous::Anonymous,\n    at::At,\n    face::Face,\n    flash_image::FlashImage,\n    friend_image::FriendImage,\n    group_image::GroupImage,\n    light_app::LightApp,\n    market_face::{Dice, FingerGuessing, MarketFace},\n    reply::Reply,\n    rich_msg::RichMsg,\n    text::Text,\n    video_file::VideoFile,\n};\nuse crate::pb::msg;\n\nmod anonymous;\nmod at;\nmod face;\nmod flash_image;\nmod friend_image;\nmod group_image;\nmod light_app;\nmod market_face;\nmod reply;\nmod rich_msg;\nmod text;\nmod video_file;\n\n#[derive(Debug, Clone)]\npub enum RQElem {\n    At(at::At),\n    Text(text::Text),\n    Face(face::Face),\n    MarketFace(market_face::MarketFace),\n    Dice(market_face::Dice),\n    FingerGuessing(market_face::FingerGuessing),\n    LightApp(light_app::LightApp),\n    RichMsg(rich_msg::RichMsg),\n    FriendImage(friend_image::FriendImage),\n    GroupImage(group_image::GroupImage),\n    FlashImage(flash_image::FlashImage),\n    VideoFile(video_file::VideoFile),\n    Other(Box<msg::elem::Elem>),\n}\n\nimpl From<msg::elem::Elem> for RQElem {\n    fn from(elem: msg::elem::Elem) -> Self {\n        match elem {\n            msg::elem::Elem::Text(e) => {\n                // TODO guild at\n                if !e.attr6_buf().is_empty() {\n                    RQElem::At(at::At::from(e))\n                } else {\n                    RQElem::Text(text::Text::from(e))\n                }\n            }\n            msg::elem::Elem::Face(e) => RQElem::Face(face::Face::from(e)),\n            msg::elem::Elem::CommonElem(ref e) => match e.service_type() {\n                // TODO image\n                3 => {\n                    if let Ok(flash) = msg::MsgElemInfoServtype3::decode(e.pb_elem()) {\n                        if let Some(i) = flash.flash_troop_pic {\n                            RQElem::FlashImage(group_image::GroupImage::from(i).flash())\n                        } else if let Some(i) = flash.flash_c2c_pic {\n                            RQElem::FlashImage(friend_image::FriendImage::from(i).flash())\n                        } else {\n                            RQElem::Other(Box::new(elem))\n                        }\n                    } else {\n                        RQElem::Other(Box::new(elem))\n                    }\n                }\n                33 => {\n                    if let Ok(new_face) = msg::MsgElemInfoServtype33::decode(e.pb_elem()) {\n                        RQElem::Face(face::Face::from(new_face))\n                    } else {\n                        RQElem::Other(Box::new(elem))\n                    }\n                }\n                _ => RQElem::Other(Box::new(elem)),\n            },\n            msg::elem::Elem::MarketFace(e) => {\n                let face = MarketFace::from(e);\n                match face.name.as_str() {\n                    // 从商城添加的会显示为“随机骰子”，但在遥远的曾经收藏的表情，会显示为“骰子”\n                    \"[骰子]\" | \"[随机骰子]\" => RQElem::Dice(Dice::from(face)),\n                    \"[猜拳]\" => RQElem::FingerGuessing(FingerGuessing::from(face)),\n                    _ => RQElem::MarketFace(face),\n                }\n            }\n            msg::elem::Elem::LightApp(e) => RQElem::LightApp(light_app::LightApp::from(e)),\n            msg::elem::Elem::RichMsg(e) => RQElem::RichMsg(rich_msg::RichMsg::from(e)),\n            msg::elem::Elem::VideoFile(e) => RQElem::VideoFile(video_file::VideoFile::from(e)),\n            msg::elem::Elem::NotOnlineImage(e) => {\n                RQElem::FriendImage(friend_image::FriendImage::from(e))\n            }\n            msg::elem::Elem::CustomFace(e) => RQElem::GroupImage(group_image::GroupImage::from(e)),\n            _ => RQElem::Other(Box::new(elem)),\n        }\n    }\n}\n\nimpl fmt::Display for RQElem {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match self {\n            RQElem::At(e) => fmt::Display::fmt(e, f),\n            RQElem::Text(e) => fmt::Display::fmt(e, f),\n            RQElem::Face(e) => fmt::Display::fmt(e, f),\n            RQElem::GroupImage(e) => fmt::Display::fmt(e, f),\n            RQElem::FriendImage(e) => fmt::Display::fmt(e, f),\n            RQElem::FlashImage(e) => fmt::Display::fmt(e, f),\n            RQElem::LightApp(e) => fmt::Display::fmt(e, f),\n            RQElem::RichMsg(e) => fmt::Display::fmt(e, f),\n            _ => return Ok(()),\n        }?;\n        f.write_str(\" \")\n    }\n}\n\n/// Extract a field from xml / json and write to formatter.\nfn fmt_extract_attr(\n    f: &mut fmt::Formatter,\n    i: &str,\n    name: &str,\n    begin: &str,\n    end: &str,\n) -> fmt::Result {\n    if let Some(v) = i\n        .rsplit_once(begin)\n        .and_then(|v| v.1.split_once(end))\n        .map(|v| v.0)\n    {\n        write!(f, \" {name}='{v}'\")?;\n    }\n    Ok(())\n}\n\nmacro_rules! impl_from {\n    ($key: tt, $fty: ty) => {\n        impl From<$fty> for RQElem {\n            fn from(e: $fty) -> Self {\n                RQElem::$key(e)\n            }\n        }\n    };\n}\n\nimpl_from!(At, at::At);\nimpl_from!(Text, text::Text);\nimpl_from!(Face, face::Face);\nimpl_from!(MarketFace, market_face::MarketFace);\nimpl_from!(Dice, market_face::Dice);\nimpl_from!(FingerGuessing, market_face::FingerGuessing);\nimpl_from!(LightApp, light_app::LightApp);\nimpl_from!(RichMsg, rich_msg::RichMsg);\nimpl_from!(FriendImage, friend_image::FriendImage);\nimpl_from!(GroupImage, group_image::GroupImage);\nimpl_from!(FlashImage, flash_image::FlashImage);\nimpl_from!(Other, Box<msg::elem::Elem>);\n\nimpl From<String> for RQElem {\n    fn from(s: String) -> Self {\n        RQElem::Text(text::Text::new(s))\n    }\n}\n\nimpl From<&str> for RQElem {\n    fn from(s: &str) -> Self {\n        RQElem::Text(text::Text::new(s.to_string()))\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/msg/elem/reply.rs",
    "content": "use std::fmt;\n\nuse crate::msg::{MessageChainBuilder, MessageElem, PushBuilder};\nuse crate::pb::msg;\n\nuse super::super::MessageChain;\n\n#[derive(Default, Debug, Clone)]\npub struct Reply {\n    pub reply_seq: i32,\n    pub sender: i64,\n    pub time: i32,\n    pub elements: MessageChain,\n}\n\nimpl From<Reply> for MessageElem {\n    fn from(e: Reply) -> Self {\n        MessageElem::SrcMsg(msg::SourceMsg {\n            orig_seqs: vec![e.reply_seq],\n            sender_uin: Some(e.sender),\n            time: Some(e.time),\n            flag: Some(1),\n            elems: e.elements.into(),\n            rich_msg: Some(vec![]),\n            pb_reserve: Some(vec![]),\n            src_msg: Some(vec![]),\n            troop_name: Some(vec![]),\n            ..Default::default()\n        })\n    }\n}\n\nimpl PushBuilder for Reply {\n    fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {\n        let index = if let Some(MessageElem::AnonGroupMsg(..)) = builder.elems.get(0) {\n            1\n        } else {\n            0\n        };\n        builder.elems.insert(index, elem.into());\n    }\n}\n\nimpl From<msg::SourceMsg> for Reply {\n    fn from(e: msg::SourceMsg) -> Self {\n        Self {\n            reply_seq: e.orig_seqs[0],\n            time: e.time(),\n            sender: e.sender_uin(),\n            elements: MessageChain::from(e.elems),\n        }\n    }\n}\n\nimpl fmt::Display for Reply {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"[Reply: {}]\", self.reply_seq)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/msg/elem/rich_msg.rs",
    "content": "use std::fmt;\nuse std::io::{Read, Write};\n\nuse flate2::read::ZlibDecoder;\nuse flate2::write::ZlibEncoder;\nuse flate2::Compression;\n\nuse super::fmt_extract_attr;\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n#[derive(Default, Debug, Clone)]\npub struct RichMsg {\n    pub service_id: i32,\n    pub template1: String,\n}\n\nimpl From<msg::RichMsg> for RichMsg {\n    fn from(e: msg::RichMsg) -> Self {\n        let data = e.template1.unwrap_or_default();\n        if data.len() > 1 {\n            let content = if data[0] == 0 {\n                data[1..].to_vec()\n            } else {\n                let mut uncompressed = Vec::new();\n                ZlibDecoder::new(&data[1..])\n                    .read_to_end(&mut uncompressed)\n                    .ok();\n                uncompressed\n            };\n            if !content.is_empty() && content.len() < 1024 ^ 3 {\n                return Self {\n                    service_id: e.service_id.unwrap_or_default(),\n                    template1: String::from_utf8_lossy(&content).into_owned(),\n                };\n            }\n        }\n        Self::default()\n    }\n}\n\nimpl PushElem for RichMsg {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::RichMsg(msg::RichMsg {\n            template1: Some({\n                let mut encoder = ZlibEncoder::new(vec![1], Compression::default());\n                encoder.write_all(elem.template1.as_bytes()).ok();\n                encoder.finish().unwrap_or_default()\n            }),\n            service_id: Some(elem.service_id),\n            ..Default::default()\n        }));\n    }\n}\n\nimpl fmt::Display for RichMsg {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(\"[RichMsg:\")?;\n        fmt_extract_attr(f, &self.template1, \"brief\", \" brief=\\\"\", \"\\\"\")?;\n        fmt_extract_attr(f, &self.template1, \"title\", \"<title>\", \"</title>\")?;\n        fmt_extract_attr(f, &self.template1, \"summary\", \"<summary>\", \"</summary>\")?;\n        fmt_extract_attr(f, &self.template1, \"url\", \" url=\\\"\", \"\\\"\")?;\n        fmt_extract_attr(f, &self.template1, \"name\", \" name=\\\"\", \"\\\"\")?;\n        f.write_str(\"]\")\n    }\n}\n\nto_elem_vec_impl!(RichMsg);\npush_builder_impl!(RichMsg);\n"
  },
  {
    "path": "ricq-core/src/msg/elem/text.rs",
    "content": "use std::{fmt, mem};\n\nuse crate::msg::{MessageChainBuilder, MessageElem, PushBuilder, PushElem};\nuse crate::pb::msg;\nuse crate::to_elem_vec_impl;\n\n#[derive(Default, Debug, Clone)]\npub struct Text {\n    pub content: String,\n}\n\nimpl Text {\n    pub fn new(content: String) -> Self {\n        Self { content }\n    }\n}\n\nto_elem_vec_impl!(Text);\n\nimpl From<msg::Text> for Text {\n    fn from(e: msg::Text) -> Self {\n        Self {\n            content: e.str.unwrap_or_default(),\n        }\n    }\n}\n\nimpl PushElem for Text {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::Text(msg::Text {\n            str: Some(elem.content),\n            ..Default::default()\n        }));\n    }\n}\n\nimpl PushBuilder for Text {\n    fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {\n        builder.buf_string.push_str(&elem.content);\n    }\n}\n\npub fn flush_builder(builder: &mut MessageChainBuilder) {\n    if !builder.buf_string.is_empty() {\n        let s = mem::take(&mut builder.buf_string);\n        builder.elems.push(MessageElem::Text(msg::Text {\n            str: Some(s),\n            ..Default::default()\n        }));\n    }\n}\n\nimpl fmt::Display for Text {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.content)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/msg/elem/video_file.rs",
    "content": "use std::fmt;\n\nuse crate::hex::encode_hex;\nuse crate::msg::{MessageChainBuilder, PushBuilder};\nuse crate::msg::{MessageElem, PushElem};\nuse crate::pb::msg;\nuse crate::{push_builder_impl, to_elem_vec_impl};\n\n#[derive(Default, Debug, Clone)]\npub struct VideoFile {\n    pub name: String,\n    pub uuid: Vec<u8>,\n    pub size: i32,\n    pub thumb_size: i32,\n    pub md5: Vec<u8>,\n    pub thumb_md5: Vec<u8>,\n}\n\nimpl From<msg::VideoFile> for VideoFile {\n    fn from(e: msg::VideoFile) -> Self {\n        Self {\n            name: e.file_name.unwrap_or_default(),\n            uuid: e.file_uuid.unwrap_or_default(),\n            size: e.file_size.unwrap_or_default(),\n            thumb_size: e.thumb_file_size.unwrap_or_default(),\n            md5: e.file_md5.unwrap_or_default(),\n            thumb_md5: e.thumb_file_md5.unwrap_or_default(),\n        }\n    }\n}\n\nimpl PushElem for VideoFile {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::Text(msg::Text {\n            str: Some(\"你的QQ暂不支持查看视频短片，请期待后续版本。\".into()),\n            ..Default::default()\n        }));\n        vec.push(MessageElem::VideoFile(msg::VideoFile {\n            file_uuid: Some(elem.uuid),\n            file_name: Some(format!(\"{}.mp4\", encode_hex(&elem.md5))),\n            file_md5: Some(elem.md5),\n            file_format: Some(3),\n            file_time: Some(10),\n            file_size: Some(elem.size),\n            thumb_width: Some(1280),\n            thumb_height: Some(720),\n            thumb_file_md5: Some(elem.thumb_md5),\n            thumb_file_size: Some(elem.thumb_size),\n            busi_type: Some(0), // guild 4601\n            from_chat_type: Some(-1),\n            to_chat_type: Some(-1),\n            bool_support_progressive: Some(true),\n            file_width: Some(1280), // guild 0\n            file_height: Some(720), // guild 0\n            sub_busi_type: None,    // guild 4601\n            video_attr: None,       // guild 0\n            ..Default::default()\n        }));\n    }\n}\n\nimpl fmt::Display for VideoFile {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"[VideoFile: {}]\", self.name)\n    }\n}\n\nto_elem_vec_impl!(VideoFile);\npush_builder_impl!(VideoFile);\n"
  },
  {
    "path": "ricq-core/src/msg/fragment.rs",
    "content": "use crate::pb::msg;\n\nuse super::MessageChain;\n\nimpl MessageChain {\n    // TODO test\n    // https://github.com/mamoe/mirai/blob/dev/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt#L68\n    pub fn fragment(mut self) -> Vec<MessageChain> {\n        let mut results = vec![];\n        let mut txt_add = false;\n        let mut last = vec![];\n        fn flush(\n            txt_add: &mut bool,\n            last: &mut Vec<msg::elem::Elem>,\n            results: &mut Vec<MessageChain>,\n        ) {\n            *txt_add = false;\n            if !last.is_empty() {\n                results.push(MessageChain(last.clone()));\n                last.clear()\n            }\n        }\n        self.0.iter_mut().for_each(|element| {\n            if last.len() >= 4 {\n                flush(&mut txt_add, &mut last, &mut results);\n            }\n            if let msg::elem::Elem::Text(t) = element {\n                if txt_add {\n                    flush(&mut txt_add, &mut last, &mut results);\n                }\n                if t.str.clone().unwrap_or_default().len() < 80 {\n                    txt_add = true;\n                    last.push(element.clone());\n                } else {\n                    flush(&mut txt_add, &mut last, &mut results);\n                    t.str\n                        .clone()\n                        .unwrap_or_default()\n                        .chars()\n                        .collect::<Vec<char>>()\n                        .chunks(80)\n                        .map(|c| c.iter().collect::<String>())\n                        .for_each(|s| {\n                            results.push(MessageChain(vec![msg::elem::Elem::Text(msg::Text {\n                                str: Some(s),\n                                ..Default::default()\n                            })]))\n                        });\n                }\n            } else {\n                last.push(element.clone());\n            }\n        });\n        flush(&mut txt_add, &mut last, &mut results);\n        results\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/msg/macros.rs",
    "content": "#[macro_export]\nmacro_rules! to_elem_vec_impl {\n    ($t:ty) => {\n        impl From<$t> for Vec<MessageElem> {\n            fn from(e: $t) -> Self {\n                let mut vec = vec![];\n                <$t>::push_to(e, &mut vec);\n                vec\n            }\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! push_builder_impl {\n    ($t:ty) => {\n        impl PushBuilder for $t {\n            fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {\n                builder.flush();\n                Self::push_to(elem, &mut builder.elems);\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "ricq-core/src/msg/mod.rs",
    "content": "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 MessageElem = msg::elem::Elem;\n\n/// [`MessageChain`]消息链, 用于发送消息\n///\n///\n/// ## 示例\n///\n/// ```rust\n/// use ricq_core::msg::elem::{At, Text};\n/// use ricq_core::msg::MessageChain;\n/// let mut chain = MessageChain::default();\n/// chain.push(Text::new(String::from(\"Hello\")));\n/// chain.push(At::new(12345));\n/// chain.push(Text::new(String::from(\"world\")));\n/// ```\n///\n/// 另请参阅: [`MessageChainBuilder`]\n///\n#[derive(Debug, Default, Clone)]\npub struct MessageChain(pub Vec<MessageElem>);\n\nimpl MessageChain {\n    /// 从消息元素构造[`MessageChain`]\n    ///\n    /// ## 示例\n    ///\n    /// ```rust\n    /// use ricq_core::msg::elem::Text;\n    /// use ricq_core::msg::MessageChain;\n    /// let chain = MessageChain::new(Text::new(String::from(\"Hello world!\")));\n    /// ```\n    ///\n    pub fn new<E: Into<Vec<MessageElem>>>(e: E) -> Self {\n        Self(e.into())\n    }\n\n    /// 将消息元素添加至[`MessageChain`]\n    ///\n    /// ## 示例\n    ///\n    /// ```rust\n    /// use ricq_core::msg::elem::Text;\n    /// use ricq_core::msg::MessageChain;\n    /// let mut chain = MessageChain::default();\n    /// chain.push(Text::new(String::from(\"Hello\")));\n    /// ```\n    ///\n    pub fn push<E: Into<Vec<MessageElem>>>(&mut self, e: E) {\n        self.0.extend(e.into())\n    }\n\n    pub fn anonymous(&self) -> Option<Anonymous> {\n        self.0.iter().find_map(|e| match e {\n            MessageElem::AnonGroupMsg(anonymous) => Some(Anonymous::from(anonymous.clone())),\n            _ => None,\n        })\n    }\n\n    /// 获取此[`MessageChain`]中的引用回复\n    pub fn reply(&self) -> Option<Reply> {\n        self.0.iter().find_map(|e| match e {\n            MessageElem::SrcMsg(src_msg) => Some(Reply::from(src_msg.clone())),\n            _ => None,\n        })\n    }\n\n    pub fn with_anonymous(&mut self, anonymous: Anonymous) {\n        self.0.insert(0, MessageElem::from(anonymous))\n    }\n\n    /// 添加引用回复\n    pub fn with_reply(&mut self, reply: Reply) {\n        let index = if self.anonymous().is_some() { 1 } else { 0 };\n        self.0.insert(index, MessageElem::from(reply))\n    }\n}\n\nimpl<E> FromIterator<E> for MessageChain\nwhere\n    E: Into<Vec<MessageElem>>,\n{\n    fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {\n        Self(iter.into_iter().flat_map(Into::into).collect())\n    }\n}\n\nimpl IntoIterator for MessageChain {\n    type Item = RQElem;\n    type IntoIter = impl Iterator<Item = RQElem> + 'static;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0\n            .into_iter()\n            .filter(|e| !matches!(e, MessageElem::SrcMsg(_) | MessageElem::AnonGroupMsg(_)))\n            .map(RQElem::from)\n    }\n}\n\nimpl From<Vec<msg::Elem>> for MessageChain {\n    fn from(elements: Vec<msg::Elem>) -> Self {\n        Self(elements.into_iter().filter_map(|e| e.elem).collect())\n    }\n}\n\nimpl From<MessageChain> for Vec<msg::Elem> {\n    fn from(e: MessageChain) -> Self {\n        e.0.into_iter()\n            .map(|e| msg::Elem { elem: Some(e) })\n            .collect()\n    }\n}\n\nimpl fmt::Display for MessageChain {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        for x in self.clone().into_iter() {\n            fmt::Display::fmt(&x, f)?\n        }\n        Ok(())\n    }\n}\n\n/// [`MessageChain`]构造器\n///\n///\n/// ## 示例\n///\n/// ```rust\n/// use ricq_core::msg::elem::At;\n/// use ricq_core::msg::MessageChainBuilder;\n/// let mut builder = MessageChainBuilder::new();\n/// builder.push_str(\"Hello\")\n///     .push(At::new(12345))\n///     .push_str(\"world\");\n/// let chain = builder.build();\n/// ```\n///\n#[derive(Debug, Default, Clone)]\npub struct MessageChainBuilder {\n    pub elems: Vec<MessageElem>,\n    pub buf_string: String,\n}\n\nimpl MessageChainBuilder {\n    /// 创建一个新的[`MessageChainBuilder`]\n    ///\n    /// ## 示例\n    /// ```rust\n    /// use ricq_core::msg::MessageChainBuilder;\n    /// let mut builder = MessageChainBuilder::new();\n    /// ```\n    ///\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// 为当前[`MessageChainBuilder`]添加一个消息元素\n    ///\n    /// ## 示例\n    /// ```rust\n    /// use ricq_core::msg::elem::Text;\n    /// use ricq_core::msg::MessageChainBuilder;\n    /// let mut builder = MessageChainBuilder::new();\n    /// builder.push(Text::new(String::from(\"Hello world!\")));\n    /// ```\n    ///\n    pub fn push<E: PushBuilder>(&mut self, elem: E) -> &mut Self {\n        E::push_builder(elem, self);\n        self\n    }\n\n    /// 向当前[`MessageChainBuilder`]的添加一段字符串\n    ///\n    /// 本函数会将字符串直接添加于[`MessageChainBuilder`]内部的字符串缓存，在每次push其他元素时刷新\n    ///\n    /// ## 示例\n    /// ```rust\n    /// use ricq_core::msg::MessageChainBuilder;\n    /// let mut builder = MessageChainBuilder::new();\n    /// builder.push_str(\"Hello world!\");\n    /// ```\n    ///\n    pub fn push_str(&mut self, str: &str) -> &mut Self {\n        self.buf_string.push_str(str);\n        self\n    }\n\n    /// 将此[`MessageChainBuilder`]构造为[`MessageChain`]\n    ///\n    /// ## 示例\n    /// ```rust\n    /// use ricq_core::msg::{MessageChain, MessageChainBuilder};\n    /// let mut builder = MessageChainBuilder::new();\n    /// let chain: MessageChain = builder.build();\n    /// ```\n    ///\n    pub fn build(mut self) -> MessageChain {\n        self.flush();\n        MessageChain::new(self.elems)\n    }\n\n    /// 清空内部字符串缓存, 将其构造为消息元素\n    fn flush(&mut self) {\n        flush_builder(self);\n    }\n}\n\npub trait PushElem {\n    fn push_to(elem: Self, vec: &mut Vec<MessageElem>);\n}\n\npub trait PushBuilder {\n    fn push_builder(elem: Self, builder: &mut MessageChainBuilder);\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_iter() {\n        let mut chain = MessageChain::default();\n        chain.push(Text::new(\"hello\".into()));\n        for e in chain.into_iter() {\n            println!(\"{e:?}\")\n        }\n    }\n\n    #[test]\n    fn test_display() {\n        let mut chain = MessageChain::default();\n        chain.with_anonymous(Anonymous::default());\n        chain.with_reply(Reply::default());\n        chain.push(Text::new(\"hello\".into()));\n        chain.push(At::new(12345));\n        chain.push(Text::new(\"world\".into()));\n        chain.push(Face::new(1));\n        chain.push(Dice::new(1));\n        chain.push(FingerGuessing::Rock);\n        chain.push(MarketFace {\n            name: \"xx\".into(),\n            ..Default::default()\n        });\n        chain.push(LightApp::new(\"{}\".into()));\n        println!(\"{chain}\");\n        println!(\"{:?}\", chain.reply());\n        println!(\"{:?}\", chain.anonymous());\n        for item in chain {\n            println!(\"{item:?}\")\n        }\n    }\n\n    #[test]\n    fn test_builder() {\n        let mut builder = MessageChainBuilder::new();\n        builder\n            .push(Anonymous::default())\n            .push(Reply::default())\n            .push_str(\"hello\")\n            .push(At::new(12345))\n            .push_str(\"world\")\n            .push(Face::new(1))\n            .push(Dice::new(1))\n            .push(Text::new(\"hello\".into()))\n            .push_str(\"world2\")\n            .push(FingerGuessing::Rock)\n            .push(MarketFace {\n                name: \"xx\".into(),\n                ..Default::default()\n            })\n            .push(LightApp::new(\"{}\".into()));\n\n        let chain = builder.build();\n        println!(\"{chain}\");\n        assert!(chain.reply().is_some());\n        assert!(chain.anonymous().is_some());\n        for item in chain {\n            println!(\"{item:?}\")\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/pb/cmd0x346/cmd0x346.proto",
    "content": "syntax = \"proto3\";\n\npackage cmd0x346;\n\nmessage ApplyCleanTrafficRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n}\nmessage ApplyCopyFromReq {\n  int64 srcUin = 10;\n  int64 srcGroup = 20;\n  int32 srcSvcid = 30;\n  bytes srcParentfolder = 40;\n  bytes srcUuid = 50;\n  bytes fileMd5 = 60;\n  int64 dstUin = 70;\n  int64 fileSize = 80;\n  string fileName = 90;\n  int32 dangerLevel = 100;\n  int64 totalSpace = 110;\n}\nmessage ApplyCopyFromRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  bytes uuid = 30;\n  int64 totalSpace = 40;\n}\nmessage ApplyCopyToReq {\n  int64 dstId = 10;\n  int64 dstUin = 20;\n  int32 dstSvcid = 30;\n  int64 srcUin = 40;\n  int64 fileSize = 50;\n  string fileName = 60;\n  string localFilepath = 70;\n  bytes uuid = 80;\n}\nmessage ApplyCopyToRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  string fileKey = 30;\n}\nmessage ApplyDownloadAbsReq {\n  int64 uin = 10;\n  bytes uuid = 20;\n}\nmessage ApplyDownloadAbsRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  DownloadInfo downloadInfo = 30;\n}\nmessage ApplyDownloadReq {\n  int64 uin = 10;\n  bytes uuid = 20;\n  int32 ownerType = 30;\n  int32 extIntype = 500;\n  int32 need_https_url = 501;\n}\nmessage ApplyDownloadRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  DownloadInfo downloadInfo = 30;\n  FileInfo fileInfo = 40;\n}\nmessage ApplyForwardFileReq {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  bytes uuid = 30;\n  int32 dangerLevel = 40;\n  int64 totalSpace = 50;\n}\nmessage ApplyForwardFileRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 totalSpace = 30;\n  int64 usedSpace = 40;\n  bytes uuid = 50;\n}\nmessage ApplyGetTrafficReq {\n}\nmessage ApplyGetTrafficRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 useFileSize = 30;\n  int32 useFileNum = 40;\n  int64 allFileSize = 50;\n  int32 allFileNum = 60;\n}\nmessage ApplyListDownloadReq {\n  int64 uin = 10;\n  int32 beginIndex = 20;\n  int32 reqCount = 30;\n}\nmessage ApplyListDownloadRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int32 totalCount = 30;\n  int32 beginIndex = 40;\n  int32 rspCount = 50;\n  int32 isEnd = 60;\n  repeated FileInfo fileList = 70;\n}\nmessage ApplyUploadHitReq {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  string localFilepath = 60;\n  int32 dangerLevel = 70;\n  int64 totalSpace = 80;\n}\nmessage ApplyUploadHitReqV2 {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  bytes bytes_3sha = 60;\n  bytes sha = 70;\n  string localFilepath = 80;\n  int32 dangerLevel = 90;\n  int64 totalSpace = 100;\n}\nmessage ApplyUploadHitReqV3 {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  bytes sha = 60;\n  string localFilepath = 70;\n  int32 dangerLevel = 80;\n  int64 totalSpace = 90;\n}\nmessage ApplyUploadHitRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  string uploadIp = 30;\n  int32 uploadPort = 40;\n  string uploadDomain = 50;\n  bytes uuid = 60;\n  bytes uploadKey = 70;\n  int64 totalSpace = 80;\n  int64 usedSpace = 90;\n}\nmessage ApplyUploadHitRspV2 {\n  int32 retCode = 10;\n  string retMsg = 20;\n  string uploadIp = 30;\n  int32 uploadPort = 40;\n  string uploadDomain = 50;\n  bytes uuid = 60;\n  bytes uploadKey = 70;\n  int64 totalSpace = 80;\n  int64 usedSpace = 90;\n}\nmessage ApplyUploadHitRspV3 {\n  int32 retCode = 10;\n  string retMsg = 20;\n  string uploadIp = 30;\n  int32 uploadPort = 40;\n  string uploadDomain = 50;\n  bytes uuid = 60;\n  bytes uploadKey = 70;\n  int64 totalSpace = 80;\n  int64 usedSpace = 90;\n}\nmessage ApplyUploadReq {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int32 fileType = 30;\n  int64 fileSize = 40;\n  string fileName = 50;\n  bytes bytes_10mMd5 = 60;\n  string localFilepath = 70;\n  int32 dangerLevel = 80;\n  int64 totalSpace = 90;\n}\nmessage ApplyUploadReqV2 {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  bytes bytes_3sha = 60;\n  string localFilepath = 70;\n  int32 dangerLevel = 80;\n  int64 totalSpace = 90;\n}\nmessage ApplyUploadReqV3 {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  bytes sha = 60;\n  string localFilepath = 70;\n  int32 dangerLevel = 80;\n  int64 totalSpace = 90;\n}\nmessage ApplyUploadRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 totalSpace = 30;\n  int64 usedSpace = 40;\n  int64 uploadedSize = 50;\n  string uploadIp = 60;\n  string uploadDomain = 70;\n  int32 uploadPort = 80;\n  bytes uuid = 90;\n  bytes uploadKey = 100;\n  bool boolFileExist = 110;\n  int32 packSize = 120;\n  repeated string uploadipList = 130;\n}\nmessage ApplyUploadRspV2 {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 totalSpace = 30;\n  int64 usedSpace = 40;\n  int64 uploadedSize = 50;\n  string uploadIp = 60;\n  string uploadDomain = 70;\n  int32 uploadPort = 80;\n  bytes uuid = 90;\n  bytes uploadKey = 100;\n  bool boolFileExist = 110;\n  int32 packSize = 120;\n  repeated string uploadipList = 130;\n  int32 httpsvrApiVer = 140;\n  bytes sha = 141;\n}\nmessage ApplyUploadRspV3 {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 totalSpace = 30;\n  int64 usedSpace = 40;\n  int64 uploadedSize = 50;\n  string uploadIp = 60;\n  string uploadDomain = 70;\n  int32 uploadPort = 80;\n  bytes uuid = 90;\n  bytes uploadKey = 100;\n  bool boolFileExist = 110;\n  int32 packSize = 120;\n  repeated string uploadIpList = 130;\n  int32 uploadHttpsPort = 140;\n  string uploadHttpsDomain = 150;\n  string uploadDns = 160;\n  string uploadLanip = 170;\n}\nmessage DelMessageReq {\n  int64 uinSender = 1;\n  int64 uinReceiver = 2;\n  int32 time = 10;\n  int32 random = 20;\n  int32 seqNo = 30;\n}\nmessage DeleteFileReq {\n  int64 uin = 10;\n  int64 peerUin = 20;\n  int32 deleteType = 30;\n  bytes uuid = 40;\n}\nmessage DeleteFileRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n}\nmessage DownloadInfo {\n  bytes downloadKey = 10;\n  string downloadIp = 20;\n  string downloadDomain = 30;\n  int32 port = 40;\n  string downloadUrl = 50;\n  repeated string downloadipList = 60;\n  string cookie = 70;\n}\nmessage DownloadSuccReq {\n  int64 uin = 10;\n  bytes uuid = 20;\n}\nmessage DownloadSuccRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int32 downStat = 30;\n}\nmessage ExtensionReq {\n  int64 id = 1;\n  int64 type = 2;\n  string dstPhonenum = 3;\n  int32 phoneConvertType = 4;\n  bytes sig = 20;\n  int64 routeId = 100;\n  DelMessageReq delMessageReq = 90100;\n  int32 downloadUrlType = 90200;\n  int32 pttFormat = 90300;\n  int32 isNeedInnerIp = 90400;\n  int32 netType = 90500;\n  int32 voiceType = 90600;\n  int32 fileType = 90700;\n  int32 pttTime = 90800;\n}\nmessage ExtensionRsp {\n}\nmessage FileInfo {\n  int64 uin = 1;\n  int32 dangerEvel = 2;\n  int64 fileSize = 3;\n  int32 lifeTime = 4;\n  int32 uploadTime = 5;\n  bytes uuid = 6;\n  string fileName = 7;\n  int32 absFileType = 90;\n  bytes bytes_10mMd5 = 100;\n  bytes sha = 101;\n  int32 clientType = 110;\n  int64 ownerUin = 120;\n  int64 peerUin = 121;\n  int32 expireTime = 130;\n}\nmessage FileQueryReq {\n  int64 uin = 10;\n  bytes uuid = 20;\n}\nmessage FileQueryRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  FileInfo fileInfo = 30;\n}\nmessage RecallFileReq {\n  int64 uin = 1;\n  bytes uuid = 2;\n}\nmessage RecallFileRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n}\nmessage RecvListQueryReq {\n  int64 uin = 1;\n  int32 beginIndex = 2;\n  int32 reqCount = 3;\n}\nmessage RecvListQueryRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n  int32 fileTotCount = 3;\n  int32 beginIndex = 4;\n  int32 rspFileCount = 5;\n  int32 isEnd = 6;\n  repeated FileInfo fileList = 7;\n}\nmessage RenewFileReq {\n  int64 uin = 1;\n  bytes uuid = 2;\n  int32 addTtl = 3;\n}\nmessage RenewFileRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n}\nmessage C346ReqBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  RecvListQueryReq recvListQueryReq = 3;\n  SendListQueryReq sendListQueryReq = 4;\n  RenewFileReq renewFileReq = 5;\n  RecallFileReq recallFileReq = 6;\n  ApplyUploadReq applyUploadReq = 7;\n  ApplyUploadHitReq applyUploadHitReq = 8;\n  ApplyForwardFileReq applyForwardFileReq = 9;\n  UploadSuccReq uploadSuccReq = 10;\n  DeleteFileReq deleteFileReq = 11;\n  DownloadSuccReq downloadSuccReq = 12;\n  ApplyDownloadAbsReq applyDownloadAbsReq = 13;\n  ApplyDownloadReq applyDownloadReq = 14;\n  ApplyListDownloadReq applyListDownloadReq = 15;\n  FileQueryReq fileQueryReq = 16;\n  ApplyCopyFromReq applyCopyFromReq = 17;\n  ApplyUploadReqV2 applyUploadReqV2 = 18;\n  ApplyUploadReqV3 applyUploadReqV3 = 19;\n  ApplyUploadHitReqV2 applyUploadHitReqV2 = 20;\n  ApplyUploadHitReqV3 applyUploadHitReqV3 = 21;\n  int32 businessId = 101;\n  int32 clientType = 102;\n  ApplyCopyToReq applyCopyToReq = 90000;\n  //ApplyCleanTrafficReq applyCleanTrafficReq = 90001; empty message\n  ApplyGetTrafficReq applyGetTrafficReq = 90002;\n  ExtensionReq extensionReq = 99999;\n}\nmessage C346RspBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  RecvListQueryRsp recvListQueryRsp = 3;\n  SendListQueryRsp sendListQueryRsp = 4;\n  RenewFileRsp renewFileRsp = 5;\n  RecallFileRsp recallFileRsp = 6;\n  ApplyUploadRsp applyUploadRsp = 7;\n  ApplyUploadHitRsp applyUploadHitRsp = 8;\n  ApplyForwardFileRsp applyForwardFileRsp = 9;\n  UploadSuccRsp uploadSuccRsp = 10;\n  DeleteFileRsp deleteFileRsp = 11;\n  DownloadSuccRsp downloadSuccRsp = 12;\n  ApplyDownloadAbsRsp applyDownloadAbsRsp = 13;\n  ApplyDownloadRsp applyDownloadRsp = 14;\n  ApplyListDownloadRsp applyListDownloadRsp = 15;\n  FileQueryRsp fileQueryRsp = 16;\n  ApplyCopyFromRsp applyCopyFromRsp = 17;\n  ApplyUploadRspV2 applyUploadRspV2 = 18;\n  ApplyUploadRspV3 applyUploadRspV3 = 19;\n  ApplyUploadHitRspV2 applyUploadHitRspV2 = 20;\n  ApplyUploadHitRspV3 applyUploadHitRspV3 = 21;\n  int32 businessId = 101;\n  int32 clientType = 102;\n  ApplyCopyToRsp applyCopyToRsp = 90000;\n  ApplyCleanTrafficRsp applyCleanTrafficRsp = 90001;\n  ApplyGetTrafficRsp applyGetTrafficRsp = 90002;\n  ExtensionRsp extensionRsp = 99999;\n\n}\nmessage SendListQueryReq {\n  int64 uin = 1;\n  int32 beginIndex = 2;\n  int32 reqCount = 3;\n}\nmessage SendListQueryRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n  int32 fileTotCount = 3;\n  int32 beginIndex = 4;\n  int32 rspFileCount = 5;\n  int32 isEnd = 6;\n  int64 totLimit = 7;\n  int64 usedLimit = 8;\n  repeated FileInfo fileList = 9;\n}\nmessage UploadSuccReq {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  bytes uuid = 30;\n}\nmessage UploadSuccRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  FileInfo fileInfo = 30;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/cmd0x352/cmd0x352.proto",
    "content": "syntax = \"proto2\";\n\npackage cmd0x352;\n/*\nmessage DelImgReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n  optional uint32 reqTerm = 3;\n  optional uint32 reqPlatformType = 4;\n  optional uint32 buType = 5;\n  optional bytes buildVer = 6;\n  optional bytes fileResid = 7;\n  optional uint32 picWidth = 8;\n  optional uint32 picHeight = 9;\n}\n\nmessage DelImgRsp {\n  optional uint32 result = 1;\n  optional bytes failMsg = 2;\n  optional bytes fileResid = 3;\n}\n\nmessage GetImgUrlReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n  optional bytes fileResid = 3;\n  optional uint32 urlFlag = 4;\n  optional uint32 urlType = 6;\n  optional uint32 reqTerm = 7;\n  optional uint32 reqPlatformType = 8;\n  optional uint32 srcFileType = 9;\n  optional uint32 innerIp = 10;\n  optional bool addressBook = 11;\n  optional uint32 buType = 12;\n  optional bytes buildVer = 13;\n  optional uint32 picUpTimestamp = 14;\n  optional uint32 reqTransferType = 15;\n}\n\nmessage GetImgUrlRsp {\n  optional bytes fileResid = 1;\n  optional uint32 clientIp = 2;\n  optional uint32 result = 3;\n  optional bytes failMsg = 4;\n  repeated bytes thumbDownUrl = 5;\n  repeated bytes originalDownUrl = 6;\n  optional ImgInfo imgInfo = 7;\n  repeated uint32 downIp = 8;\n  repeated uint32 downPort = 9;\n  optional bytes thumbDownPara = 10;\n  optional bytes originalDownPara = 11;\n  optional bytes downDomain = 12;\n  repeated bytes bigDownUrl = 13;\n  optional bytes bigDownPara = 14;\n  optional bytes bigThumbDownPara = 15;\n  optional uint32 httpsUrlFlag = 16;\n  repeated IPv6Info downIp6 = 26;\n  optional bytes clientIp6 = 27;\n}\n\nmessage IPv6Info {\n  optional bytes ip6 = 1;\n  optional uint32 port = 2;\n}\n*/\n\nmessage ReqBody {\n  optional uint32 subcmd = 1;\n  repeated D352TryUpImgReq tryupImgReq = 2;\n  // repeated GetImgUrlReq getimgUrlReq = 3;\n  // repeated DelImgReq delImgReq = 4;\n  optional uint32 netType = 10;\n}\n\nmessage RspBody {\n  optional uint32 subcmd = 1;\n  repeated TryUpImgRsp tryupImgRsp = 2;\n  // repeated GetImgUrlRsp getimgUrlRsp = 3;\n  optional bool newBigchan = 4;\n  // repeated DelImgRsp delImgRsp = 5;\n  optional bytes failMsg = 10;\n}\n\nmessage D352TryUpImgReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n  optional uint64 fileId = 3;\n  optional bytes fileMd5 = 4;\n  optional uint64 fileSize = 5;\n  optional bytes fileName = 6;\n  optional uint32 srcTerm = 7;\n  optional uint32 platformType = 8;\n  optional uint32 innerIp = 9;\n  optional bool addressBook = 10;\n  optional uint32 retry = 11;\n  optional uint32 buType = 12;\n  optional bool picOriginal = 13;\n  optional uint32 picWidth = 14;\n  optional uint32 picHeight = 15;\n  optional uint32 picType = 16;\n  optional bytes buildVer = 17;\n  optional bytes fileIndex = 18;\n  optional uint32 storeDays = 19;\n  optional uint32 tryupStepflag = 20;\n  optional bool rejectTryfast = 21;\n  optional uint32 srvUpload = 22;\n  optional bytes transferUrl = 23;\n}\n\nmessage TryUpImgRsp {\n  optional uint64 fileId = 1;\n  optional uint32 clientIp = 2;\n  optional uint32 result = 3;\n  optional bytes failMsg = 4;\n  optional bool fileExit = 5;\n  // optional ImgInfo imgInfo = 6;\n  repeated uint32 upIp = 7;\n  repeated uint32 upPort = 8;\n  optional bytes upUkey = 9;\n  optional string upResid = 10;\n  optional bytes upUuid = 11;\n  optional uint64 upOffset = 12;\n  optional uint64 blockSize = 13;\n  optional bytes encryptDstip = 14;\n  optional uint32 roamdays = 15;\n  // repeated IPv6Info upIp6 = 26;\n  optional bytes clientIp6 = 27;\n  optional bytes thumbDownPara = 60;\n  optional bytes originalDownPara = 61;\n  optional bytes downDomain = 62;\n  optional bytes bigDownPara = 64;\n  optional bytes bigThumbDownPara = 65;\n  optional uint32 httpsUrlFlag = 66;\n  // optional TryUpInfo4Busi info4Busi = 1001;\n}\n\n/*\nmessage TryUpInfo4Busi {\n  optional bytes fileResid = 1;\n  optional bytes downDomain = 2;\n  optional bytes thumbDownUrl = 3;\n  optional bytes originalDownUrl = 4;\n  optional bytes bigDownUrl = 5;\n}\n*/"
  },
  {
    "path": "ricq-core/src/pb/cmd0x388/cmd0x388.proto",
    "content": "syntax = \"proto2\";\n\npackage cmd0x388;\n\nmessage DelImgReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n  optional uint32 reqTerm = 3;\n  optional uint32 reqPlatformType = 4;\n  optional uint32 buType = 5;\n  optional bytes buildVer = 6;\n  optional bytes fileResid = 7;\n  optional uint32 picWidth = 8;\n  optional uint32 picHeight = 9;\n}\n\nmessage DelImgRsp {\n  optional uint32 result = 1;\n  optional bytes failMsg = 2;\n  optional bytes fileResid = 3;\n}\n\nmessage ExpRoamExtendInfo {\n  optional bytes resid = 1;\n}\n\nmessage ExpRoamPicInfo {\n  optional uint32 shopFlag = 1;\n  optional uint32 pkgId = 2;\n  optional bytes picId = 3;\n}\n\nmessage ExtensionCommPicTryUp {\n  repeated bytes extinfo = 1;\n}\n\nmessage ExtensionExpRoamTryUp {\n  repeated ExpRoamPicInfo exproamPicInfo = 1;\n}\n\nmessage GetImgUrlReq {\n  optional uint64 groupCode = 1;\n  optional uint64 dstUin = 2;\n  optional uint64 fileid = 3;\n  optional bytes fileMd5 = 4;\n  optional uint32 urlFlag = 5;\n  optional uint32 urlType = 6;\n  optional uint32 reqTerm = 7;\n  optional uint32 reqPlatformType = 8;\n  optional uint32 innerIp = 9;\n  optional uint32 buType = 10;\n  optional bytes buildVer = 11;\n  optional uint64 fileId = 12;\n  optional uint64 fileSize = 13;\n  optional uint32 originalPic = 14;\n  optional uint32 retryReq = 15;\n  optional uint32 fileHeight = 16;\n  optional uint32 fileWidth = 17;\n  optional uint32 picType = 18;\n  optional uint32 picUpTimestamp = 19;\n  optional uint32 reqTransferType = 20;\n  optional uint64 qqmeetGuildId = 21;\n  optional uint64 qqmeetChannelId = 22;\n  optional bytes downloadIndex = 23;\n}\n\nmessage GetImgUrlRsp {\n  optional uint64 fileid = 1;\n  optional bytes fileMd5 = 2;\n  optional uint32 result = 3;\n  optional bytes failMsg = 4;\n  optional ImgInfo imgInfo = 5;\n  repeated bytes thumbDownUrl = 6;\n  repeated bytes originalDownUrl = 7;\n  repeated bytes bigDownUrl = 8;\n  repeated uint32 downIp = 9;\n  repeated uint32 downPort = 10;\n  optional bytes downDomain = 11;\n  optional bytes thumbDownPara = 12;\n  optional bytes originalDownPara = 13;\n  optional bytes bigDownPara = 14;\n  optional uint64 fileId = 15;\n  optional uint32 autoDownType = 16;\n  repeated uint32 orderDownType = 17;\n  optional bytes bigThumbDownPara = 19;\n  optional uint32 httpsUrlFlag = 20;\n  repeated IPv6Info downIp6 = 26;\n  optional bytes clientIp6 = 27;\n}\n\nmessage GetPttUrlReq {\n  optional uint64 groupCode = 1;\n  optional uint64 dstUin = 2;\n  optional uint64 fileid = 3;\n  optional bytes fileMd5 = 4;\n  optional uint32 reqTerm = 5;\n  optional uint32 reqPlatformType = 6;\n  optional uint32 innerIp = 7;\n  optional uint32 buType = 8;\n  optional bytes buildVer = 9;\n  optional uint64 fileId = 10;\n  optional bytes fileKey = 11;\n  optional uint32 codec = 12;\n  optional uint32 buId = 13;\n  optional uint32 reqTransferType = 14;\n  optional uint32 isAuto = 15;\n}\n\nmessage GetPttUrlRsp {\n  optional uint64 fileid = 1;\n  optional bytes fileMd5 = 2;\n  optional uint32 result = 3;\n  optional bytes failMsg = 4;\n  repeated bytes downUrl = 5;\n  repeated uint32 downIp = 6;\n  repeated uint32 downPort = 7;\n  optional bytes downDomain = 8;\n  optional bytes downPara = 9;\n  optional uint64 fileId = 10;\n  optional uint32 transferType = 11;\n  optional uint32 allowRetry = 12;\n  repeated IPv6Info downIp6 = 26;\n  optional bytes clientIp6 = 27;\n  optional string domain = 28;\n}\n\nmessage IPv6Info {\n  optional bytes ip6 = 1;\n  optional uint32 port = 2;\n}\n\nmessage ImgInfo {\n  optional bytes fileMd5 = 1;\n  optional uint32 fileType = 2;\n  optional uint64 fileSize = 3;\n  optional uint32 fileWidth = 4;\n  optional uint32 fileHeight = 5;\n}\n\nmessage PicSize {\n  optional uint32 original = 1;\n  optional uint32 thumb = 2;\n  optional uint32 high = 3;\n}\n\nmessage D388ReqBody {\n  optional uint32 netType = 1;\n  optional uint32 subcmd = 2;\n  repeated TryUpImgReq tryupImgReq = 3;\n  repeated GetImgUrlReq getimgUrlReq = 4;\n  repeated TryUpPttReq tryupPttReq = 5;\n  repeated GetPttUrlReq getpttUrlReq = 6;\n  optional uint32 commandId = 7;\n  repeated DelImgReq delImgReq = 8;\n  optional bytes extension = 1001;\n}\n\nmessage D388RspBody {\n  optional uint32 clientIp = 1;\n  optional uint32 subcmd = 2;\n  repeated D388TryUpImgRsp tryupImgRsp = 3;\n  repeated GetImgUrlRsp getimgUrlRsp = 4;\n  repeated TryUpPttRsp tryupPttRsp = 5;\n  repeated GetPttUrlRsp getpttUrlRsp = 6;\n  repeated DelImgRsp delImgRsp = 7;\n}\n\nmessage TryUpImgReq {\n  optional uint64 groupCode = 1;\n  optional uint64 srcUin = 2;\n  optional uint64 fileId = 3;\n  optional bytes fileMd5 = 4;\n  optional uint64 fileSize = 5;\n  optional bytes fileName = 6;\n  optional uint32 srcTerm = 7;\n  optional uint32 platformType = 8;\n  optional uint32 buType = 9;\n  optional uint32 picWidth = 10;\n  optional uint32 picHeight = 11;\n  optional uint32 picType = 12;\n  optional bytes buildVer = 13;\n  optional uint32 innerIp = 14;\n  optional uint32 appPicType = 15;\n  optional uint32 originalPic = 16;\n  optional bytes fileIndex = 17;\n  optional uint64 dstUin = 18;\n  optional uint32 srvUpload = 19;\n  optional bytes transferUrl = 20;\n  optional uint64 qqmeetGuildId = 21;\n  optional uint64 qqmeetChannelId = 22;\n}\n\nmessage D388TryUpImgRsp {\n  optional uint64 fileId = 1;\n  optional uint32 result = 2;\n  optional bytes failMsg = 3;\n  optional bool fileExit = 4;\n  optional ImgInfo imgInfo = 5;\n  repeated uint32 upIp = 6;\n  repeated uint32 upPort = 7;\n  optional bytes upUkey = 8;\n  optional uint64 fileid = 9;\n  optional uint64 upOffset = 10;\n  optional uint64 blockSize = 11;\n  optional bool newBigChan = 12;\n  repeated IPv6Info upIp6 = 26;\n  optional bytes clientIp6 = 27;\n  optional bytes downloadIndex = 28;\n  optional TryUpInfo4Busi info4Busi = 1001;\n}\n\nmessage TryUpInfo4Busi {\n  optional bytes downDomain = 1;\n  optional bytes thumbDownUrl = 2;\n  optional bytes originalDownUrl = 3;\n  optional bytes bigDownUrl = 4;\n  optional bytes fileResid = 5;\n}\n\nmessage TryUpPttReq {\n  optional uint64 groupCode = 1;\n  optional uint64 srcUin = 2;\n  optional uint64 fileId = 3;\n  optional bytes fileMd5 = 4;\n  optional uint64 fileSize = 5;\n  optional bytes fileName = 6;\n  optional uint32 srcTerm = 7;\n  optional uint32 platformType = 8;\n  optional uint32 buType = 9;\n  optional bytes buildVer = 10;\n  optional uint32 innerIp = 11;\n  optional uint32 voiceLength = 12;\n  optional bool newUpChan = 13;\n  optional uint32 codec = 14;\n  optional uint32 voiceType = 15;\n  optional uint32 buId = 16;\n}\n\nmessage TryUpPttRsp {\n  optional uint64 fileId = 1;\n  optional uint32 result = 2;\n  optional bytes failMsg = 3;\n  optional bool fileExit = 4;\n  repeated uint32 upIp = 5;\n  repeated uint32 upPort = 6;\n  optional bytes upUkey = 7;\n  optional uint64 fileid = 8;\n  optional uint64 upOffset = 9;\n  optional uint64 blockSize = 10;\n  optional bytes fileKey = 11;\n  optional uint32 channelType = 12;\n  repeated IPv6Info upIp6 = 26;\n  optional bytes clientIp6 = 27;\n}\n\n"
  },
  {
    "path": "ricq-core/src/pb/cmd0x3bb/cmd0x3bb.proto",
    "content": "syntax = \"proto2\";\n\npackage cmd0x3bb;\n\nmessage AnonyMsg {\n  optional uint32 cmd = 1;\n  optional C3BBReqBody anonyReq = 10;\n  optional C3BBRspBody anonyRsp = 11;\n}\n\nmessage AnonyStatus {\n  optional uint32 forbidTalking = 1;\n  optional bytes errMsg = 10;\n}\n\nmessage C3BBReqBody {\n  optional uint64 uin = 1;\n  optional uint64 groupCode = 2;\n}\n\nmessage C3BBRspBody {\n  optional int32 ret = 1;\n  optional uint64 groupCode = 2;\n  optional bytes anonyName = 3;\n  optional uint32 portraitIndex = 4;\n  optional uint32 bubbleIndex = 5;\n  optional uint32 expiredTime = 6;\n  optional AnonyStatus anonyStatus = 10;\n  optional string color = 15;\n}"
  },
  {
    "path": "ricq-core/src/pb/cmd0x6ff/smbcmd0x519.proto",
    "content": "syntax = \"proto2\";\npackage cmd0x6ff;\n\nmessage C519CRMMsgHead {\n  optional uint32 crmSubCmd = 1;\n  optional uint32 headLen = 2;\n  optional uint32 verNo = 3;\n  optional uint64 kfUin = 4;\n  optional uint32 seq = 5;\n  optional uint32 packNum = 6;\n  optional uint32 curPack = 7;\n  optional string bufSig = 8;\n  optional uint64 pubQq = 9;\n  optional uint32 clienttype = 10;\n  optional uint64 laborUin = 11;\n  optional string laborName = 12;\n  optional uint64 puin = 13;\n}\n\nmessage GetNavigationMenuReqBody {\n  optional uint64 puin = 1;\n  optional uint64 uin = 2;\n  optional uint32 verNo = 3;\n}\n\nmessage GetNavigationMenuRspBody {\n  optional C519RetInfo ret = 1;\n  optional int32 isShow = 2;\n  optional string uctMsg = 3;\n  optional uint32 verNo = 4;\n}\n\nmessage C519ReqBody {\n  optional uint32 subCmd = 1;\n  optional C519CRMMsgHead crmCommonHead = 2;\n  optional GetAddressDetailListReqBody getAddressDetailListReqBody = 33;\n  optional GetNavigationMenuReqBody getNavigationMenuReq = 35;\n}\n\nmessage C519RetInfo {\n  optional uint32 retCode = 1;\n  optional string errorMsg = 2;\n}\n\nmessage C519RspBody {\n  optional uint32 subCmd = 1;\n  optional C519CRMMsgHead crmCommonHead = 2;\n  optional GetAddressDetailListRspBody getAddressDetailListRspBody = 33;\n  optional GetNavigationMenuRspBody getNavigationMenuRsp = 35;\n}\n\nmessage GetAddressDetailListReqBody {\n  optional fixed32 timestamp = 1;\n  optional fixed64 timestamp2 = 2;\n}\n\nmessage GetAddressDetailListRspBody {\n  optional C519RetInfo ret = 1;\n  optional fixed32 timestamp = 2;\n  optional bool full = 3;\n  repeated AddressDetail addressDetail = 4;\n  optional fixed64 timestamp2 = 5;\n}\n\nmessage AddressDetail {\n  optional uint32 aid = 1;\n  optional fixed32 modifyTime = 2;\n  optional fixed32 createTime = 3;\n  optional uint32 status = 4;\n  optional uint32 groupid = 5;\n  optional bytes addGroupName = 6;\n  optional bytes name = 7;\n  optional uint32 gender = 8;\n  optional fixed32 birthday = 9;\n  optional bytes company0 = 10;\n  optional bytes companyPosition0 = 11;\n  optional bytes company1 = 12;\n  optional bytes companyPosition1 = 13;\n  optional bytes fixedPhone0 = 14;\n  optional bytes fixedPhone1 = 15;\n  optional bytes email0 = 16;\n  optional bytes email1 = 17;\n  optional bytes fax0 = 18;\n  optional bytes fax1 = 19;\n  optional bytes comment = 20;\n  optional bytes headUrl = 21;\n  repeated AddressMobileInfo mobilePhone = 22;\n  optional bool mobilePhoneUpdated = 23;\n  repeated AddressQQinfo qq = 24;\n  optional bool qqPhoneUpdated = 25;\n  optional fixed64 modifyTime2 = 26;\n  optional NewBizClientRegion clientRegion = 27;\n  optional NewBizClientRegionCode clientRegionCode = 28;\n}\n\nmessage AddressMobileInfo {\n  optional uint32 index = 1;\n  optional bytes account = 2;\n  optional bytes formattedAccount = 5;\n}\n\nmessage AddressQQinfo {\n  optional uint32 index = 1;\n  optional uint64 account = 2;\n}\n\nmessage NewBizClientRegion {\n  optional string clientNation = 1;\n  optional string clientProvince = 2;\n  optional string clientCity = 3;\n  optional string clientRegion = 4;\n}\n\nmessage NewBizClientRegionCode {\n  optional uint64 nationid = 1;\n  optional uint64 provinceid = 2;\n  optional uint64 cityid = 3;\n  optional uint64 regionid = 4;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/cmd0x6ff/subcmd0x501.proto",
    "content": "syntax = \"proto2\";\npackage cmd0x6ff;\n\nmessage C501ReqBody {\n  optional SubCmd0x501ReqBody ReqBody = 1281;\n}\nmessage C501RspBody {\n  optional SubCmd0x501RspBody RspBody = 1281;\n}\nmessage SubCmd0x501ReqBody {\n  optional uint64 uin = 1;\n  optional uint32 idcId = 2;\n  optional uint32 appid = 3;\n  optional uint32 loginSigType = 4;\n  optional bytes loginSigTicket = 5;\n  optional uint32 requestFlag = 6;\n  repeated uint32 serviceTypes = 7;\n  optional uint32 bid = 8;\n}\nmessage SubCmd0x501RspBody {\n  optional bytes sigSession = 1;\n  optional bytes sessionKey = 2;\n  repeated SrvAddrs addrs = 3;\n}\n\nmessage SrvAddrs {\n  optional uint32 serviceType = 1;\n  repeated IpAddr addrs = 2;\n}\n\nmessage IpAddr {\n  optional uint32 type = 1;\n  optional fixed32 ip = 2;\n  optional uint32 port = 3;\n  optional uint32 area = 4;\n}\n\n"
  },
  {
    "path": "ricq-core/src/pb/cmd0x899/cmd0x899.proto",
    "content": "syntax = \"proto2\";\n\npackage cmd0x899;\n\nmessage ReqBody {\n  optional uint64 groupCode = 1;\n  optional uint64 startUin = 2;\n  optional uint32 identifyFlag = 3;\n  repeated uint64 uinList = 4;\n  optional memberlist memberlistOpt = 5;\n  optional uint32 memberNum = 6;\n  optional uint32 filterMethod = 7;\n  optional uint32 onlineFlag = 8;\n}\n\nmessage RspBody {\n  optional uint64 groupCode = 1;\n  optional uint64 startUin = 2;\n  optional uint32 identifyFlag = 3;\n  repeated memberlist memberlist = 4;\n  optional bytes errorinfo = 5;\n}\n\nmessage memberlist {\n  optional uint64 memberUin = 1;\n  optional uint32 uinFlag = 2;\n  optional uint32 uinFlagex = 3;\n  optional uint32 uinMobileFlag = 4;\n  optional uint32 uinArchFlag = 5;\n  optional uint32 joinTime = 6;\n  optional uint32 oldMsgSeq = 7;\n  optional uint32 newMsgSeq = 8;\n  optional uint32 lastSpeakTime = 9;\n  optional uint32 level = 10;\n  optional uint32 point = 11;\n  optional uint32 shutupTimestap = 12;\n  optional uint32 flagex2 = 13;\n  optional bytes specialTitle = 14;\n  optional uint32 specialTitleExpireTime = 15;\n  optional uint32 activeDay = 16;\n  optional bytes uinKey = 17;\n  optional uint32 privilege = 18;\n  optional bytes richInfo = 19;\n}\n\nmessage uin_key {\n  optional uint64 groupCode = 1;\n  optional uint64 memberUin = 2;\n  optional uint64 genTime = 3;\n  optional uint32 validTime = 4;\n  optional uint32 randNum = 5;\n}\n\n"
  },
  {
    "path": "ricq-core/src/pb/data.proto",
    "content": "syntax = \"proto3\";\n\npackage pb;\n\nmessage SSOReserveField {\n  int32 flag = 9;\n  string qimei = 12;\n  int32 newconn_flag = 14;\n  string uid = 16;\n  int32 imsi = 18;\n  int32 network_type = 19;\n  int32 ip_stack_type = 20;\n  int32 message_type = 21;\n  SsoSecureInfo sec_info = 24;\n  int32 sso_ip_origin = 28;\n}\n\nmessage SsoSecureInfo {\n  bytes sec_sig = 1;\n  bytes sec_device_token = 2;\n  bytes sec_extra = 3;\n}\n\nmessage DeviceInfo {\n  string bootloader = 1;\n  string procVersion = 2;\n  string codename = 3;\n  string incremental = 4;\n  string fingerprint = 5;\n  string bootId = 6;\n  string androidId = 7;\n  string baseBand = 8;\n  string innerVersion = 9;\n}\n\nmessage RequestBody {\n  repeated ConfigSeq rpt_config_list = 1;\n}\n\nmessage ConfigSeq {\n  int32 type = 1;\n  int32 version = 2;\n}\n\nmessage D50ReqBody {\n  int64 appid = 1;\n  int32 maxPkgSize = 2;\n  int32 startTime = 3;\n  int32 startIndex = 4;\n  int32 reqNum = 5;\n  repeated int64 uinList = 6;\n  int32 reqMusicSwitch = 91001;\n  int32 reqMutualmarkAlienation = 101001;\n  int32 reqMutualmarkScore = 141001;\n  int32 reqKsingSwitch = 151001;\n  int32 reqMutualmarkLbsshare = 181001;\n}\n\nmessage D388ReqBody {\n  int32 netType = 1;\n  int32 subcmd = 2;\n  repeated TryUpImgReq msgTryUpImgReq = 3;\n  repeated TryUpPttReq msgTryUpPttReq = 5;\n  repeated GetPttUrlReq msgGetPttReq = 6;\n  int32 commandId = 7;\n  bytes extension = 1001;\n}\n\nmessage D388RespBody {\n  int32 clientIp = 1;\n  int32 subCmd = 2;\n  repeated TryUpImgResp msgTryUpImgRsp = 3;\n  repeated TryUpPttResp msgTryUpPttRsp = 5;\n  repeated GetPttUrlRsp msgGetPttUrlRsp = 6;\n}\n\nmessage GetPttUrlReq {\n  int64 groupCode = 1;\n  int64 dstUin = 2;\n  int64 fileId = 3;\n  bytes fileMd5 = 4;\n  int32 reqTerm = 5;\n  int32 reqPlatformType = 6;\n  int32 innerIp = 7;\n  int32 buType = 8;\n  bytes buildVer = 9;\n  //int64 fileId = 10;\n  bytes fileKey = 11;\n  int32 codec = 12;\n  int32 buId = 13;\n  int32 reqTransferType = 14;\n  int32 isAuto = 15;\n}\n\nmessage GetPttUrlRsp {\n  int64 fileId = 1;\n  bytes fileMd5 = 2;\n  int32 result = 3;\n  bytes failMsg = 4;\n  bytes bytesDownUrl = 5;\n  repeated int32 uint32DownIp = 6;\n  repeated int32 uint32DownPort = 7;\n  bytes downDomain = 8;\n  bytes downPara = 9;\n  //int64 fileId = 10;\n  int32 transferType = 11;\n  int32 allowRetry = 12;\n  //repeated IPv6Info msgDownIp6 = 26;\n  bytes clientIp6 = 27;\n  string strDomain = 28;\n}\n\nmessage ReqDataHighwayHead {\n  DataHighwayHead msgBasehead = 1;\n  SegHead msgSeghead = 2;\n  bytes reqExtendinfo = 3;\n  int64 timestamp = 4;\n  //LoginSigHead? msgLoginSigHead = 5;\n}\n\nmessage RspDataHighwayHead {\n  DataHighwayHead msgBasehead = 1;\n  SegHead msgSeghead = 2;\n  int32 errorCode = 3;\n  int32 allowRetry = 4;\n  int32 cachecost = 5;\n  int32 htcost = 6;\n  bytes rspExtendinfo = 7;\n  int64 timestamp = 8;\n  int64 range = 9;\n  int32 isReset = 10;\n}\n\nmessage DataHighwayHead {\n  int32 version = 1;\n  string uin = 2;\n  string command = 3;\n  int32 seq = 4;\n  int32 retryTimes = 5;\n  int32 appid = 6;\n  int32 dataflag = 7;\n  int32 commandId = 8;\n  string buildVer = 9;\n  int32 localeId = 10;\n}\n\nmessage SegHead {\n  int32 serviceid = 1;\n  int64 filesize = 2;\n  int64 dataoffset = 3;\n  int32 datalength = 4;\n  int32 rtcode = 5;\n  bytes serviceticket = 6;\n  int32 flag = 7;\n  bytes md5 = 8;\n  bytes fileMd5 = 9;\n  int32 cacheAddr = 10;\n  int32 queryTimes = 11;\n  int32 updateCacheip = 12;\n}\n\nmessage TryUpImgReq {\n  int64 groupCode = 1;\n  int64 srcUin = 2;\n  int64 fileId = 3;\n  bytes fileMd5 = 4;\n  int64 fileSize = 5;\n  string fileName = 6;\n  int32 srcTerm = 7;\n  int32 platformType = 8;\n  int32 buType = 9;\n  int32 picWidth = 10;\n  int32 picHeight = 11;\n  int32 picType = 12;\n  string buildVer = 13;\n  int32 innerIp = 14;\n  int32 appPicType = 15;\n  int32 originalPic = 16;\n  bytes fileIndex = 17;\n  int64 dstUin = 18;\n  int32 srvUpload = 19;\n  bytes transferUrl = 20;\n}\n\nmessage TryUpImgResp {\n  int64 fileId = 1;\n  int32 result = 2;\n  string failMsg = 3;\n  bool boolFileExit = 4;\n  ImgInfo msgImgInfo = 5;\n  repeated uint32 uint32UpIp = 6;\n  repeated uint32 uint32UpPort = 7;\n  bytes upUkey = 8;\n  int64 fid = 9;\n}\n\nmessage TryUpPttReq {\n  int64 groupCode = 1;\n  int64 srcUin = 2;\n  int64 fileId = 3;\n  bytes fileMd5 = 4;\n  int64 fileSize = 5;\n  bytes fileName = 6;\n  int32 srcTerm = 7;\n  int32 platformType = 8;\n  int32 buType = 9;\n  string buildVer = 10;\n  int32 innerIp = 11;\n  int32 voiceLength = 12;\n  bool boolNewUpChan = 13;\n  int32 codec = 14;\n  int32 voiceType = 15;\n  int32 buId = 16;\n\n}\n\nmessage TryUpPttResp {\n  int64 fileId = 1;\n  int32 result = 2;\n  string failMsg = 3;\n  bool boolFileExit = 4;\n  repeated int32 uint32UpIp = 5;\n  repeated int32 uint32UpPort = 6;\n  bytes upUkey = 7;\n  int64 fileId2 = 8;\n  int64 upOffset = 9;\n  int64 blockSize = 10;\n  bytes fileKey = 11;\n  int32 channelType = 12;\n  //    List<IPv6Info>? msgUpIp6 = 26;\n  //    bytes clientIp6 = 27;\n\n}\n\nmessage ImgInfo {\n  bytes fileMd5 = 1;\n  int32 fileType = 2;\n  int64 fileSize = 3;\n  int32 fileWidth = 4;\n  int32 fileHeight = 5;\n}\n\nmessage DeleteMessageRequest {\n  repeated MessageItem items = 1;\n}\n\nmessage MessageItem {\n  int64 fromUin = 1;\n  int64 toUin = 2;\n  int32 msgType = 3;\n  int32 msgSeq = 4;\n  int64 msgUid = 5;\n  bytes sig = 7;\n}\n\nmessage SubD4 {\n  int64 uin = 1;\n}\n\nmessage Sub8A {\n  repeated Sub8AMsgInfo msg_info = 1;\n  int32 appId = 2;\n  int32 instId = 3;\n  int32 longMessageFlag = 4;\n  bytes reserved = 5;\n}\n\nmessage Sub8AMsgInfo {\n  int64 fromUin = 1;\n  int64 toUin = 2;\n  int32 msgSeq = 3;\n  int64 msgUid = 4;\n  int64 msgTime = 5;\n  int32 msgRandom = 6;\n  int32 pkgNum = 7;\n  int32 pkgIndex = 8;\n  int32 devSeq = 9;\n}\n\nmessage SubB3 {\n  int32 type = 1;\n  SubB3AddFrdNotify msgAddFrdNotify = 2;\n}\n\nmessage SubB3AddFrdNotify {\n  int64 uin = 1;\n  string nick = 5;\n}\n\nmessage Sub44 {\n  Sub44FriendSyncMsg friendSyncMsg = 1;\n  Sub44GroupSyncMsg groupSyncMsg = 2;\n}\n\nmessage Sub44FriendSyncMsg {\n  int64 uin = 1;\n  int64 fUin = 2;\n  int32 processType = 3;\n  int32 time = 4;\n  int32 processFlag = 5;\n  int32 sourceId = 6;\n  int32 sourceSubId = 7;\n  repeated string strWording = 8;\n}\n\nmessage Sub44GroupSyncMsg {\n  int32 msgType = 1;\n  int64 msgSeq = 2;\n  int64 grpCode = 3;\n  int64 gaCode = 4;\n  int64 optUin1 = 5;\n  int64 optUin2 = 6;\n  bytes msgBuf = 7;\n  bytes authKey = 8;\n  int32 msgStatus = 9;\n  int64 actionUin = 10;\n  int64 actionTime = 11;\n  int32 curMaxMemCount = 12;\n  int32 nextMaxMemCount = 13;\n  int32 curMemCount = 14;\n  int32 reqSrcId = 15;\n  int32 reqSrcSubId = 16;\n  int32 inviterRole = 17;\n  int32 extAdminNum = 18;\n  int32 processFlag = 19;\n}\n\nmessage GroupMemberReqBody {\n  int64 groupCode = 1;\n  int64 uin = 2;\n  bool newClient = 3;\n  int32 clientType = 4;\n  int32 richCardNameVer = 5;\n}\n\nmessage GroupMemberRspBody {\n  int64 groupCode = 1;\n  int32 selfRole = 2;\n  GroupMemberInfo memInfo = 3;\n  bool boolSelfLocationShared = 4;\n  int32 groupType = 5;\n}\n\nmessage GroupMemberInfo {\n  int64 uin = 1;\n  int32 result = 2;\n  bytes errmsg = 3;\n  bool IsFriend = 4;\n  bytes remark = 5;\n  bool IsConcerned = 6;\n  int32 credit = 7;\n  bytes card = 8;\n  int32 sex = 9;\n  bytes location = 10;\n  bytes nick = 11;\n  int32 age = 12;\n  bytes lev = 13;\n  int64 join = 14;\n  int64 lastSpeak = 15;\n  //repeated CustomEntry customEnties = 16;\n  //repeated GBarInfo gbarConcerned = 17;\n  bytes gbarTitle = 18;\n  bytes gbarUrl = 19;\n  int32 gbarCnt = 20;\n  bool isAllowModCard = 21;\n  bool isVip = 22;\n  bool isYearVip = 23;\n  bool isSuperVip = 24;\n  bool isSuperQq = 25;\n  int32 vipLev = 26;\n  int32 role = 27;\n  bool locationShared = 28;\n  int64 int64Distance = 29;\n  int32 concernType = 30;\n  bytes specialTitle = 31;\n  int32 specialTitleExpireTime = 32;\n  //FlowersEntry flowerEntry = 33;\n  //TeamEntry teamEntry = 34;\n  bytes phoneNum = 35;\n  bytes job = 36;\n  int32 medalId = 37;\n\n  int32 level = 39;\n\n  string honor = 41;\n}\n\n"
  },
  {
    "path": "ricq-core/src/pb/longmsg/longmsg.proto",
    "content": "syntax = \"proto3\";\n\npackage longmsg;\n\nmessage LongMsgDeleteReq {\n  bytes msgResid = 1;\n  int32 msgType = 2;\n}\nmessage LongMsgDeleteRsp {\n  int32 result = 1;\n  bytes msgResid = 2;\n}\nmessage LongMsgDownReq {\n  int32 srcUin = 1;\n  bytes msgResid = 2;\n  int32 msgType = 3;\n  int32 needCache = 4;\n}\nmessage LongMsgDownRsp {\n  int32 result = 1;\n  bytes msgResid = 2;\n  bytes msgContent = 3;\n}\nmessage LongMsgUpReq {\n  int32 msgType = 1;\n  int64 dstUin = 2;\n  int32 msgId = 3;\n  bytes msgContent = 4;\n  int32 storeType = 5;\n  bytes msgUkey = 6;\n  int32 needCache = 7;\n}\nmessage LongMsgUpRsp {\n  int32 result = 1;\n  int32 msgId = 2;\n  bytes msgResid = 3;\n}\nmessage LongReqBody {\n  int32 subcmd = 1;\n  int32 termType = 2;\n  int32 platformType = 3;\n  repeated LongMsgUpReq msgUpReq = 4;\n  repeated LongMsgDownReq msgDownReq = 5;\n  repeated LongMsgDeleteReq msgDelReq = 6;\n  int32 agentType = 10;\n}\nmessage LongRspBody {\n  int32 subcmd = 1;\n  repeated LongMsgUpRsp msgUpRsp = 2;\n  repeated LongMsgDownRsp msgDownRsp = 3;\n  repeated LongMsgDeleteRsp msgDelRsp = 4;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/mod.rs",
    "content": "#![allow(clippy::all)]\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/pb.rs\"));\n\nmacro_rules! add_includes {\n    ($( $name:ident ),* $(,)?) => {\n        $(\n            pub mod $name {\n                include!(concat!(env!(\"OUT_DIR\"), \"/\", stringify!($name), \".rs\"));\n            }\n        )*\n    };\n}\n\nadd_includes!(\n    cmd0x346,\n    cmd0x352,\n    cmd0x388,\n    cmd0x3bb,\n    cmd0x6ff,\n    cmd0x899,\n    longmsg,\n    msf,\n    msg,\n    msgtype0x210,\n    multimsg,\n    notify,\n    oidb,\n    online_status,\n    profilecard,\n    sig_act,\n    structmsg,\n    short_video,\n);\n"
  },
  {
    "path": "ricq-core/src/pb/msf/register_proxy.proto",
    "content": "syntax = \"proto2\";\n\npackage msf;\n\nmessage DiscussList {\n  optional uint64 discussCode = 1;\n  optional uint64 discussSeq = 2;\n  optional uint64 memberSeq = 3;\n  optional uint64 infoSeq = 4;\n  optional bool bHotGroup = 5;\n  optional uint64 redpackTime = 6;\n  optional bool hasMsg = 7;\n  optional int64 dicussFlag = 8;\n}\n\nmessage GroupList {\n  optional uint64 groupCode = 1;\n  optional uint64 groupSeq = 2;\n  optional uint64 memberSeq = 3;\n  optional uint64 mask = 4;\n  optional uint64 redpackTime = 5;\n  optional bool hasMsg = 6;\n  optional int64 groupFlag = 7;\n  optional uint64 groupType = 8;\n  optional uint32 groupNameSeq = 9;\n  optional uint32 groupMemberSeq = 10;\n  optional uint32 uinFlagEx2 = 11;\n  optional uint32 importantMsgLatestSeq = 12;\n}\n\nmessage SvcPbResponsePullDisMsgProxy {\n  optional uint64 memberSeq = 1;\n  optional bytes content = 2;\n}\n\nmessage SvcRegisterProxyMsgResp {\n  optional uint32 result = 1;\n  optional bytes errMsg = 2;\n  optional uint32 flag = 3;\n  optional uint32 seq = 4;\n  optional SvcResponseMsgInfo info = 5;\n  repeated GroupList groupList = 6;\n  repeated DiscussList discussList = 7;\n  repeated SvcResponsePbPullGroupMsgProxy groupMsg = 8;\n  repeated SvcPbResponsePullDisMsgProxy discussMsg = 9;\n  optional bytes c2CMsg = 10;\n  optional bytes pubAccountMsg = 11;\n  optional uint32 discussListFlag = 12;\n}\n\nmessage SvcResponseMsgInfo {\n  optional uint32 groupNum = 1;\n  optional uint32 discussNum = 2;\n}\n\nmessage SvcResponsePbPullGroupMsgProxy {\n  optional uint64 memberSeq = 1;\n  optional bytes content = 2;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/msg/TextMsgExt.proto",
    "content": "syntax = \"proto2\";\n\npackage msg;\n\nmessage ExtChannelInfo {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n}\n\nmessage TextResvAttr {\n  optional bytes wording = 1;\n  optional uint32 textAnalysisResult = 2;\n  optional uint32 atType = 3;\n  optional uint64 atMemberUin = 4;\n  optional uint64 atMemberTinyid = 5;\n  optional ExtRoleInfo atMemberRoleInfo = 6;\n  optional ExtRoleInfo atRoleInfo = 7;\n  optional ExtChannelInfo atChannelInfo = 8;\n}\n\nmessage ExtRoleInfo {\n  optional uint64 id = 1;\n  optional bytes info = 2;\n  optional uint32 flag = 3;\n}"
  },
  {
    "path": "ricq-core/src/pb/msg/head.proto",
    "content": "syntax = \"proto2\";\npackage msg;\n\nmessage C2CHead {\n  optional uint64 toUin = 1;\n  optional uint64 fromUin = 2;\n  optional uint32 ccType = 3;\n  optional uint32 ccCmd = 4;\n  optional bytes authPicSig = 5;\n  optional bytes authSig = 6;\n  optional bytes authBuf = 7;\n  optional uint32 serverTime = 8;\n  optional uint32 clientTime = 9;\n  optional uint32 rand = 10;\n  optional string phoneNumber = 11;\n}\n\nmessage CSHead {\n  optional uint64 uin = 1;\n  optional uint32 command = 2;\n  optional uint32 seq = 3;\n  optional uint32 version = 4;\n  optional uint32 retryTimes = 5;\n  optional uint32 clientType = 6;\n  optional uint32 pubno = 7;\n  optional uint32 localid = 8;\n  optional uint32 timezone = 9;\n  optional fixed32 clientIp = 10;\n  optional uint32 clientPort = 11;\n  optional fixed32 connIp = 12;\n  optional uint32 connPort = 13;\n  optional fixed32 interfaceIp = 14;\n  optional uint32 interfacePort = 15;\n  optional fixed32 actualIp = 16;\n  optional uint32 flag = 17;\n  optional fixed32 timestamp = 18;\n  optional uint32 subcmd = 19;\n  optional uint32 result = 20;\n  optional uint32 appId = 21;\n  optional uint32 instanceId = 22;\n  optional uint64 sessionId = 23;\n  optional uint32 idcId = 24;\n}\n\nmessage DeltaHead {\n  optional uint64 totalLen = 1;\n  optional uint64 offset = 2;\n  optional uint64 ackOffset = 3;\n  optional bytes cookie = 4;\n  optional bytes ackCookie = 5;\n  optional uint32 result = 6;\n  optional uint32 flags = 7;\n}\n\nmessage IMHead {\n  optional uint32 headType = 1;\n  optional CSHead csHead = 2;\n  optional S2CHead s2CHead = 3;\n  optional HttpConnHead httpconnHead = 4;\n  optional uint32 paintFlag = 5;\n  optional LoginSig loginSig = 6;\n  optional DeltaHead deltaHead = 7;\n  optional C2CHead c2CHead = 8;\n}\n\nmessage HttpConnHead {\n  optional uint64 uin = 1;\n  optional uint32 command = 2;\n  optional uint32 subCommand = 3;\n  optional uint32 seq = 4;\n  optional uint32 version = 5;\n  optional uint32 retryTimes = 6;\n  optional uint32 clientType = 7;\n  optional uint32 pubNo = 8;\n  optional uint32 localId = 9;\n  optional uint32 timeZone = 10;\n  optional fixed32 clientIp = 11;\n  optional uint32 clientPort = 12;\n  optional fixed32 qzhttpIp = 13;\n  optional uint32 qzhttpPort = 14;\n  optional fixed32 sppIp = 15;\n  optional uint32 sppPort = 16;\n  optional uint32 flag = 17;\n  optional bytes key = 18;\n  optional uint32 compressType = 19;\n  optional uint32 originSize = 20;\n  optional uint32 errorCode = 21;\n  optional RedirectMsg redirect = 22;\n  optional uint32 commandId = 23;\n  optional uint32 serviceCmdid = 24;\n  optional TransOidbHead oidbhead = 25;\n}\n\n\nmessage LoginSig {\n  optional uint32 type = 1;\n  optional bytes sig = 2;\n}\n\nmessage RedirectMsg {\n  optional fixed32 lastRedirectIp = 1;\n  optional uint32 lastRedirectPort = 2;\n  optional fixed32 redirectIp = 3;\n  optional uint32 redirectPort = 4;\n  optional uint32 redirectCount = 5;\n}\n\nmessage S2CHead {\n  optional uint32 subMsgtype = 1;\n  optional uint32 msgType = 2;\n  optional uint64 fromUin = 3;\n  optional uint32 msgId = 4;\n  optional fixed32 relayIp = 5;\n  optional uint32 relayPort = 6;\n  optional uint64 toUin = 7;\n}\n\nmessage TransOidbHead {\n  optional uint32 command = 1;\n  optional uint32 serviceType = 2;\n  optional uint32 result = 3;\n  optional string errorMsg = 4;\n}"
  },
  {
    "path": "ricq-core/src/pb/msg/msg.proto",
    "content": "syntax = \"proto2\";\n\npackage msg;\n\nmessage GetMessageRequest {\n  optional SyncFlag syncFlag = 1;\n  optional bytes syncCookie = 2;\n  optional int32 rambleFlag = 3;\n  optional int32 latestRambleNumber = 4;\n  optional int32 otherRambleNumber = 5;\n  optional int32 onlineSyncFlag = 6;\n  optional int32 contextFlag = 7;\n  optional int32 whisperSessionId = 8;\n  optional int32 msgReqType = 9;\n  optional bytes pubaccountCookie = 10;\n  optional bytes msgCtrlBuf = 11;\n  optional bytes serverBuf = 12;\n}\n\nmessage SendMessageRequest {\n  optional RoutingHead routingHead = 1;\n  optional ContentHead contentHead = 2;\n  optional MessageBody msgBody = 3;\n  optional int32 msgSeq = 4;\n  optional int32 msgRand = 5;\n  optional bytes syncCookie = 6;\n  //MsgComm.AppShareInfo? appShare = 7;\n  optional int32 msgVia = 8;\n  optional int32 dataStatist = 9;\n  //MultiMsgAssist? multiMsgAssist = 10;\n  //PbInputNotifyInfo? inputNotifyInfo = 11;\n  optional MsgCtrl msgCtrl = 12;\n  //ImReceipt.ReceiptReq? receiptReq = 13;\n  optional int32 multiSendSeq = 14;\n}\n\nmessage SendMessageResponse {\n  optional int32 result = 1;\n  optional string errMsg = 2;\n}\n\nmessage MsgWithDrawReq {\n  repeated C2CMsgWithDrawReq c2cWithDraw = 1;\n  repeated GroupMsgWithDrawReq groupWithDraw = 2;\n}\n\nmessage C2CMsgWithDrawReq{\n  repeated C2CMsgInfo msgInfo = 1;\n  optional int32 longMessageFlag = 2;\n  optional bytes reserved = 3;\n  optional int32 subCmd = 4;\n}\n\nmessage GroupMsgWithDrawReq{\n  optional int32 subCmd = 1;\n  optional int32 groupType = 2;\n  optional int64 groupCode = 3;\n  repeated GroupMsgInfo msgList = 4;\n  optional bytes userDef = 5;\n}\n\nmessage MsgWithDrawResp {\n  repeated C2CMsgWithDrawResp c2cWithDraw = 1;\n  repeated GroupMsgWithDrawResp groupWithDraw = 2;\n}\n\nmessage C2CMsgWithDrawResp {\n  optional int32 result = 1;\n  optional string errMsg = 2;\n}\n\nmessage GroupMsgWithDrawResp {\n  optional int32 result = 1;\n  optional string errMsg = 2;\n}\n\nmessage GroupMsgInfo {\n  optional int32 msgSeq = 1;\n  optional int32 msgRandom = 2;\n  optional int32 msgType = 3;\n}\n\nmessage C2CMsgInfo {\n  optional int64 fromUin = 1;\n  optional int64 toUin = 2;\n  optional int32 msgSeq = 3;\n  optional int64 msgUid = 4;\n  optional int64 msgTime = 5;\n  optional int32 msgRandom = 6;\n  optional int32 pkgNum = 7;\n  optional int32 pkgIndex = 8;\n  optional int32 divSeq = 9;\n  optional int32 msgType = 10;\n  optional RoutingHead routingHead = 20;\n}\n\nmessage RoutingHead {\n  oneof RoutingHead{\n    C2C c2c = 1;\n    Grp grp = 2;\n    GrpTmp grpTmp = 3;\n    WPATmp wpaTmp = 6;\n  }\n  /*\n  Dis dis = 4;\n  DisTmp disTmp = 5;\n  SecretFileHead? secretFile = 7;\n  PublicPlat? publicPlat = 8;\n  TransMsg? transMsg = 9;\n  AddressListTmp? addressList = 10;\n  RichStatusTmp? richStatusTmp = 11;\n  TransCmd? transCmd = 12;\n  AccostTmp? accostTmp = 13;\n  PubGroupTmp? pubGroupTmp = 14;\n  Trans0x211? trans0x211 = 15;\n  BusinessWPATmp? businessWpaTmp = 16;\n  AuthTmp? authTmp = 17;\n  BsnsTmp? bsnsTmp = 18;\n  QQQueryBusinessTmp? qqQuerybusinessTmp = 19;\n  NearByDatingTmp? nearbyDatingTmp = 20;\n  NearByAssistantTmp? nearbyAssistantTmp = 21;\n  CommTmp? commTmp = 22;\n  */\n}\n\nmessage WPATmp {\n  optional uint64 toUin = 1;\n  optional bytes sig = 2;\n}\n\nmessage C2C {\n  optional int64 toUin = 1;\n}\n\nmessage Grp {\n  optional int64 groupCode = 1;\n}\n\nmessage GrpTmp {\n  optional int64 groupUin = 1;\n  optional int64 toUin = 2;\n}\n\nmessage MsgCtrl {\n  optional int32 msgFlag = 1;\n}\n\nmessage GetMessageResponse {\n  optional int32 result = 1;\n  optional string errorMessage = 2;\n  optional bytes syncCookie = 3;\n  optional SyncFlag syncFlag = 4;\n  repeated UinPairMessage uinPairMsgs = 5;\n  optional int64 bindUin = 6;\n  optional int32 msgRspType = 7;\n  optional bytes pubAccountCookie = 8;\n  optional bool isPartialSync = 9;\n  optional bytes msgCtrlBuf = 10;\n}\n\nmessage PushMessagePacket {\n  optional Message message = 1;\n  optional int32 svrip = 2;\n  optional bytes pushToken = 3;\n  optional int32 pingFLag = 4;\n  optional int32 generalFlag = 9;\n}\n\nmessage UinPairMessage {\n  optional int32 lastReadTime = 1;\n  optional int64 peerUin = 2;\n  optional int32 msgCompleted = 3;\n  repeated Message messages = 4;\n}\n\nmessage Message {\n  optional MessageHead head = 1;\n  optional ContentHead content = 2;\n  optional MessageBody body = 3;\n}\n\nmessage MessageBody {\n  optional RichText richText = 1;\n  optional bytes msgContent = 2;\n  optional bytes msgEncryptContent = 3;\n}\n\nmessage RichText {\n  optional Attr attr = 1;\n  repeated Elem elems = 2;\n  optional NotOnlineFile notOnlineFile = 3;\n  optional Ptt ptt = 4;\n}\n\nmessage Elem {\n  oneof elem {\n    Text text = 1;\n    Face face = 2;\n    OnlineImage onlineImage = 3;\n    NotOnlineImage notOnlineImage = 4;\n    TransElem transElemInfo = 5;\n    MarketFace marketFace = 6;\n    //ElemFlags elemFlags = 7;\n    CustomFace customFace = 8;\n    ElemFlags2 elemFlags2 = 9;\n    //FunFace funFace = 10;\n    //SecretFileMsg secretFile = 11;\n    RichMsg richMsg = 12;\n    GroupFile groupFile = 13;\n    //PubGroup pubGroup = 14;\n    //MarketTrans marketTrans = 15;\n    ExtraInfo extraInfo = 16;\n    //ShakeWindow? shakeWindow = 17;\n    //PubAccount? pubAccount = 18;\n    VideoFile videoFile = 19;\n    //TipsInfo? tipsInfo = 20;\n    AnonymousGroupMessage anonGroupMsg = 21;\n    //QQLiveOld? qqLiveOld = 22;\n    //LifeOnlineAccount? lifeOnline = 23;\n    QQWalletMsg QQWalletMsg = 24;\n    //CrmElem? crmElem = 25;\n    //ConferenceTipsInfo? conferenceTipsInfo = 26;\n    //RedBagInfo? redbagInfo = 27;\n    //LowVersionTips? lowVersionTips = 28;\n    //bytes bankcodeCtrlInfo = 29;\n    //NearByMessageType? nearByMsg = 30;\n    CustomElem customElem = 31;\n    //LocationInfo? locationInfo = 32;\n    //PubAccInfo? pubAccInfo = 33;\n    //SmallEmoji? smallEmoji = 34;\n    //FSJMessageElem? fsjMsgElem = 35;\n    //ArkAppElem? arkApp = 36;\n    GeneralFlags generalFlags = 37;\n    //CustomFace? hcFlashPic = 38;\n    //DeliverGiftMsg? deliverGiftMsg = 39;\n    //BitAppMsg? bitappMsg = 40;\n    //OpenQQData? openQqData = 41;\n    //ApolloActMsg? apolloMsg = 42;\n    //GroupPubAccountInfo? groupPubAccInfo = 43;\n    //BlessingMessage? blessMsg = 44;\n    SourceMsg srcMsg = 45;\n    //LolaMsg? lolaMsg = 46;\n    //GroupBusinessMsg? groupBusinessMsg = 47;\n    //WorkflowNotifyMsg? msgWorkflowNotify = 48;\n    //PatsElem? patElem = 49;\n    //GroupPostElem? groupPostElem = 50;\n    LightApp lightApp = 51;\n    //EIMInfo? eimInfo = 52;\n    CommonElem commonElem = 53;\n  }\n}\n\nmessage MarketFace {\n  optional bytes faceName = 1;\n  optional uint32 itemType = 2;\n  optional uint32 faceInfo = 3;\n  optional bytes faceId = 4;\n  optional uint32 tabId = 5;\n  optional uint32 subType = 6;\n  optional bytes key = 7;\n  optional bytes param = 8;\n  optional uint32 mediaType = 9;\n  optional uint32 imageWidth = 10;\n  optional uint32 imageHeight = 11;\n  optional bytes mobileparam = 12;\n  optional bytes pbReserve = 13;\n}\n\nmessage ElemFlags2 {\n  optional uint32 colorTextId = 1;\n  optional uint64 msgId = 2;\n  optional uint32 whisperSessionId = 3;\n  optional uint32 pttChangeBit = 4;\n  optional uint32 vipStatus = 5;\n  optional uint32 compatibleId = 6;\n  repeated Inst insts = 7;\n  optional uint32 msgRptCnt = 8;\n  optional Inst srcInst = 9;\n  optional uint32 longtitude = 10;\n  optional uint32 latitude = 11;\n  optional uint32 customFont = 12;\n  optional PcSupportDef pcSupportDef = 13;\n  optional uint32 crmFlags = 14;\n\n  message Inst {\n    optional uint32 appId = 1;\n    optional uint32 instId = 2;\n  }\n}\n\nmessage PcSupportDef {\n  optional uint32 pcPtlBegin = 1;\n  optional uint32 pcPtlEnd = 2;\n  optional uint32 macPtlBegin = 3;\n  optional uint32 macPtlEnd = 4;\n  repeated uint32 ptlsSupport = 5;\n  repeated uint32 ptlsNotSupport = 6;\n}\n\nmessage CommonElem {\n  optional int32 serviceType = 1;\n  optional bytes pbElem = 2;\n  optional int32 businessType = 3;\n}\n\nmessage QQWalletMsg {\n  optional QQWalletAioBody aioBody = 1;\n}\n\nmessage QQWalletAioBody {\n  optional uint64 sendUin = 1;\n  optional QQWalletAioElem sender = 2;\n  optional QQWalletAioElem receiver = 3;\n  optional sint32 ChannelId = 4;\n  optional sint32 templateId = 5;\n  optional uint32 resend = 6;\n  optional uint32 msgPriority = 7;\n  optional sint32 redType = 8;\n  optional bytes billNo = 9;\n  optional bytes authKey = 10;\n  optional sint32 sessionType = 11;\n  optional sint32 msgType = 12;\n  optional sint32 envelOpeId = 13;\n  optional bytes name = 14;\n  optional sint32 confType = 15;\n  optional sint32 msgFrom = 16;\n  optional bytes pcBody = 17;\n  optional bytes index = 18;\n  optional uint32 redChannel = 19;\n  repeated uint64 grapUin = 20;\n  optional bytes pbReserve = 21;\n}\n\nmessage QQWalletAioElem{\n  optional uint32 background = 1;\n  optional uint32 icon = 2;\n  optional string title = 3;\n  optional string subtitle = 4;\n  optional string content = 5;\n  optional bytes linkUrl = 6;\n  optional bytes blackStripe = 7;\n  optional bytes notice = 8;\n  optional uint32 titleColor = 9;\n  optional uint32 subtitleColor = 10;\n  optional bytes actionsPriority = 11;\n  optional bytes jumpUrl = 12;\n  optional bytes nativeIos = 13;\n  optional bytes nativeAndroid = 14;\n  optional bytes iconUrl = 15;\n  optional uint32 contentColor = 16;\n  optional uint32 contentBgColor = 17;\n  optional bytes aioImageLeft = 18;\n  optional bytes aioImageRight = 19;\n  optional bytes cftImage = 20;\n  optional bytes pbReserve = 21;\n}\n\nmessage RichMsg {\n  optional bytes template1 = 1;\n  optional int32 serviceId = 2;\n  optional bytes msgResId = 3;\n  optional int32 rand = 4;\n  optional int32 seq = 5;\n}\n\nmessage CustomElem {\n  optional bytes desc = 1;\n  optional bytes data = 2;\n  optional int32  enumType = 3;\n  optional bytes ext = 4;\n  optional bytes sound = 5;\n}\n\nmessage Text {\n  optional string str = 1;\n  optional string link = 2;\n  optional bytes attr6Buf = 3;\n  optional bytes attr7Buf = 4;\n  optional bytes buf = 11;\n  optional bytes pbReserve = 12;\n}\n\nmessage Attr {\n  optional int32 codePage = 1;\n  optional int32 time = 2;\n  optional int32 random = 3;\n  optional int32 color = 4;\n  optional int32 size = 5;\n  optional int32 effect = 6;\n  optional int32 charSet = 7;\n  optional int32 pitchAndFamily = 8;\n  optional string fontName = 9;\n  optional bytes reserveData = 10;\n}\n\nmessage Ptt {\n  optional int32 fileType = 1;\n  optional int64 srcUin = 2;\n  optional bytes fileUuid = 3;\n  optional bytes fileMd5 = 4;\n  optional string fileName = 5;\n  optional int32 fileSize = 6;\n  optional bytes reserve = 7;\n  optional int32 fileId = 8;\n  optional int32 serverIp = 9;\n  optional int32 serverPort = 10;\n  optional bool boolValid = 11;\n  optional bytes signature = 12;\n  optional bytes shortcut = 13;\n  optional bytes fileKey = 14;\n  optional int32 magicPttIndex = 15;\n  optional int32 voiceSwitch = 16;\n  optional bytes pttUrl = 17;\n  optional bytes groupFileKey = 18;\n  optional int32 time = 19;\n  optional bytes downPara = 20;\n  optional int32 format = 29;\n  optional bytes pbReserve = 30;\n  repeated bytes bytesPttUrls = 31;\n  optional int32 downloadFlag = 32;\n}\n\nmessage OnlineImage {\n  optional bytes guid = 1;\n  optional bytes filePath = 2;\n  optional  bytes oldVerSendFile = 3;\n}\n\nmessage NotOnlineImage {\n  optional string filePath = 1;\n  optional uint32 fileLen = 2;\n  optional string downloadPath = 3;\n  optional bytes oldVerSendFile = 4;\n  optional int32 imgType = 5;\n  optional bytes previewsImage = 6;\n  optional bytes picMd5 = 7;\n  optional uint32 picHeight = 8;\n  optional uint32 picWidth = 9;\n  optional string resId = 10;\n  optional bytes flag = 11;\n  optional string thumbUrl = 12;\n  optional int32 original = 13;\n  optional string bigUrl = 14;\n  optional string origUrl = 15;\n  optional int32 bizType = 16;\n  optional int32 result = 17;\n  optional int32 index = 18;\n  optional bytes opFaceBuf = 19;\n  optional bool oldPicMd5 = 20;\n  optional int32 thumbWidth = 21;\n  optional int32 thumbHeight = 22;\n  optional int32 fileId = 23;\n  optional int32 showLen = 24;\n  optional int32 downloadLen = 25;\n  optional bytes pbReserve = 29;\n}\n\nmessage NotOnlineFile {\n  optional int32 fileType = 1;\n  optional bytes sig = 2;\n  optional bytes fileUuid = 3;\n  optional bytes fileMd5 = 4;\n  optional bytes fileName = 5;\n  optional int64 fileSize = 6;\n  optional bytes note = 7;\n  optional int32 reserved = 8;\n  optional int32 subcmd = 9;\n  optional int32 microCloud = 10;\n  repeated bytes bytesFileUrls = 11;\n  optional int32 downloadFlag = 12;\n  optional int32 dangerEvel = 50;\n  optional int32 lifeTime = 51;\n  optional int32 uploadTime = 52;\n  optional int32 absFileType = 53;\n  optional int32 clientType = 54;\n  optional int32 expireTime = 55;\n  optional bytes pbReserve = 56;\n}\n\nmessage TransElem {\n  optional int32 elemType = 1;\n  optional bytes elemValue = 2;\n}\n\nmessage ExtraInfo {\n  optional bytes nick = 1;\n  optional bytes groupCard = 2;\n  optional int32 level = 3;\n  optional int32 flags = 4;\n  optional int32 groupMask = 5;\n  optional int32 msgTailId = 6;\n  optional bytes senderTitle = 7;\n  optional bytes apnsTips = 8;\n  optional int64 uin = 9;\n  optional int32 msgStateFlag = 10;\n  optional int32 apnsSoundType = 11;\n  optional int32 newGroupFlag = 12;\n}\n\nmessage GroupFile {\n  optional bytes filename = 1;\n  optional int64 fileSize = 2;\n  optional bytes fileId = 3;\n  optional bytes batchId = 4;\n  optional bytes fileKey = 5;\n  optional bytes mark = 6;\n  optional int64 sequence = 7;\n  optional bytes batchItemId = 8;\n  optional int32 feedMsgTime = 9;\n  optional bytes pbReserve = 10;\n}\n\nmessage AnonymousGroupMessage {\n  optional int32 flags = 1;\n  optional bytes anonId = 2;\n  optional bytes anonNick = 3;\n  optional int32 headPortrait = 4;\n  optional int32 expireTime = 5;\n  optional int32 bubbleId = 6;\n  optional bytes rankColor = 7;\n}\n\nmessage VideoFile {\n  optional bytes fileUuid = 1;\n  optional bytes fileMd5 = 2;\n  optional string fileName = 3;\n  optional int32 fileFormat = 4;\n  optional int32 fileTime = 5;\n  optional int32 fileSize = 6;\n  optional int32 thumbWidth = 7;\n  optional int32 thumbHeight = 8;\n  optional bytes thumbFileMd5 = 9;\n  optional bytes source = 10;\n  optional int32 thumbFileSize = 11;\n  optional int32 busiType = 12;\n  optional int32 fromChatType = 13;\n  optional int32 toChatType = 14;\n  optional bool boolSupportProgressive = 15;\n  optional int32 fileWidth = 16;\n  optional int32 fileHeight = 17;\n  optional int32 subBusiType = 18;\n  optional int32 videoAttr = 19;\n  repeated bytes bytesThumbFileUrls = 20;\n  repeated bytes bytesVideoFileUrls = 21;\n  optional int32 thumbDownloadFlag = 22;\n  optional int32 videoDownloadFlag = 23;\n  optional bytes pbReserve = 24;\n}\n\nmessage SourceMsg {\n  repeated int32 origSeqs = 1;\n  optional int64 senderUin = 2;\n  optional int32 time = 3;\n  optional int32 flag = 4;\n  repeated Elem elems = 5;\n  optional int32 type = 6;\n  optional bytes richMsg = 7;\n  optional bytes pbReserve = 8;\n  optional bytes srcMsg = 9;\n  optional int64 toUin = 10;\n  optional bytes troopName = 11;\n}\n\nmessage Face {\n  optional int32 index = 1;\n  optional bytes old = 2;\n  optional bytes buf = 11;\n}\n\nmessage LightApp {\n  optional bytes data = 1;\n  optional bytes msgResid = 2;\n}\n\nmessage CustomFace {\n  optional bytes guid = 1;\n  optional  string filePath = 2;\n  optional string shortcut = 3;\n  optional bytes buffer = 4;\n  optional bytes flag = 5;\n  optional bytes oldData = 6;\n  optional int32 fileId = 7;\n  optional uint32 serverIp = 8;\n  optional uint32 serverPort = 9;\n  optional int32 fileType = 10;\n  optional bytes signature = 11;\n  optional int32 useful = 12;\n  optional bytes md5 = 13;\n  optional string thumbUrl = 14;\n  optional string bigUrl = 15;\n  optional string origUrl = 16;\n  optional int32 bizType = 17;\n  optional int32 repeatIndex = 18;\n  optional int32 repeatImage = 19;\n  optional int32 imageType = 20;\n  optional int32 index = 21;\n  optional uint32 width = 22;\n  optional uint32 height = 23;\n  optional int32 source = 24;\n  optional uint32 size = 25;\n  optional int32 origin = 26;\n  optional int32 thumbWidth = 27;\n  optional int32 thumbHeight = 28;\n  optional int32 showLen = 29;\n  optional int32 downloadLen = 30;\n  optional string x400Url = 31;//x\n  optional int32 x400Width = 32;//x\n  optional int32 x400Height = 33;//x\n  optional bytes pbReserve = 34;\n}\n\nmessage ContentHead {\n  optional int32 pkgNum = 1;\n  optional int32 pkgIndex = 2;\n  optional int32 divSeq = 3;\n  optional int32 autoReply = 4;\n}\n\nmessage MessageHead {\n  optional int64 fromUin = 1;\n  optional int64 toUin = 2;\n  optional int32 msgType = 3;\n  optional int32 c2cCmd = 4;\n  optional int32 msgSeq = 5;\n  optional int32 msgTime = 6;\n  optional int64 msgUid = 7;\n  optional C2CTempMessageHead c2cTmpMsgHead = 8;\n  optional GroupInfo groupInfo = 9;\n  optional int32 fromAppid = 10;\n  optional int32 fromInstid = 11;\n  optional int32 userActive = 12;\n  optional DiscussInfo discussInfo = 13;\n  optional string fromNick = 14;\n  optional int64 authUin = 15;\n  optional string authNick = 16;\n  optional int32 msgFlag = 17;\n  optional string authRemark = 18;\n  optional string groupName = 19;\n  optional MutilTransHead mutiltransHead = 20;\n  optional InstCtrl msgInstCtrl = 21;\n  optional int32 publicAccountGroupSendFlag = 22;\n  optional int32 wseqInC2cMsghead = 23;\n  optional int64 cpid = 24;\n  optional ExtGroupKeyInfo extGroupKeyInfo = 25;\n  optional string multiCompatibleText = 26;\n  optional int32 authSex = 27;\n  optional bool isSrcMsg = 28;\n}\n\nmessage GroupInfo {\n  optional int64 groupCode = 1;\n  optional int32 groupType = 2;\n  optional int64 groupInfoSeq = 3;\n  optional bytes groupCard = 4;\n  optional bytes groupRank = 5;\n  optional int32 groupLevel = 6;\n  optional int32 groupCardType = 7;\n  optional bytes groupName = 8;\n}\n\nmessage DiscussInfo {\n  optional int64 discussUin = 1;\n  optional int32 discussType = 2;\n  optional int64 discussInfoSeq = 3;\n  optional bytes discussRemark = 4;\n  optional bytes discussName = 5;\n}\n\nmessage MutilTransHead{\n  optional int32 status = 1;\n  optional int32 msgId = 2;\n}\n\nmessage C2CTempMessageHead {\n  optional int32 c2cType = 1;\n  optional int32 serviceType = 2;\n  optional int64 groupUin = 3;\n  optional int64 groupCode = 4;\n  optional bytes sig = 5;\n  optional int32 sigType = 6;\n  optional string fromPhone = 7;\n  optional string toPhone = 8;\n  optional int32 lockDisplay = 9;\n  optional int32 directionFlag = 10;\n  optional bytes reserved = 11;\n}\n\nmessage InstCtrl {\n  repeated InstInfo msgSendToInst = 1;\n  repeated InstInfo msgExcludeInst = 2;\n  optional InstInfo msgFromInst = 3;\n}\n\nmessage InstInfo {\n  optional int32 apppid = 1;\n  optional int32 instid = 2;\n  optional int32 platform = 3;\n  optional int32 enumDeviceType = 10;\n}\n\nmessage ExtGroupKeyInfo {\n  optional int32 curMaxSeq = 1;\n  optional int64 curTime = 2;\n}\n\nmessage SyncCookie {\n  optional int64 time1 = 1;\n  optional int64 time = 2;\n  optional int64 ran1 = 3;\n  optional int64 ran2 = 4;\n  optional int64 const1 = 5;\n  optional int64 const2 = 11;\n  optional int64 const3 = 12;\n  optional int64 lastSyncTime = 13;\n  optional int64 const4 = 14;\n}\n\nmessage TransMsgInfo {\n  optional int64 fromUin = 1;\n  optional int64 toUin = 2;\n  optional int32 msgType = 3;\n  optional int32 msgSubtype = 4;\n  optional int32 msgSeq = 5;\n  optional int64 msgUid = 6;\n  optional int32 msgTime = 7;\n  optional int32 realMsgTime = 8;\n  optional string nickName = 9;\n  optional bytes msgData = 10;\n  optional int32 svrIp = 11;\n  optional ExtGroupKeyInfo extGroupKeyInfo = 12;\n  optional int32 generalFlag = 17;\n}\n\nmessage GeneralFlags {\n  optional int32 bubbleDiyTextId = 1;\n  optional int32 groupFlagNew = 2;\n  optional int64 uin = 3;\n  optional bytes rpId = 4;\n  optional int32 prpFold = 5;\n  optional int32 longTextFlag = 6;\n  optional string longTextResid = 7;\n  optional int32 groupType = 8;\n  optional int32 toUinFlag = 9;\n  optional int32 glamourLevel = 10;\n  optional int32 memberLevel = 11;\n  optional int64 groupRankSeq = 12;\n  optional int32 olympicTorch = 13;\n  optional bytes babyqGuideMsgCookie = 14;\n  optional int32 uin32ExpertFlag = 15;\n  optional int32 bubbleSubId = 16;\n  optional int64 pendantId = 17;\n  optional bytes rpIndex = 18;\n  optional bytes pbReserve = 19;\n}\n\n\nmessage PbMultiMsgItem {\n  optional string fileName = 1;\n  optional PbMultiMsgNew buffer = 2;\n}\nmessage PbMultiMsgNew {\n  repeated Message msg = 1;\n}\nmessage PbMultiMsgTransmit {\n  repeated Message msg = 1;\n  repeated PbMultiMsgItem pbItemList = 2;\n}\n\nmessage MsgElemInfo_servtype3 {\n  optional CustomFace flash_troop_pic = 1;\n  optional NotOnlineImage flash_c2c_pic = 2;\n}\n\nmessage MsgElemInfo_servtype33 {\n  optional uint32 index = 1;\n  optional bytes text = 2;\n  optional bytes compat = 3;\n  optional bytes buf = 4;\n}\n\nmessage SubMsgType0x4Body {\n  optional NotOnlineFile notOnlineFile = 1;\n  optional uint32 msgTime = 2;\n  optional uint32 onlineFileForPolyToOffline = 3;\n  // fileImageInfo\n}\n\nenum SyncFlag {\n  START = 0;\n  CONTINUME = 1;\n  STOP = 2;\n}\n\nmessage ResvAttr {\n  optional uint32 imageBizType = 1;\n  optional AnimationImageShow image_show = 7;\n}\n\nmessage AnimationImageShow {\n  optional int32 effect_id = 1;\n  optional bytes animation_param = 2;\n}\n\nmessage UinTypeUserDef {\n  optional int32 fromUinType = 1;\n  optional int64 fromGroupCode = 2;\n  optional string fileUuid = 3;\n}\n\nmessage GetGroupMsgReq {\n  optional uint64 groupCode = 1;\n  optional uint64 beginSeq = 2;\n  optional uint64 endSeq = 3;\n  optional uint32 filter = 4;\n  optional uint64 memberSeq = 5;\n  optional bool publicGroup = 6;\n  optional uint32 shieldFlag = 7;\n  optional uint32 saveTrafficFlag = 8;\n}\n\nmessage GetGroupMsgResp {\n  optional uint32 result = 1;\n  optional string errmsg = 2;\n  optional uint64 groupCode = 3;\n  optional uint64 returnBeginSeq = 4;\n  optional uint64 returnEndSeq = 5;\n  repeated Message msg = 6;\n}\n\nmessage PbGetOneDayRoamMsgReq {\n  optional uint64 peerUin = 1;\n  optional uint64 lastMsgTime = 2;\n  optional uint64 random = 3;\n  optional uint32 readCnt = 4;\n}\n\nmessage PbGetOneDayRoamMsgResp {\n  optional uint32 result = 1;\n  optional string errMsg = 2;\n  optional uint64 peerUin = 3;\n  optional uint64 lastMsgTime = 4;\n  optional uint64 random = 5;\n  repeated Message msg = 6;\n  optional uint32 isComplete = 7;\n}\n\nmessage PbPushMsg {\n  optional Message msg = 1;\n  optional int32 svrip = 2;\n  optional bytes pushToken = 3;\n  optional uint32 pingFlag = 4;\n  optional uint32 generalFlag = 9;\n  optional uint64 bindUin = 10;\n}\n\nmessage MsgElemInfo_servtype37 {\n  optional bytes packid = 1;\n  optional bytes stickerid = 2;\n  optional uint32 qsid = 3;\n  optional uint32 sourcetype = 4;\n  optional uint32 stickertype = 5;\n  optional bytes resultid = 6;\n  optional bytes text = 7;\n  optional bytes surpriseid = 8;\n  optional uint32 randomtype = 9;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/msg/objmsg.proto",
    "content": "syntax = \"proto3\";\n\npackage msg;\n\nmessage MsgPic {\n  bytes smallPicUrl = 1;\n  bytes originalPicUrl = 2;\n  int32 localPicId = 3;\n}\nmessage ObjMsg {\n  int32 msgType = 1;\n  bytes title = 2;\n  bytes bytesAbstact = 3;\n  bytes titleExt = 5;\n  repeated MsgPic msgPic = 6;\n  repeated MsgContentInfo msgContentInfo = 7;\n  int32 reportIdShow = 8;\n}\nmessage MsgContentInfo {\n  bytes contentInfoId = 1;\n  MsgFile msgFile = 2;\n}\nmessage MsgFile {\n  int32 busId = 1;\n  bytes filePath = 2;\n  int64 fileSize = 3;\n  string fileName = 4;\n  int64 int64DeadTime = 5;\n  bytes fileSha1 = 6;\n  bytes ext = 7;\n}\n   "
  },
  {
    "path": "ricq-core/src/pb/msg/report.proto",
    "content": "syntax = \"proto2\";\n\npackage msg;\n\nmessage PbMsgReadedReportReq {\n  repeated PbGroupReadedReportReq grpReadReport = 1;\n  repeated PbDiscussReadedReportReq disReadReport = 2;\n  optional PbC2CReadedReportReq c2CReadReport = 3;\n  //optional PbBindUinMsgReadedConfirmReq bindUinReadReport = 4;\n}\n\nmessage PbMsgReadedReportResp {\n  repeated PbGroupReadedReportResp grpReadReport = 1;\n  repeated PbDiscussReadedReportResp disReadReport = 2;\n  optional PbC2CReadedReportResp c2CReadReport = 3;\n  //optional PbBindUinMsgReadedConfirmResp bindUinReadReport = 4;\n}\n\nmessage PbGroupReadedReportReq {\n  optional uint64 groupCode = 1;\n  optional uint64 lastReadSeq = 2;\n}\n\nmessage PbDiscussReadedReportReq {\n  optional uint64 confUin = 1;\n  optional uint64 lastReadSeq = 2;\n}\n\nmessage PbC2CReadedReportReq {\n  optional bytes syncCookie = 1;\n  repeated UinPairReadInfo pairInfo = 2;\n}\n\nmessage UinPairReadInfo {\n  optional uint64 peerUin = 1;\n  optional uint32 lastReadTime = 2;\n  optional bytes crmSig = 3;\n  optional uint32 peerType = 4;\n  optional uint32 chatType = 5;\n  optional uint64 cpid = 6;\n  optional uint32 aioType = 7;\n  optional uint64 toTinyId = 9;\n}\n\nmessage PbGroupReadedReportResp {\n  optional uint32 result = 1;\n  optional string errmsg = 2;\n  optional uint64 groupCode = 3;\n  optional uint64 memberSeq = 4;\n  optional uint64 groupMsgSeq = 5;\n}\n\nmessage PbDiscussReadedReportResp {\n  optional uint32 result = 1;\n  optional string errmsg = 2;\n  optional uint64 confUin = 3;\n  optional uint64 memberSeq = 4;\n  optional uint64 confSeq = 5;\n}\n\nmessage PbC2CReadedReportResp {\n  optional uint32 result = 1;\n  optional string errmsg = 2;\n  optional bytes syncCookie = 3;\n}"
  },
  {
    "path": "ricq-core/src/pb/msgtype0x210/subMsgType0x27.proto",
    "content": "syntax = \"proto2\";\n\npackage msgtype0x210;\n\nmessage AddGroup {\n  optional uint32 groupid = 1;\n  optional uint32 sortid = 2;\n  optional bytes groupname = 3;\n}\n\nmessage AppointmentNotify {\n  optional uint64 fromUin = 1;\n  optional string appointId = 2;\n  optional uint32 notifytype = 3;\n  optional string tipsContent = 4;\n  optional uint32 unreadCount = 5;\n  optional string joinWording = 6;\n  optional string viewWording = 7;\n  optional bytes sig = 8;\n  optional bytes eventInfo = 9;\n  optional bytes nearbyEventInfo = 10;\n  optional bytes feedEventInfo = 11;\n}\n\nmessage BinaryMsg {\n  optional uint32 opType = 1;\n  optional bytes opValue = 2;\n}\n\nmessage ChatMatchInfo {\n  optional bytes sig = 1;\n  optional uint64 uin = 2;\n  optional uint64 matchUin = 3;\n  optional bytes tipsWording = 4;\n  optional uint32 leftChatTime = 5;\n  optional uint64 timeStamp = 6;\n  optional uint32 matchExpiredTime = 7;\n  optional uint32 c2CExpiredTime = 8;\n  optional uint32 matchCount = 9;\n  optional bytes nick = 10;\n}\n\nmessage ConfMsgRoamFlag {\n  optional uint64 confid = 1;\n  optional uint32 flag = 2;\n  optional uint64 timestamp = 3;\n}\n\nmessage DaRenNotify {\n  optional uint64 uin = 1;\n  optional uint32 loginDays = 2;\n  optional uint32 days = 3;\n  optional uint32 isYestodayLogin = 4;\n  optional uint32 isTodayLogin = 5;\n}\n\nmessage DelFriend {\n  repeated uint64 uins = 1;\n}\n\nmessage DelGroup {\n  optional uint32 groupid = 1;\n}\n\nmessage FanpaiziNotify {\n  optional uint64 fromUin = 1;\n  optional string fromNick = 2;\n  optional bytes tipsContent = 3;\n  optional bytes sig = 4;\n}\n\nmessage ForwardBody {\n  optional uint32 notifyType = 1;\n  optional uint32 opType = 2;\n  optional AddGroup addGroup = 3;\n  optional DelGroup delGroup = 4;\n  optional ModGroupName modGroupName = 5;\n  optional ModGroupSort modGroupSort = 6;\n  optional ModFriendGroup modFriendGroup = 7;\n  optional ModProfile modProfile = 8;\n  optional ModFriendRemark modFriendRemark = 9;\n  optional ModLongNick modLongNick = 10;\n  optional ModCustomFace modCustomFace = 11;\n  optional ModGroupProfile modGroupProfile = 12;\n  optional ModGroupMemberProfile modGroupMemberProfile = 13;\n  optional DelFriend delFriend = 14;\n  optional ModFrdRoamPriv roamPriv = 15;\n  optional GrpMsgRoamFlag grpMsgRoamFlag = 16;\n  optional ConfMsgRoamFlag confMsgRoamFlag = 17;\n  optional ModLongNick modRichLongNick = 18;\n  optional BinaryMsg binPkg = 19;\n  optional ModSnsGeneralInfo modFriendRings = 20;\n  optional ModConfProfile modConfProfile = 21;\n  optional SnsUpdateFlag modFriendFlag = 22;\n  optional AppointmentNotify appointmentNotify = 23;\n  optional DaRenNotify darenNotify = 25;\n  optional NewComeinUserNotify newComeinUserNotify = 26;\n  optional PushSearchDev pushSearchDev = 200;\n  optional PushReportDev pushReportDev = 201;\n  optional QQPayPush qqPayPush = 202;\n  optional bytes redpointInfo = 203;\n  optional HotFriendNotify hotFriendNotify = 204;\n  optional PraiseRankNotify praiseRankNotify = 205;\n  optional MQQCampusNotify campusNotify = 210;\n  optional ModLongNick modRichLongNickEx = 211;\n  optional ChatMatchInfo chatMatchInfo = 212;\n  optional FrdCustomOnlineStatusChange frdCustomOnlineStatusChange = 214;\n  optional FanpaiziNotify fanpanziNotify = 2000;\n}\n\nmessage FrdCustomOnlineStatusChange {\n  optional uint64 uin = 1;\n}\n\nmessage FriendGroup {\n  optional uint64 fuin = 1;\n  repeated uint32 oldGroupId = 2;\n  repeated uint32 newGroupId = 3;\n}\n\nmessage FriendRemark {\n  optional uint32 type = 1;\n  optional uint64 fuin = 2;\n  optional bytes rmkName = 3;\n  optional uint64 groupCode = 4;\n}\n\nmessage GPS {\n  optional int32 lat = 1;\n  optional int32 lon = 2;\n  optional int32 alt = 3;\n  optional int32 type = 4;\n}\n\nmessage GroupMemberProfileInfo {\n  optional uint32 field = 1;\n  optional bytes value = 2;\n}\n\nmessage GroupProfileInfo {\n  optional uint32 field = 1;\n  optional bytes value = 2;\n}\n\nmessage GroupSort {\n  optional uint32 groupid = 1;\n  optional uint32 sortid = 2;\n}\n\nmessage GrpMsgRoamFlag {\n  optional uint64 groupcode = 1;\n  optional uint32 flag = 2;\n  optional uint64 timestamp = 3;\n}\n\nmessage HotFriendNotify {\n  optional uint64 dstUin = 1;\n  optional uint32 praiseHotLevel = 2;\n  optional uint32 chatHotLevel = 3;\n  optional uint32 praiseHotDays = 4;\n  optional uint32 chatHotDays = 5;\n  optional uint32 closeLevel = 6;\n  optional uint32 closeDays = 7;\n  optional uint32 praiseFlag = 8;\n  optional uint32 chatFlag = 9;\n  optional uint32 closeFlag = 10;\n  optional uint64 notifyTime = 11;\n  optional uint64 lastPraiseTime = 12;\n  optional uint64 lastChatTime = 13;\n  optional uint32 qzoneHotLevel = 14;\n  optional uint32 qzoneHotDays = 15;\n  optional uint32 qzoneFlag = 16;\n  optional uint64 lastQzoneTime = 17;\n}\n\nmessage MQQCampusNotify {\n  optional uint64 fromUin = 1;\n  optional string wording = 2;\n  optional string target = 3;\n  optional uint32 type = 4;\n  optional string source = 5;\n}\n\nmessage ModConfProfile {\n  optional uint64 uin = 1;\n  optional uint32 confUin = 2;\n  repeated ProfileInfo profileInfos = 3;\n}\n\nmessage ModCustomFace {\n  optional uint32 type = 1;\n  optional uint64 uin = 2;\n  optional uint64 groupCode = 3;\n  optional uint64 cmdUin = 4;\n}\n\nmessage ModFrdRoamPriv {\n  repeated OneRoamPriv roamPriv = 1;\n}\n\nmessage ModFriendGroup {\n  repeated FriendGroup frdGroup = 1;\n}\n\nmessage ModFriendRemark {\n  repeated FriendRemark frdRmk = 1;\n}\n\nmessage ModGroupMemberProfile {\n  optional uint64 groupUin = 1;\n  optional uint64 uin = 2;\n  repeated GroupMemberProfileInfo groupMemberProfileInfos = 3;\n  optional uint64 groupCode = 4;\n}\n\nmessage ModGroupName {\n  optional uint32 groupid = 1;\n  optional bytes groupname = 2;\n}\n\nmessage ModGroupProfile {\n  optional uint64 groupUin = 1;\n  repeated GroupProfileInfo groupProfileInfos = 2;\n  optional uint64 groupCode = 3;\n  optional uint64 cmdUin = 4;\n}\n\nmessage ModGroupSort {\n  repeated GroupSort groupsort = 1;\n}\n\nmessage ModLongNick {\n  optional uint64 uin = 1;\n  optional bytes value = 2;\n}\n\nmessage ModProfile {\n  optional uint64 uin = 1;\n  repeated ProfileInfo profileInfos = 2;\n}\n\nmessage ModSnsGeneralInfo {\n  repeated SnsUpateBuffer snsGeneralInfos = 1;\n}\n\nmessage SubMsg0x27Body {\n  repeated ForwardBody modInfos = 1;\n}\n\nmessage NewComeinUser {\n  optional uint64 uin = 1;\n  optional uint32 isFrd = 2;\n  optional bytes remark = 3;\n  optional bytes nick = 4;\n}\n\nmessage NewComeinUserNotify {\n  optional uint32 msgType = 1;\n  optional bool ongNotify = 2;\n  optional uint32 pushTime = 3;\n  optional NewComeinUser newComeinUser = 4;\n  optional NewGroup newGroup = 5;\n  optional NewGroupUser newGroupUser = 6;\n}\n\nmessage NewGroup {\n  optional uint64 groupCode = 1;\n  optional bytes groupName = 2;\n  optional uint64 ownerUin = 3;\n  optional bytes ownerNick = 4;\n  optional bytes distance = 5;\n}\n\nmessage NewGroupUser {\n  optional uint64 uin = 1;\n  optional int32 sex = 2;\n  optional int32 age = 3;\n  optional string nick = 4;\n  optional bytes distance = 5;\n}\n\nmessage OneRoamPriv {\n  optional uint64 fuin = 1;\n  optional uint32 privTag = 2;\n  optional uint32 privValue = 3;\n}\n\nmessage PraiseRankNotify {\n  optional uint32 isChampion = 11;\n  optional uint32 rankNum = 12;\n  optional string msg = 13;\n}\n\nmessage ProfileInfo {\n  optional uint32 field = 1;\n  optional bytes value = 2;\n}\n\nmessage PushReportDev {\n  optional uint32 msgType = 1;\n  optional bytes cookie = 4;\n  optional uint32 reportMaxNum = 5;\n  optional bytes sn = 6;\n}\n\nmessage PushSearchDev {\n  optional uint32 msgType = 1;\n  optional GPS gpsInfo = 2;\n  optional uint32 devTime = 3;\n  optional uint32 pushTime = 4;\n  optional uint64 din = 5;\n  optional string data = 6;\n}\n\nmessage QQPayPush {\n  optional uint64 uin = 1;\n  optional bool payOk = 2;\n}\n\nmessage SnsUpateBuffer {\n  optional uint64 uin = 1;\n  optional uint64 code = 2;\n  optional uint32 result = 3;\n  repeated SnsUpdateItem snsUpdateItem = 400;\n  repeated uint32 idlist = 401;\n}\n\nmessage SnsUpdateFlag {\n  repeated SnsUpdateOneFlag updateSnsFlag = 1;\n}\n\nmessage SnsUpdateItem {\n  optional uint32 updateSnsType = 1;\n  optional bytes value = 2;\n}\n\nmessage SnsUpdateOneFlag {\n  optional uint64 XUin = 1;\n  optional uint64 id = 2;\n  optional uint32 flag = 3;\n}"
  },
  {
    "path": "ricq-core/src/pb/multimsg/multimsg.proto",
    "content": "syntax = \"proto3\";\n\npackage multimsg;\n\nmessage ExternMsg {\n  int32 channelType = 1;\n}\nmessage MultiMsgApplyDownReq {\n  bytes msgResid = 1;\n  int32 msgType = 2;\n  int64 srcUin = 3;\n}\nmessage MultiMsgApplyDownRsp {\n  int32 result = 1;\n  bytes thumbDownPara = 2;\n  bytes msgKey = 3;\n  repeated uint32 downIp = 4;\n  repeated uint32 downPort = 5;\n  bytes msgResid = 6;\n  ExternMsg msgExternInfo = 7;\n  repeated bytes bytesDownIpV6 = 8;\n  repeated int32 uint32DownV6Port = 9;\n}\nmessage MultiMsgApplyUpReq {\n  int64 dstUin = 1;\n  int64 msgSize = 2;\n  bytes msgMd5 = 3;\n  int32 msgType = 4;\n  int32 applyId = 5;\n}\nmessage MultiMsgApplyUpRsp {\n  int32 result = 1;\n  string msgResid = 2;\n  bytes msgUkey = 3;\n  repeated int32 uint32UpIp = 4;\n  repeated int32 uint32UpPort = 5;\n  int64 blockSize = 6;\n  int64 upOffset = 7;\n  int32 applyId = 8;\n  bytes msgKey = 9;\n  bytes msgSig = 10;\n  ExternMsg msgExternInfo = 11;\n  repeated bytes bytesUpIpV6 = 12;\n  repeated int32 uint32UpV6Port = 13;\n}\nmessage MultiReqBody {\n  int32 subcmd = 1;\n  int32 termType = 2;\n  int32 platformType = 3;\n  int32 netType = 4;\n  string buildVer = 5;\n  repeated MultiMsgApplyUpReq multimsgApplyupReq = 6;\n  repeated MultiMsgApplyDownReq multimsgApplydownReq = 7;\n  int32 buType = 8;\n  int32 reqChannelType = 9;\n}\nmessage MultiRspBody {\n  int32 subcmd = 1;\n  repeated MultiMsgApplyUpRsp multimsgApplyupRsp = 2;\n  repeated MultiMsgApplyDownRsp multimsgApplydownRsp = 3;\n}"
  },
  {
    "path": "ricq-core/src/pb/notify/group0x857.proto",
    "content": "syntax = \"proto3\";\n\npackage notify;\n\nmessage NotifyMsgBody {\n  AIOGrayTipsInfo optMsgGrayTips = 5;\n  RedGrayTipsInfo optMsgRedTips = 9;\n  MessageRecallReminder optMsgRecall = 11;\n  GeneralGrayTipInfo optGeneralGrayTip = 26;\n  QQGroupDigestMsg qqGroupDigestMsg = 33;\n  int32 serviceType = 13;\n}\n\nmessage AIOGrayTipsInfo{\n  uint32 showLatest = 1;\n  bytes content = 2;\n  uint32 remind = 3;\n  bytes brief = 4;\n  uint64 receiverUin = 5;\n  uint32 reliaoAdminOpt = 6;\n}\n\nmessage GeneralGrayTipInfo {\n  uint64 busiType = 1;\n  uint64 busiId = 2;\n  uint32 ctrlFlag = 3;\n  uint32 c2cType = 4;\n  uint32 serviceType = 5;\n  uint64 templId = 6;\n  repeated TemplParam msgTemplParam = 7;\n  string content = 8;\n}\n\nmessage TemplParam {\n  string name = 1;\n  string value = 2;\n}\n\nmessage MessageRecallReminder {\n  int64 uin = 1;\n  bytes nickname = 2;\n  repeated RecalledMessageMeta recalledMsgList = 3;\n  bytes reminderContent = 4;\n  bytes userdef = 5;\n  int32 groupType = 6;\n  int32 opType = 7;\n}\n\nmessage RecalledMessageMeta {\n  int32 seq = 1;\n  int32 time = 2;\n  int32 msgRandom = 3;\n  int32 msgType = 4;\n  int32 msgFlag = 5;\n  int64 authorUin = 6;\n}\n\nmessage RedGrayTipsInfo {\n  uint32 showLatest = 1;\n  uint64 senderUin = 2;\n  uint64 receiverUin = 3;\n  string senderRichContent = 4;\n  string receiverRichContent = 5;\n  bytes authKey = 6;\n  sint32 msgType = 7;\n  uint32 luckyFlag = 8;\n  uint32 hideFlag = 9;\n  uint64 luckyUin = 12;\n}\n\nmessage QQGroupDigestMsg {\n  uint64 groupCode = 1;\n  uint32 seq = 2;\n  uint32 random = 3;\n  int32 opType = 4;\n  uint64 sender = 5;\n  uint64 digestOper = 6;\n  uint32 opTime = 7;\n  uint32 lastestMsgSeq = 8;\n  bytes operNick = 9;\n  bytes senderNick = 10;\n  int32 extInfo = 11;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage OIDBSSOPkg {\n  int32 command = 1;\n  int32 serviceType = 2;\n  int32 result = 3;\n  bytes bodybuffer = 4;\n  string errorMsg = 5;\n  string clientVersion = 6;\n}\n\nmessage D8A0RspBody {\n  int64 optUint64GroupCode = 1;\n  repeated D8A0KickResult msgKickResult = 2;\n}\nmessage D8A0KickResult {\n  int32 optUint32Result = 1;\n  int64 optUint64MemberUin = 2;\n}\nmessage D8A0KickMemberInfo {\n  int32 optUint32Operate = 1;\n  int64 optUint64MemberUin = 2;\n  int32 optUint32Flag = 3;\n  bytes optBytesMsg = 4;\n}\nmessage D8A0ReqBody {\n  int64 optUint64GroupCode = 1;\n  repeated D8A0KickMemberInfo msgKickList = 2;\n  repeated int64 kickList = 3;\n  int32 kickFlag = 4;\n  bytes kickMsg = 5;\n}\n\nmessage D89AReqBody {\n  int64 groupCode = 1;\n  D89AGroupinfo stGroupInfo = 2;\n  int64 originalOperatorUin = 3;\n  int32 reqGroupOpenAppid = 4;\n}\n\nmessage D89AGroupinfo {\n  int32 groupExtAdmNum = 1;\n  int32 flag = 2;\n  bytes ingGroupName = 3;\n  bytes ingGroupMemo = 4;\n  bytes ingGroupFingerMemo = 5;\n  bytes ingGroupAioSkinUrl = 6;\n  bytes ingGroupBoardSkinUrl = 7;\n  bytes ingGroupCoverSkinUrl = 8;\n  int32 groupGrade = 9;\n  int32 activeMemberNum = 10;\n  int32 certificationType = 11;\n  bytes ingCertificationText = 12;\n  bytes ingGroupRichFingerMemo = 13;\n  D89AGroupNewGuidelinesInfo stGroupNewguidelines = 14;\n  int32 groupFace = 15;\n  int32 addOption = 16;\n  oneof shutupTime {\n    int32 val = 17;\n  }\n  int32 groupTypeFlag = 18;\n  bytes stringGroupTag = 19;\n  D89AGroupGeoInfo msgGroupGeoInfo = 20;\n  int32 groupClassExt = 21;\n  bytes ingGroupClassText = 22;\n  int32 appPrivilegeFlag = 23;\n  int32 appPrivilegeMask = 24;\n  D89AGroupExInfoOnly stGroupExInfo = 25;\n  int32 groupSecLevel = 26;\n  int32 groupSecLevelInfo = 27;\n  int64 subscriptionUin = 28;\n  int32 allowMemberInvite = 29;\n  bytes ingGroupQuestion = 30;\n  bytes ingGroupAnswer = 31;\n  int32 groupFlagext3 = 32;\n  int32 groupFlagext3Mask = 33;\n  int32 groupOpenAppid = 34;\n  int32 noFingerOpenFlag = 35;\n  int32 noCodeFingerOpenFlag = 36;\n  int64 rootId = 37;\n  int32 msgLimitFrequency = 38;\n}\nmessage D89AGroupNewGuidelinesInfo {\n  bool boolEnabled = 1;\n  bytes ingContent = 2;\n}\nmessage D89AGroupExInfoOnly {\n  int32 tribeId = 1;\n  int32 moneyForAddGroup = 2;\n}\n\nmessage D89AGroupGeoInfo {\n  int32 cityId = 1;\n  int64 longtitude = 2;\n  int64 latitude = 3;\n  bytes ingGeoContent = 4;\n  int64 poiId = 5;\n}\n\nmessage DED3ReqBody {\n  int64 toUin = 1;\n  int64 groupCode = 2;\n  int32 msgSeq = 3;\n  int32 msgRand = 4;\n  int64 aioUin = 5;\n}"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x6d6.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage DeleteFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string parentFolderId = 4;\n  optional string fileId = 5;\n}\nmessage DeleteFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n}\nmessage DownloadFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string fileId = 4;\n  optional bool boolThumbnailReq = 5;\n  optional int32 urlType = 6;\n  optional bool boolPreviewReq = 7;\n}\nmessage DownloadFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional string downloadIp = 4;\n  optional bytes downloadDns = 5;\n  optional bytes downloadUrl = 6;\n  optional bytes sha = 7;\n  optional bytes sha3 = 8;\n  optional bytes md5 = 9;\n  optional bytes cookieVal = 10;\n  optional string saveFileName = 11;\n  optional int32 previewPort = 12;\n}\nmessage MoveFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string fileId = 4;\n  optional string parentFolderId = 5;\n  optional string destFolderId = 6;\n}\nmessage MoveFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional string parentFolderId = 4;\n}\nmessage RenameFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string fileId = 4;\n  optional string parentFolderId = 5;\n  optional string newFileName = 6;\n}\nmessage RenameFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n}\nmessage D6D6ReqBody {\n  optional UploadFileReqBody uploadFileReq = 1;\n  optional ResendReqBody resendFileReq = 2;\n  optional DownloadFileReqBody downloadFileReq = 3;\n  optional DeleteFileReqBody deleteFileReq = 4;\n  optional RenameFileReqBody renameFileReq = 5;\n  optional MoveFileReqBody moveFileReq = 6;\n}\nmessage ResendReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string fileId = 4;\n  optional bytes sha = 5;\n}\nmessage ResendRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional string uploadIp = 4;\n  optional bytes fileKey = 5;\n  optional bytes checkKey = 6;\n}\nmessage D6D6RspBody {\n  optional UploadFileRspBody uploadFileRsp = 1;\n  optional ResendRspBody resendFileRsp = 2;\n  optional DownloadFileRspBody downloadFileRsp = 3;\n  optional DeleteFileRspBody deleteFileRsp = 4;\n  optional RenameFileRspBody renameFileRsp = 5;\n  optional MoveFileRspBody moveFileRsp = 6;\n}\nmessage UploadFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional int32 entrance = 4;\n  optional string parentFolderId = 5;\n  optional string fileName = 6;\n  optional string localPath = 7;\n  optional int64 int64FileSize = 8;\n  optional bytes sha = 9;\n  optional bytes sha3 = 10;\n  optional bytes md5 = 11;\n  optional bool supportMultiUpload = 15;\n}\nmessage UploadFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional string uploadIp = 4;\n  optional string serverDns = 5;\n  optional int32 busId = 6;\n  optional string fileId = 7;\n  optional bytes fileKey = 8;\n  optional bytes checkKey = 9;\n  optional bool boolFileExist = 10;\n  repeated string uploadIpLanV4 = 12;\n  repeated string uploadIpLanV6 = 13;\n  optional int32 uploadPort = 14;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x6d8.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage D6D8ReqBody {\n  optional GetFileInfoReqBody fileInfoReq = 1;\n  optional GetFileListReqBody fileListInfoReq = 2;\n  optional GetFileCountReqBody groupFileCountReq = 3;\n  optional GetSpaceReqBody groupSpaceReq = 4;\n}\n\nmessage D6D8RspBody {\n  optional GetFileInfoRspBody fileInfoRsp = 1;\n  optional GetFileListRspBody fileListInfoRsp = 2;\n  optional GetFileCountRspBody fileCountRsp = 3;\n  optional GetSpaceRspBody groupSpaceRsp = 4;\n}\n\nmessage GetFileInfoReqBody {\n  optional uint64 groupCode = 1;\n  optional uint32 appId = 2;\n  optional uint32 busId = 3;\n  optional string fileId = 4;\n  optional uint32 fieldFlag = 5;\n}\n\nmessage GetFileInfoRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional GroupFileInfo fileInfo = 4;\n}\n\nmessage GetFileListRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional bool isEnd = 4;\n  message Item {\n    optional uint32 type = 1;\n    optional GroupFolderInfo folderInfo = 2;\n    optional GroupFileInfo fileInfo = 3;\n  }\n  repeated Item itemList = 5;\n  optional FileTimeStamp maxTimestamp = 6;\n  optional uint32 allFileCount = 7;\n  optional uint32 filterCode = 8;\n  optional bool safeCheckFlag = 11;\n  optional uint32 safeCheckRes = 12;\n  optional uint32 nextIndex = 13;\n  optional bytes context = 14;\n  optional uint32 role = 15;\n  optional uint32 openFlag = 16;\n}\n\nmessage GroupFileInfo {/* renamed from FileInfo */\n  optional string fileId = 1;\n  optional string fileName = 2;\n  optional uint64 fileSize = 3;\n  optional uint32 busId = 4;\n  optional uint64 uploadedSize = 5;\n  optional uint32 uploadTime = 6;\n  optional uint32 deadTime = 7;\n  optional uint32 modifyTime = 8;\n  optional uint32 downloadTimes = 9;\n  optional bytes sha = 10;\n  optional bytes sha3 = 11;\n  optional bytes md5 = 12;\n  optional string localPath = 13;\n  optional string uploaderName = 14;\n  optional uint64 uploaderUin = 15;\n  optional string parentFolderId = 16;\n}\n\nmessage GroupFolderInfo {/* renamed from FolderInfo */\n  optional string folderId = 1;\n  optional string parentFolderId = 2;\n  optional string folderName = 3;\n  optional uint32 createTime = 4;\n  optional uint32 modifyTime = 5;\n  optional uint64 createUin = 6;\n  optional string creatorName = 7;\n  optional uint32 totalFileCount = 8;\n}\n\n\nmessage GetFileListReqBody {\n  optional uint64 groupCode = 1;\n  optional uint32 appId = 2;\n  optional string folderId = 3;\n  optional FileTimeStamp startTimestamp = 4;\n  optional uint32 fileCount = 5;\n  optional FileTimeStamp maxTimestamp = 6;\n  optional uint32 allFileCount = 7;\n  optional uint32 reqFrom = 8;\n  optional uint32 sortBy = 9;\n  optional uint32 filterCode = 10;\n  optional uint64 uin = 11;\n  optional uint32 fieldFlag = 12;\n  optional uint32 startIndex = 13;\n  optional bytes context = 14;\n  optional uint32 clientVersion = 15;\n}\n\nmessage GetFileCountReqBody {\n  optional uint64 groupCode = 1;\n  optional uint32 appId = 2;\n  optional uint32 busId = 3;\n}\n\nmessage GetSpaceReqBody {\n  optional uint64 groupCode = 1;\n  optional uint32 appId = 2;\n}\n\nmessage GetFileCountRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional uint32 allFileCount = 4;\n  optional bool fileTooMany = 5;\n  optional uint32 limitCount = 6;\n  optional bool isFull = 7;\n}\n\nmessage GetSpaceRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional uint64 totalSpace = 4;\n  optional uint64 usedSpace = 5;\n}\n\nmessage FileTimeStamp {\n  optional uint32 uploadTime = 1;\n  optional string fileId = 2;\n}"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x758.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage InviteUinInfo {\n  optional uint64 uin = 1;\n  optional uint64 judgeGroupCode = 2;\n  optional uint64 judgeConfCode = 3;\n}\n\nmessage D758ReqBody {\n  optional uint64 joinGroupCode = 1;\n  repeated InviteUinInfo beInvitedUinInfo = 2;\n  optional string msg = 3;\n  optional uint32 mainSourceId = 4;\n  optional uint32 subSourceId = 5;\n  optional string verifyToken = 6;\n  optional uint32 verifyType = 7;\n}\n\nmessage D758RspBody {\n  optional uint64 groupCode = 1;\n  optional uint64 currentMaxMsgseq = 2;\n  optional string verifyUrl = 3;\n  optional uint32 verifyType = 4;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x769.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage CPU {\n  optional string model = 1;\n  optional uint32 cores = 2;\n  optional uint32 frequency = 3;\n}\n\nmessage Camera {\n  optional uint64 primary = 1;\n  optional uint64 secondary = 2;\n  optional bool flash = 3;\n}\n\nmessage D769ConfigSeq {\n  optional uint32 type = 1;\n  optional uint32 version = 2;\n}\n\nmessage Content {\n  optional uint32 taskId = 1;\n  optional uint32 compress = 2;\n  optional bytes content = 10;\n}\n\nmessage D769DeviceInfo {\n  optional string brand = 1;\n  optional string model = 2;\n  optional C41219OS os = 3;\n  optional CPU cpu = 4;\n  optional Memory memory = 5;\n  optional Storage storage = 6;\n  optional Screen screen = 7;\n  optional Camera camera = 8;\n}\n\nmessage Memory {\n  optional uint64 total = 1;\n  optional uint64 process = 2;\n}\n\nmessage C41219OS {\n  optional uint32 type = 1;\n  optional string version = 2;\n  optional string sdk = 3;\n  optional string kernel = 4;\n  optional string rom = 5;\n}\n\nmessage QueryUinPackageUsageReq {\n  optional uint32 type = 1;\n  optional uint64 uinFileSize = 2;\n}\n\nmessage QueryUinPackageUsageRsp {\n  optional uint32 status = 1;\n  optional uint64 leftUinNum = 2;\n  optional uint64 maxUinNum = 3;\n  optional uint32 proportion = 4;\n  repeated UinPackageUsedInfo uinPackageUsedList = 10;\n}\n\nmessage D769ReqBody {\n  repeated D769ConfigSeq configList = 1;\n  optional D769DeviceInfo deviceInfo = 2;\n  optional string info = 3;\n  optional string province = 4;\n  optional string city = 5;\n  optional int32 reqDebugMsg = 6;\n  optional QueryUinPackageUsageReq queryUinPackageUsageReq = 101;\n}\n\nmessage D769RspBody {\n  optional uint32 result = 1;\n  repeated D769ConfigSeq configList = 2;\n  optional QueryUinPackageUsageRsp queryUinPackageUsageRsp = 101;\n}\n\nmessage Screen {\n  optional string model = 1;\n  optional uint32 width = 2;\n  optional uint32 height = 3;\n  optional uint32 dpi = 4;\n  optional bool multiTouch = 5;\n}\n\nmessage Storage {\n  optional uint64 builtin = 1;\n  optional uint64 external = 2;\n}\n\nmessage UinPackageUsedInfo {\n  optional uint32 ruleId = 1;\n  optional string author = 2;\n  optional string url = 3;\n  optional uint64 uinNum = 4;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x88d.proto",
    "content": "syntax = \"proto2\"; // 似乎查询服务端是通过 exists flag 来返回 group info 的 这地方只能用 proto2\n\npackage oidb;\n\nmessage D88DGroupHeadPortraitInfo\n{\n  optional uint32 picId = 1;\n}\n\nmessage D88DGroupHeadPortrait\n{\n  optional uint32 picCount = 1;\n  repeated D88DGroupHeadPortraitInfo msgInfo = 2;\n  optional uint32 defaultId = 3;\n  optional uint32 verifyingPicCnt = 4;\n  repeated D88DGroupHeadPortraitInfo msgVerifyingPicInfo = 5;\n}\n\nmessage D88DGroupExInfoOnly\n{\n  optional uint32 tribeId = 1;\n  optional uint32 moneyForAddGroup = 2;\n};\n\nmessage D88DGroupInfo\n{\n  optional uint64 groupOwner = 1;\n  optional uint32 groupCreateTime = 2;\n  optional uint32 groupFlag = 3;\n  optional uint32 groupFlagExt = 4;\n  optional uint32 groupMemberMaxNum = 5;\n  optional uint32 groupMemberNum = 6;\n  optional uint32 groupOption = 7;\n  optional uint32 groupClassExt = 8;\n  optional uint32 groupSpecialClass = 9;\n  optional uint32 groupLevel = 10;\n  optional uint32 groupFace = 11;\n  optional uint32 groupDefaultPage = 12;\n  optional uint32 groupInfoSeq = 13;\n  optional uint32 groupRoamingTime = 14;\n  optional bytes  groupName = 15;\n  optional bytes  groupMemo = 16;\n  optional bytes  groupFingerMemo = 17;\n  optional bytes  groupClassText = 18;\n  repeated uint32 groupAllianceCode = 19;\n  optional uint32 groupExtraAadmNum = 20;\n  optional uint64 groupUin = 21;\n  optional uint32 groupCurMsgSeq = 22;\n  optional uint32 groupLastMsgTime = 23;\n  optional bytes  groupQuestion = 24;\n  optional bytes  groupAnswer = 25;\n  optional uint32 groupVisitorMaxNum = 26;\n  optional uint32 groupVisitorCurNum = 27;\n  optional uint32 levelNameSeq = 28;\n  optional uint32 groupAdminMaxNum = 29;\n  optional uint32 groupAioSkinTimestamp = 30;\n  optional uint32 groupBoardSkinTimestamp = 31;\n  optional bytes  groupAioSkinUrl = 32;\n  optional bytes  groupBoardSkinUrl = 33;\n  optional uint32 groupCoverSkinTimestamp = 34;\n  optional bytes  groupCoverSkinUrl = 35;\n  optional uint32 groupGrade = 36;\n  optional uint32 activeMemberNum = 37;\n  optional uint32 certificationType = 38;\n  optional bytes  certificationText = 39;\n  optional bytes  groupRichFingerMemo = 40;\n  repeated D88DTagRecord tagRecord = 41;\n  optional D88DGroupGeoInfo groupGeoInfo = 42;\n  optional uint32 headPortraitSeq = 43;\n  optional D88DGroupHeadPortrait msgHeadPortrait = 44;\n  optional uint32 shutupTimestamp = 45 ;\n  optional uint32 shutupTimestampMe = 46 ;\n  optional uint32 createSourceFlag = 47 ;\n  optional uint32 cmduinMsgSeq = 48;\n  optional uint32 cmduinJoinTime = 49;\n  optional uint32 cmduinUinFlag = 50;\n  optional uint32 cmduinFlagEx = 51;\n  optional uint32 cmduinNewMobileFlag = 52;\n  optional uint32 cmduinReadMsgSeq = 53;\n  optional uint32 cmduinLastMsgTime = 54;\n  optional uint32 groupTypeFlag = 55;\n  optional uint32 appPrivilegeFlag = 56;\n  optional D88DGroupExInfoOnly stGroupExInfo = 57;\n  optional uint32 groupSecLevel = 58;\n  optional uint32 groupSecLevelInfo = 59;\n  optional uint32 cmduinPrivilege = 60;\n  optional bytes  poidInfo = 61;\n  optional uint32 cmduinFlagEx2 = 62;\n  optional uint64 confUin = 63;\n  optional uint32 confMaxMsgSeq = 64;\n  optional uint32 confToGroupTime = 65;\n  optional uint32 passwordRedbagTime = 66;\n  optional uint64 subscriptionUin = 67;\n  optional uint32 memberListChangeSeq = 68;\n  optional uint32 membercardSeq = 69;\n  optional uint64 rootId = 70;\n  optional uint64 parentId = 71;\n  optional uint32 teamSeq = 72;\n  optional uint64 historyMsgBeginTime = 73;\n  optional uint64 inviteNoAuthNumLimit = 74;\n  optional uint32 cmduinHistoryMsgSeq = 75;\n  optional uint32 cmduinJoinMsgSeq = 76;\n  optional uint32 groupFlagext3 = 77;\n  optional uint32 groupOpenAppid = 78;\n  optional uint32 isConfGroup = 79;\n  optional uint32 isModifyConfGroupFace = 80;\n  optional uint32 isModifyConfGroupName = 81;\n  optional uint32 noFingerOpenFlag = 82;\n  optional uint32 noCodeFingerOpenFlag = 83;\n};\n\nmessage ReqGroupInfo\n{\n  optional uint64 groupCode = 1;\n  optional D88DGroupInfo stgroupinfo = 2;\n  optional uint32 lastGetGroupNameTime = 3;\n};\n\nmessage D88DReqBody\n{\n  optional uint32 appId = 1;\n  repeated ReqGroupInfo reqGroupInfo = 2;\n  optional uint32 pcClientVersion = 3;\n};\n\nmessage RspGroupInfo\n{\n  optional uint64 groupCode = 1;\n  optional uint32 result = 2;\n  optional D88DGroupInfo groupInfo = 3;\n};\n\nmessage D88DRspBody\n{\n  repeated RspGroupInfo rspGroupInfo = 1;\n  optional bytes  strErrorInfo = 2;\n};\n\nmessage D88DTagRecord\n{\n  optional uint64 fromUin = 1;\n  optional uint64 groupCode = 2;\n  optional bytes  tagId = 3;\n  optional uint64 setTime = 4;\n  optional uint32 goodNum = 5;\n  optional uint32 badNum = 6;\n  optional uint32 tagLen = 7;\n  optional bytes  tagValue = 8;\n};\n\nmessage D88DGroupGeoInfo\n{\n  optional uint64 owneruin = 1;\n  optional uint32 settime = 2;\n  optional uint32 cityid = 3;\n  optional int64 longitude = 4;\n  optional int64 latitude = 5;\n  optional bytes  geocontent = 6;\n  optional uint64 poiId = 7;\n};\n\n"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x8a7.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage D8A7ReqBody {\n  optional uint32 subCmd = 1;\n  optional uint32 limitIntervalTypeForUin = 2;\n  optional uint32 limitIntervalTypeForGroup = 3;\n  optional uint64 uin = 4;\n  optional uint64 groupCode = 5;\n}\nmessage D8A7RspBody {\n  optional bool canAtAll = 1;\n  optional uint32 remainAtAllCountForUin = 2;\n  optional uint32 remainAtAllCountForGroup = 3;\n  optional bytes promptMsg1 = 4;\n  optional bytes promptMsg2 = 5;\n}"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x8fc.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage D8FCReqBody {\n  optional int64 groupCode = 1;\n  optional int32 showFlag = 2;\n  repeated D8FCMemberInfo memLevelInfo = 3;\n  repeated D8FCLevelName levelName = 4;\n  optional int32 updateTime = 5;\n  optional int32 officeMode = 6;\n  optional int32 groupOpenAppid = 7;\n  optional D8FCClientInfo msgClientInfo = 8;\n  optional bytes authKey = 9;\n}\n\nmessage D8FCMemberInfo {\n  optional int64 uin = 1;\n  optional int32 point = 2;\n  optional int32 activeDay = 3;\n  optional int32 level = 4;\n  optional bytes specialTitle = 5;\n  optional int32 specialTitleExpireTime = 6;\n  optional bytes uinName = 7;\n  optional bytes memberCardName = 8;\n  optional bytes phone = 9;\n  optional bytes email = 10;\n  optional bytes remark = 11;\n  optional int32 gender = 12;\n  optional bytes job = 13;\n  optional int32 tribeLevel = 14;\n  optional int32 tribePoint = 15;\n  repeated D8FCCardNameElem richCardName = 16;\n  optional bytes commRichCardName = 17;\n}\n\nmessage D8FCCardNameElem {\n  optional int32 enumCardType = 1;\n  optional bytes value = 2;\n}\n\nmessage D8FCLevelName {\n  optional int32 level = 1;\n  optional string name = 2;\n}\n\nmessage D8FCClientInfo {\n  optional int32 implat = 1;\n  optional string ingClientver = 2;\n}\n\nmessage D8FCCommCardNameBuf {\n  repeated D8FCRichCardNameElem richCardName = 1;\n}\n\nmessage D8FCRichCardNameElem {\n  optional bytes ctrl = 1;\n  optional bytes text = 2;\n}"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0x990.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage TranslateReqBody {\n  // TranslateReq translate_req = 1;\n  BatchTranslateReq batch_translate_req = 2;\n}\n\nmessage TranslateRspBody {\n  // TranslateRsp translate_rsp = 1;\n  BatchTranslateRsp batch_translate_rsp = 2;\n}\n\nmessage BatchTranslateReq {\n  string src_language = 1;\n  string dst_language = 2;\n  repeated string src_text_list = 3;\n}\n\nmessage BatchTranslateRsp {\n  int32 error_code = 1;\n  bytes error_msg = 2;\n  string src_language = 3;\n  string dst_language = 4;\n  repeated string src_text_list = 5;\n  repeated string dst_text_list = 6;\n}"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0xb77.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage DB77ReqBody {\n  uint64 appId = 1;\n  uint32 appType = 2;\n  uint32 msgStyle = 3;\n  uint64 senderUin = 4;\n  DB77ClientInfo clientInfo = 5;\n  string textMsg = 6;\n  DB77ExtInfo extInfo = 7;\n  uint32 sendType = 10;\n  uint64 recvUin = 11;\n  DB77RichMsgBody richMsgBody = 12;\n  uint64 recvGuildId = 19;\n}\n\nmessage DB77ClientInfo {\n  uint32 platform = 1;\n  string sdkVersion = 2;\n  string androidPackageName = 3;\n  string androidSignature = 4;\n  string iosBundleId = 5;\n  string pcSign = 6;\n}\n\nmessage DB77ExtInfo {\n  repeated uint32 customFeatureId = 11;\n  string apnsWording = 12;\n  uint32 groupSaveDbFlag = 13;\n  uint32 receiverAppId = 14;\n  uint64 msgSeq = 15;\n}\n\nmessage DB77RichMsgBody {\n  string title = 10;\n  string summary  = 11;\n  string brief = 12;\n  string url = 13;\n  string pictureUrl = 14;\n  string action = 15;\n  string musicUrl = 16;\n  //ImageInfo imageInfo = 17;\n}"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0xe07.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage DE07ReqBody {\n  int32 version = 1;\n  int32 client = 2;\n  int32 entrance = 3;\n  OCRReqBody ocrReqBody = 10;\n}\n\nmessage OCRReqBody {\n  string imageUrl = 1;\n  string languageType = 2;\n  string scene = 3;\n  string originMd5 = 10;\n  string afterCompressMd5 = 11;\n  int32 afterCompressFileSize = 12;\n  int32 afterCompressWeight = 13;\n  int32 afterCompressHeight = 14;\n  bool isCut = 15;\n}\n\nmessage DE07RspBody {\n  int32 retCode = 1;\n  string errMsg = 2;\n  string wording = 3;\n  OCRRspBody ocrRspBody = 10;\n}\n\nmessage TextDetection {\n  string detectedText = 1;\n  int32 confidence = 2;\n  Polygon polygon = 3;\n  string advancedInfo = 4;\n}\n\nmessage Polygon {\n  repeated Coordinate coordinates = 1;\n}\n\nmessage Coordinate {\n  int32 X = 1;\n  int32 Y = 2;\n}\n\nmessage Language {\n  string language = 1;\n  string languageDesc = 2;\n}\n\nmessage OCRRspBody {\n  repeated TextDetection textDetections = 1;\n  string language = 2;\n  string requestId = 3;\n  repeated string ocrLanguageList = 101;\n  repeated string dstTranslateLanguageList = 102;\n  repeated Language languageList = 103;\n  int32 afterCompressWeight = 111;\n  int32 afterCompressHeight = 112;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0xeac.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\n/*\nmessage ArkMsg {\n  optional string appName = 1;\n  optional string json = 2;\n}\n\nmessage BatchReqBody {\n  optional uint64 groupCode = 1;\n  repeated MsgInfo msgs = 2;\n}\n\nmessage BatchRspBody {\n  optional string wording = 1;\n  optional uint32 errorCode = 2;\n  optional int32 succCnt = 3;\n  repeated MsgProcessInfo procInfos = 4;\n  optional uint32 digestTime = 5;\n}\n\nmessage DigestMsg {\n  optional uint64 groupCode = 1;\n  optional uint32 seq = 2;\n  optional uint32 random = 3;\n  repeated MsgElem content = 4;\n  optional uint64 textSize = 5;\n  optional uint64 picSize = 6;\n  optional uint64 videoSize = 7;\n  optional uint64 senderUin = 8;\n  optional uint32 senderTime = 9;\n  optional uint64 addDigestUin = 10;\n  optional uint32 addDigestTime = 11;\n  optional uint32 startTime = 12;\n  optional uint32 latestMsgSeq = 13;\n  optional uint32 opType = 14;\n}\n\nmessage FaceMsg {\n  optional uint32 index = 1;\n  optional string text = 2;\n}\n\nmessage GroupFileMsg {\n  optional bytes fileName = 1;\n  optional uint32 busId = 2;\n  optional string fileId = 3;\n  optional uint64 fileSize = 4;\n  optional uint64 deadTime = 5;\n  optional bytes fileSha1 = 6;\n  optional bytes ext = 7;\n  optional bytes fileMd5 = 8;\n}\n\nmessage ImageMsg {\n  optional string md5 = 1;\n  optional string uuid = 2;\n  optional uint32 imgType = 3;\n  optional uint32 fileSize = 4;\n  optional uint32 width = 5;\n  optional uint32 height = 6;\n  optional uint32 fileId = 101;\n  optional uint32 serverIp = 102;\n  optional uint32 serverPort = 103;\n  optional string filePath = 104;\n  optional string thumbUrl = 201;\n  optional string originalUrl = 202;\n  optional string resaveUrl = 203;\n}\n\nmessage MsgElem {\n  optional uint32 type = 1;\n  optional TextMsg textMsg = 11;\n  optional FaceMsg faceMsg = 12;\n  optional ImageMsg imageMsg = 13;\n  optional GroupFileMsg groupFileMsg = 14;\n  optional ShareMsg shareMsg = 15;\n  optional RichMsg richMsg = 16;\n  optional ArkMsg arkMsg = 17;\n}\n\nmessage MsgInfo {\n  optional uint32 seq = 1;\n  optional uint32 random = 2;\n}\n\nmessage MsgProcessInfo {\n  optional MsgInfo msg = 1;\n  optional uint32 errorCode = 2;\n  optional uint64 digestUin = 3;\n  optional uint32 digestTime = 4;\n}\n*/\n\nmessage EACReqBody {\n  optional uint64 groupCode = 1;\n  optional uint32 seq = 2;\n  optional uint32 random = 3;\n}\n\n/*\nmessage RichMsg {\n  optional uint32 serviceId = 1;\n  optional string xml = 2;\n  optional string longMsgResid = 3;\n}\n*/\n\nmessage EACRspBody {\n  optional string wording = 1;\n  optional uint64 digestUin = 2;\n  optional uint32 digestTime = 3;\n  //optional DigestMsg msg = 4;\n  optional uint32 errorCode = 10;\n}\n\n/*\nmessage ShareMsg {\n  optional string type = 1;\n  optional string title = 2;\n  optional string summary = 3;\n  optional string brief = 4;\n  optional string url = 5;\n  optional string pictureUrl = 6;\n  optional string action = 7;\n  optional string source = 8;\n  optional string sourceUrl = 9;\n}\n\nmessage TextMsg {\n  optional bytes str = 1;\n}\n */"
  },
  {
    "path": "ricq-core/src/pb/oidb/oidb0xeb7.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\n// DEB7 prefix\nmessage DEB7ReqBody {\n  optional StSignInStatusReq signInStatusReq = 1;\n  optional StSignInWriteReq signInWriteReq = 2;\n}\n\nmessage DEB7Ret {\n  optional uint32 code = 1;\n  optional string msg = 2;\n}\n\nmessage DEB7RspBody {\n  optional StSignInStatusRsp signInStatusRsp = 1;\n  optional StSignInWriteRsp signInWriteRsp = 2;\n}\n\nmessage SignInStatusBase {\n  optional uint32 status = 1;\n  optional int64 currentTimeStamp = 2;\n}\n\nmessage SignInStatusDoneInfo {\n  optional string leftTitleWrod = 1;\n  optional string rightDescWord = 2;\n  repeated string belowPortraitWords = 3;\n  optional string recordUrl = 4;\n}\n\nmessage SignInStatusGroupScore {\n  optional string groupScoreWord = 1;\n  optional string scoreUrl = 2;\n}\n\nmessage SignInStatusNotInfo {\n  optional string buttonWord = 1;\n  optional string signDescWordLeft = 2;\n  optional string signDescWordRight = 3;\n}\n\nmessage SignInStatusYesterdayFirst {\n  optional string yesterdayFirstUid = 1;\n  optional string yesterdayWord = 2;\n  optional string yesterdayNick = 3;\n}\n\nmessage StDaySignedInfo {\n  optional string uid = 1;\n  optional string uidGroupNick = 2;\n  optional int64 signedTimeStamp = 3;\n  optional int32 signInRank = 4;\n}\n\nmessage StDaySignedListReq {\n  optional string dayYmd = 1;\n  optional string uid = 2;\n  optional string groupId = 3;\n  optional int32 offset = 4;\n  optional int32 limit = 5;\n}\n\nmessage StDaySignedListRsp {\n  optional DEB7Ret ret = 1;\n  repeated StDaySignedPage page = 2;\n}\n\nmessage StDaySignedPage {\n  repeated StDaySignedInfo infos = 1;\n  optional int32 offset = 2;\n  optional int32 total = 3;\n}\n\nmessage StKingSignedInfo {\n  optional string uid = 1;\n  optional string groupNick = 2;\n  optional int64 signedTimeStamp = 3;\n  optional int32 signedCount = 4;\n}\n\nmessage StKingSignedListReq {\n  optional string uid = 1;\n  optional string groupId = 2;\n}\n\nmessage StKingSignedListRsp {\n  optional DEB7Ret ret = 1;\n  optional StKingSignedInfo yesterdayFirst = 2;\n  repeated StKingSignedInfo topSignedTotal = 3;\n  repeated StKingSignedInfo topSignedContinue = 4;\n}\n\nmessage StSignInRecordDaySigned {\n  optional float daySignedRatio = 1;\n  optional int32 dayTotalSignedUid = 2;\n  optional StDaySignedPage daySignedPage = 3;\n  optional string daySignedUrl = 4;\n}\n\nmessage StSignInRecordKing {\n  optional StKingSignedInfo yesterdayFirst = 1;\n  repeated StKingSignedInfo topSignedTotal = 2;\n  repeated StKingSignedInfo topSignedContinue = 3;\n  optional string kingUrl = 4;\n}\n\nmessage StSignInRecordReq {\n  optional string dayYmd = 1;\n  optional string uid = 2;\n  optional string groupId = 3;\n}\n\nmessage StSignInRecordRsp {\n  optional DEB7Ret ret = 1;\n  optional SignInStatusBase base = 2;\n  optional StSignInRecordUser userRecord = 3;\n  optional StSignInRecordDaySigned daySigned = 4;\n  optional StSignInRecordKing kingRecord = 5;\n  optional StViewGroupLevel level = 6;\n}\n\nmessage StSignInRecordUser {\n  optional int32 totalSignedDays = 2;\n  optional int64 earliestSignedTimeStamp = 3;\n  optional int64 continueSignedDays = 4;\n  repeated string historySignedDays = 5;\n  optional string groupName = 6;\n}\n\nmessage StSignInStatusReq {\n  optional string uid = 1;\n  optional string groupId = 2;\n  optional uint32 scene = 3;\n  optional string clientVersion = 4;\n}\n\nmessage StSignInStatusRsp {\n  optional DEB7Ret ret = 1;\n  optional SignInStatusBase base = 2;\n  optional SignInStatusYesterdayFirst yesterday = 3;\n  optional SignInStatusNotInfo notInfo = 4;\n  optional SignInStatusDoneInfo doneInfo = 5;\n  optional SignInStatusGroupScore groupScore = 6;\n  optional string mantleUrl = 7;\n  optional string backgroundUrl = 8;\n}\n\nmessage StSignInWriteReq {\n  optional string uid = 1;\n  optional string groupId = 2;\n  optional string clientVersion = 3;\n}\n\nmessage StSignInWriteRsp {\n  optional DEB7Ret ret = 1;\n  optional SignInStatusDoneInfo doneInfo = 2;\n  optional SignInStatusGroupScore groupScore = 3;\n}\n\nmessage StViewGroupLevel {\n  optional string title = 1;\n  optional string url = 2;\n}"
  },
  {
    "path": "ricq-core/src/pb/online_status/OnlineStatusExtInfo.java.proto",
    "content": "syntax = \"proto2\";\n\npackage online_status;\n\nmessage AutoStateBizInfo {\n  optional uint64 updateTime = 1;\n}\n\nmessage CustomStatus {\n  optional uint64 faceIndex = 1;\n  optional string wording = 2;\n  optional uint64 faceType = 3;\n}\n\nmessage WeatherBizInfo {\n  optional string weatherType = 1;\n  optional string weatherTypeId = 2;\n  optional uint32 adcode = 3;\n  optional uint64 updateTime = 4;\n  optional string city = 5;\n  optional string area = 6;\n  optional string temper = 7;\n  optional uint32 flag = 8;\n  optional string weatherDesc = 9;\n}\n\nmessage ZodiacBizInfo {\n  optional string todayTrend = 1;\n  optional string tomorrowTrend = 2;\n  optional string miniapp = 3;\n  optional string todayDate = 4;\n  optional string luckyColor = 5;\n  optional string luckyNumber = 6;\n}"
  },
  {
    "path": "ricq-core/src/pb/profilecard/busi.proto",
    "content": "syntax = \"proto2\";\npackage profilecard;\n\nmessage BusiColor {\n  optional int32 r = 1;\n  optional int32 g = 2;\n  optional int32 b = 3;\n}\n\nmessage BusiComm {\n  optional int32 ver = 1;\n  optional int32 seq = 2;\n  optional int64 fromuin = 3;\n  optional int64 touin = 4;\n  optional int32 service = 5;\n  optional int32 sessionType = 6;\n  optional bytes sessionKey = 7;\n  optional int32 clientIp = 8;\n  optional BusiUi display = 9;\n  optional int32 result = 10;\n  optional string errMsg = 11;\n  optional int32 platform = 12;\n  optional string qqver = 13;\n  optional int32 build = 14;\n  optional BusiLoginSig msgLoginSig = 15;\n  optional int32 version = 17;\n  optional BusiUinInfo msgUinInfo = 18;\n  optional BusiRichUi msgRichDisplay = 19;\n}\nmessage BusiCommonReq {\n  optional string serviceCmd = 1;\n  optional BusiVisitorCountReq vcReq = 2;\n  optional BusiHideRecordsReq hrReq = 3;\n}\nmessage BusiDetailRecord {\n  optional int32 fuin = 1;\n  optional int32 source = 2;\n  optional int32 vtime = 3;\n  optional int32 mod = 4;\n  optional int32 hideFlag = 5;\n}\nmessage BusiHideRecordsReq {\n  optional int32 huin = 1;\n  optional int32 fuin = 2;\n  repeated BusiDetailRecord records = 3;\n}\nmessage BusiLabel {\n  optional bytes name = 1;\n  optional int32 enumType = 2;\n  optional BusiColor textColor = 3;\n  optional BusiColor edgingColor = 4;\n  optional int32 labelAttr = 5;\n  optional int32 labelType = 6;\n}\nmessage BusiLoginSig {\n  optional int32 type = 1;\n  optional bytes sig = 2;\n  optional int32 appid = 3;\n}\nmessage BusiRichUi {\n  optional string name = 1;\n  optional string serviceUrl = 2;\n  //repeated UiInfo uiList = 3;\n}\nmessage BusiUi {\n  optional string url = 1;\n  optional string title = 2;\n  optional string content = 3;\n  optional string jumpUrl = 4;\n}\n\nmessage BusiUinInfo {\n  optional int64 int64Longitude = 1;\n  optional int64 int64Latitude = 2;\n}\nmessage BusiVisitorCountReq {\n  optional int32 requireuin = 1;\n  optional int32 operuin = 2;\n  optional int32 mod = 3;\n  optional int32 reportFlag = 4;\n}\nmessage BusiVisitorCountRsp {\n  optional int32 requireuin = 1;\n  optional int32 totalLike = 2;\n  optional int32 totalView = 3;\n  optional int32 hotValue = 4;\n  optional int32 redValue = 5;\n  optional int32 hotDiff = 6;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/profilecard/gate.proto",
    "content": "syntax = \"proto2\";\npackage profilecard;\n\nmessage GateCommTaskInfo {\n  optional int32 appid = 1;\n  optional bytes taskData = 2;\n}\nmessage GateGetGiftListReq {\n  optional int32 uin = 1;\n}\nmessage GateGetGiftListRsp {\n  repeated string giftUrl = 1;\n  optional string customUrl = 2;\n  optional string desc = 3;\n  optional bool isOn = 4;\n}\nmessage GateGetVipCareReq {\n  optional int64 uin = 1;\n}\nmessage GateGetVipCareRsp {\n  optional int32 buss = 1;\n  optional int32 notice = 2;\n}\nmessage GateOidbFlagInfo {\n  optional int32 fieled = 1;\n  optional bytes byetsValue = 2;\n}\nmessage GatePrivilegeBaseInfoReq {\n  optional int64 uReqUin = 1;\n}\nmessage GatePrivilegeBaseInfoRsp {\n  optional bytes msg = 1;\n  optional bytes jumpUrl = 2;\n  repeated GatePrivilegeInfo vOpenPriv = 3;\n  repeated GatePrivilegeInfo vClosePriv = 4;\n  optional int32 uIsGrayUsr = 5;\n}\nmessage GatePrivilegeInfo {\n  optional int32 iType = 1;\n  optional int32 iSort = 2;\n  optional int32 iFeeType = 3;\n  optional int32 iLevel = 4;\n  optional int32 iFlag = 5;\n  optional bytes iconUrl = 6;\n  optional bytes deluxeIconUrl = 7;\n  optional bytes jumpUrl = 8;\n  optional int32 iIsBig = 9;\n}\nmessage GateVaProfileGateReq {\n  optional int32 uCmd = 1;\n  optional GatePrivilegeBaseInfoReq stPrivilegeReq = 2;\n  optional GateGetGiftListReq stGiftReq = 3;\n  repeated GateCommTaskInfo taskItem = 4;\n  repeated GateOidbFlagInfo oidbFlag = 5;\n  optional GateGetVipCareReq stVipCare = 6;\n}\n\nmessage GateQidInfoItem {\n  optional string qid = 1;\n  optional string url = 2;\n  optional string color = 3;\n  optional string logoUrl = 4;\n}\n\nmessage GateVaProfileGateRsp {\n  optional int32 iRetCode = 1;\n  optional bytes sRetMsg = 2;\n  optional GatePrivilegeBaseInfoRsp stPrivilegeRsp = 3;\n  optional GateGetGiftListRsp stGiftRsp = 4;\n  repeated GateCommTaskInfo taskItem = 5;\n  repeated GateOidbFlagInfo oidbFlag = 6;\n  optional GateGetVipCareRsp stVipCare = 7;\n  optional GateQidInfoItem qidInfo = 9;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/short_video/short_video.proto",
    "content": "syntax = \"proto3\";\npackage short_video;\n\nmessage ShortVideoReqBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  ShortVideoUploadReq pttShortVideoUploadReq = 3;\n  ShortVideoDownloadReq pttShortVideoDownloadReq = 4;\n  repeated ShortVideoExtensionReq extensionReq = 100;\n}\n\nmessage ShortVideoRspBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  ShortVideoUploadRsp pttShortVideoUploadRsp = 3;\n  ShortVideoDownloadRsp pttShortVideoDownloadRsp = 4;\n}\n\nmessage ShortVideoUploadReq {\n  int64 fromUin = 1;\n  int64 toUin = 2;\n  int32 chatType = 3;\n  int32 clientType = 4;\n  ShortVideoFileInfo info = 5;\n  int64 groupCode = 6;\n  int32 agentType = 7;\n  int32 businessType = 8;\n  int32 supportLargeSize = 20;\n}\nmessage ShortVideoDownloadReq {\n  int64 fromUin = 1;\n  int64 toUin = 2;\n  int32 chatType = 3;\n  int32 clientType = 4;\n  string fileId = 5;\n  int64 groupCode = 6;\n  int32 agentType = 7;\n  bytes fileMd5 = 8;\n  int32 businessType = 9;\n  int32 fileType = 10;\n  int32 downType = 11;\n  int32 sceneType = 12;\n}\n\nmessage ShortVideoDownloadRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n  repeated ShortVideoIpList sameAreaOutAddr = 3;\n  repeated ShortVideoIpList diffAreaOutAddr = 4;\n  bytes downloadKey = 5;\n  bytes fileMd5 = 6;\n  repeated ShortVideoIpList sameAreaInnerAddr = 7;\n  repeated ShortVideoIpList diffAreaInnerAddr = 8;\n  ShortVideoAddr downloadAddr = 9;\n  bytes encryptKey = 10;\n}\n\nmessage ShortVideoUploadRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n  repeated ShortVideoIpList sameAreaOutAddr = 3;\n  repeated ShortVideoIpList diffAreaOutAddr = 4;\n  bytes fileId = 5;\n  bytes uKey = 6;\n  int32 fileExists = 7;\n  repeated ShortVideoIpList sameAreaInnerAddr = 8;\n  repeated ShortVideoIpList diffAreaInnerAddr = 9;\n  repeated DataHole dataHole = 10;\n}\n\nmessage ShortVideoFileInfo {\n  string fileName = 1;\n  bytes fileMd5 = 2;\n  bytes thumbFileMd5 = 3;\n  int64 fileSize = 4;\n  int32 fileResLength = 5;\n  int32 fileResWidth = 6;\n  int32 fileFormat = 7;\n  int32 fileTime = 8;\n  int64 thumbFileSize = 9;\n}\n\nmessage DataHole {\n  int64 begin = 1;\n  int64 end = 2;\n}\n\nmessage ShortVideoIpList {\n  int32 ip = 1;\n  int32 port = 2;\n}\n\nmessage ShortVideoAddr {\n  repeated string host = 10;\n  string urlArgs = 11;\n  //repeated string domain = 13;\n}\n\nmessage ShortVideoExtensionReq {\n  int32 subBusiType = 1;\n  int32 userCnt = 2;\n}\n"
  },
  {
    "path": "ricq-core/src/pb/sig_act/sig_act.proto",
    "content": "syntax = \"proto2\";\npackage sig_act;\n\nmessage Platform {\n  optional int64 platform = 1;\n  optional string osver = 2;\n  optional string mqqver = 3;\n}\n\nmessage ReqBody {\n  optional uint32 cmd = 1;\n  optional uint64 seq = 2;\n  optional Platform plf = 3;\n  optional SigactReq req = 4;\n  optional SigauthReq authReq = 5;\n  optional uint32 source = 6;\n}\n\nmessage RspBody {\n  optional int32 ret = 1;\n  optional string desc = 2;\n  optional uint32 cmd = 3;\n  optional uint64 seq = 4;\n  optional SigactRsp rsp = 5;\n  optional SigauthRsp authRsp = 6;\n}\n\nmessage SigactReq {\n  optional uint64 uinDisable = 1;\n  optional int32 actid = 2;\n  optional int32 acttype = 3;\n}\n\nmessage SigactRsp {\n  optional uint64 uin = 1;\n  optional uint32 rank = 2;\n}\n\nmessage SigauthReq {\n  optional uint64 uinDisable = 1;\n  optional int32 itemid = 2;\n  optional int32 len = 3;\n  optional bytes data = 4;\n  optional int32 fontid = 5;\n}\n\nmessage SigauthRsp {\n  optional bytes result = 1;\n  optional string url = 2;\n  optional TipsInfo tipsInfo = 3;\n  optional int32 authfailedAppid = 4;\n\n  message TipsInfo {\n    optional bool valid = 1;\n    optional int32 ret = 2;\n    optional uint32 type = 3;\n    optional string titleWording = 4;\n    optional string wording = 5;\n    optional string rightBtnWording = 6;\n    optional string leftBtnWording = 7;\n    optional string vipType = 8;\n    optional uint32 vipMonth = 9;\n    optional string url = 10;\n  }\n}"
  },
  {
    "path": "ricq-core/src/pb/structmsg/structmsg.proto",
    "content": "syntax = \"proto3\";\n\n//option go_package = \"./;structmsg\";\npackage structmsg;\n\nmessage AddFrdSNInfo {\n  int32 notSeeDynamic = 1;\n  int32 setSn = 2;\n}\n\nmessage FlagInfo {\n  int32 grpMsgKickAdmin = 1;\n  int32 grpMsgHiddenGrp = 2;\n  int32 grpMsgWordingDown = 3;\n  int32 frdMsgGetBusiCard = 4;\n  int32 grpMsgGetOfficialAccount = 5;\n  int32 grpMsgGetPayInGroup = 6;\n  int32 frdMsgDiscuss2ManyChat = 7;\n  int32 grpMsgNotAllowJoinGrpInviteNotFrd = 8;\n  int32 frdMsgNeedWaitingMsg = 9;\n  int32 frdMsgUint32NeedAllUnreadMsg = 10;\n  int32 grpMsgNeedAutoAdminWording = 11;\n  int32 grpMsgGetTransferGroupMsgFlag = 12;\n  int32 grpMsgGetQuitPayGroupMsgFlag = 13;\n  int32 grpMsgSupportInviteAutoJoin = 14;\n  int32 grpMsgMaskInviteAutoJoin = 15;\n  int32 grpMsgGetDisbandedByAdmin = 16;\n  int32 grpMsgGetC2cInviteJoinGroup = 17;\n}\n\nmessage FriendInfo {\n  string msgJointFriend = 1;\n  string msgBlacklist = 2;\n}\n\nmessage SGroupInfo {\n  int32 groupAuthType = 1;\n  int32 displayAction = 2;\n  string msgAlert = 3;\n  string msgDetailAlert = 4;\n  string msgOtherAdminDone = 5;\n  int32 appPrivilegeFlag = 6;\n}\n\nmessage MsgInviteExt {\n  int32 srcType = 1;\n  int64 srcCode = 2;\n  int32 waitState = 3;\n}\n\nmessage MsgPayGroupExt {\n  int64 joinGrpTime = 1;\n  int64 quitGrpTime = 2;\n}\n\nmessage ReqNextSystemMsg {\n  int32 msgNum = 1;\n  int64 followingFriendSeq = 2;\n  int64 followingGroupSeq = 3;\n  int32  checktype = 4;\n  FlagInfo flag = 5;\n  int32 language = 6;\n  int32 version = 7;\n  int64 friendMsgTypeFlag = 8;\n}\n\nmessage ReqSystemMsg {\n  int32 msgNum = 1;\n  int64 latestFriendSeq = 2;\n  int64 latestGroupSeq = 3;\n  int32 version = 4;\n  int32 language = 5;\n}\n\nmessage ReqSystemMsgAction {\n  int32  msgType = 1;\n  int64 msgSeq = 2;\n  int64 reqUin = 3;\n  int32 subType = 4;\n  int32 srcId = 5;\n  int32 subSrcId = 6;\n  int32 groupMsgType = 7;\n  SystemMsgActionInfo actionInfo = 8;\n  int32 language = 9;\n}\n\nmessage ReqSystemMsgNew {\n  int32 msgNum = 1;\n  int64 latestFriendSeq = 2;\n  int64 latestGroupSeq = 3;\n  int32 version = 4;\n  int32 checktype = 5;\n  FlagInfo flag = 6;\n  int32 language = 7;\n  bool isGetFrdRibbon = 8;\n  bool isGetGrpRibbon = 9;\n  int64 friendMsgTypeFlag = 10;\n  int32 reqMsgType = 11;\n}\nmessage ReqSystemMsgRead {\n  int64 latestFriendSeq = 1;\n  int64 latestGroupSeq = 2;\n  int32 type = 3;\n  int32 checktype = 4;\n}\nmessage RspHead {\n  int32 result = 1;\n  string msgFail = 2;\n}\nmessage RspNextSystemMsg {\n  RspHead head = 1;\n  repeated StructMsg msgs = 2;\n  int64 followingFriendSeq = 3;\n  int64 followingGroupSeq = 4;\n  int32  checktype = 5;\n  string gameNick = 100;\n  bytes undecidForQim = 101;\n  int32 unReadCount3 = 102;\n}\nmessage RspSystemMsg {\n  RspHead head = 1;\n  repeated StructMsg msgs = 2;\n  int32 unreadCount = 3;\n  int64 latestFriendSeq = 4;\n  int64 latestGroupSeq = 5;\n  int64 followingFriendSeq = 6;\n  int64 followingGroupSeq = 7;\n  string msgDisplay = 8;\n}\n\nmessage RspSystemMsgAction {\n  RspHead head = 1;\n  string msgDetail = 2;\n  int32 type = 3;\n  string msgInvalidDecided = 5;\n  int32 remarkResult = 6;\n}\nmessage RspSystemMsgNew {\n  RspHead head = 1;\n  int32 unreadFriendCount = 2;\n  int32 unreadGroupCount = 3;\n  int64 latestFriendSeq = 4;\n  int64 latestGroupSeq = 5;\n  int64 followingFriendSeq = 6;\n  int64 followingGroupSeq = 7;\n  repeated StructMsg friendmsgs = 9;\n  repeated StructMsg groupmsgs = 10;\n  StructMsg msgRibbonFriend = 11;\n  StructMsg msgRibbonGroup = 12;\n  string msgDisplay = 13;\n  string grpMsgDisplay = 14;\n  int32 over = 15;\n  int32  checktype = 20;\n  string gameNick = 100;\n  bytes undecidForQim = 101;\n  int32 unReadCount3 = 102;\n}\nmessage RspSystemMsgRead {\n  RspHead head = 1;\n  int32 type = 2;\n  int32  checktype = 3;\n}\nmessage StructMsg {\n  int32 version = 1;\n  int32  msgType = 2;\n  int64 msgSeq = 3;\n  int64 msgTime = 4;\n  int64 reqUin = 5;\n  int32 unreadFlag = 6;\n  SystemMsg msg = 50;\n}\nmessage SystemMsg {\n  int32 subType = 1;\n  string msgTitle = 2;\n  string msgDescribe = 3;\n  string msgAdditional = 4;\n  string msgSource = 5;\n  string msgDecided = 6;\n  int32 srcId = 7;\n  int32 subSrcId = 8;\n  repeated SystemMsgAction actions = 9;\n  int64 groupCode = 10;\n  int64 actionUin = 11;\n  int32 groupMsgType = 12;\n  int32 groupInviterRole = 13;\n  FriendInfo friendInfo = 14;\n  SGroupInfo groupInfo = 15;\n  int64 actorUin = 16;\n  string msgActorDescribe = 17;\n  string msgAdditionalList = 18;\n  int32 relation = 19;\n  int32 reqsubtype = 20;\n  int64 cloneUin = 21;\n  int64 discussUin = 22;\n  int64 eimGroupId = 23;\n  MsgInviteExt msgInviteExtinfo = 24;\n  MsgPayGroupExt msgPayGroupExtinfo = 25;\n  int32 sourceFlag = 26;\n  bytes gameNick = 27;\n  bytes gameMsg = 28;\n  int32 groupFlagext3 = 29;\n  int64 groupOwnerUin = 30;\n  int32 doubtFlag = 31;\n  bytes warningTips = 32;\n  bytes nameMore = 33;\n  int32 reqUinFaceid = 50;\n  string reqUinNick = 51;\n  string groupName = 52;\n  string actionUinNick = 53;\n  string msgQna = 54;\n  string msgDetail = 55;\n  int32 groupExtFlag = 57;\n  string actorUinNick = 58;\n  string picUrl = 59;\n  string cloneUinNick = 60;\n  string reqUinBusinessCard = 61;\n  string eimGroupIdName = 63;\n  string reqUinPreRemark = 64;\n  string actionUinQqNick = 65;\n  string actionUinRemark = 66;\n  int32 reqUinGender = 67;\n  int32 reqUinAge = 68;\n  int32 c2cInviteJoinGroupFlag = 69;\n  int32 cardSwitch = 101;\n}\nmessage SystemMsgAction {\n  string name = 1;\n  string result = 2;\n  int32 action = 3;\n  SystemMsgActionInfo actionInfo = 4;\n  string detailName = 5;\n}\nmessage SystemMsgActionInfo {\n  int32  type = 1;\n  int64 groupCode = 2;\n  bytes sig = 3;\n  string msg = 50;\n  int32 groupId = 51;\n  string remark = 52;\n  bool blacklist = 53;\n  AddFrdSNInfo addFrdSNInfo = 54;\n}\n   "
  },
  {
    "path": "ricq-core/src/protocol/device.rs",
    "content": "use bytes::Bytes;\nuse rand::distributions::DistString;\nuse rand::{distributions::Alphanumeric, Rng, RngCore};\nuse serde::{Deserialize, Serialize};\n\nuse crate::hex::encode_hex;\nuse crate::protocol::qimei::Qimei;\n\n//系统版本\n#[derive(Serialize, Deserialize, Debug, Clone)]\npub struct OSVersion {\n    pub incremental: String,\n    pub release: String,\n    pub codename: String,\n    pub sdk: u32,\n}\n\nimpl Default for OSVersion {\n    fn default() -> Self {\n        OSVersion {\n            incremental: \"5891938\".into(),\n            release: \"10\".into(),\n            codename: \"REL\".into(),\n            sdk: 29,\n        }\n    }\n}\n\n//手机设备信息\n#[derive(Default, Serialize, Deserialize, Debug, Clone)]\npub struct Device {\n    pub display: String,\n    pub product: String,\n    pub device: String,\n    pub board: String,\n    pub model: String,\n    pub finger_print: String,\n    pub boot_id: String,\n    pub proc_version: String,\n    pub imei: String,\n    pub brand: String,\n    pub bootloader: String,\n    pub base_band: String,\n    pub version: OSVersion,\n    pub sim_info: String,\n    pub os_type: String,\n    pub mac_address: String,\n    pub ip_address: Vec<u8>,\n    pub wifi_bssid: String,\n    pub wifi_ssid: String,\n    pub imsi_md5: Vec<u8>,\n    pub android_id: String,\n    pub apn: String,\n    pub vendor_name: String,\n    pub vendor_os_name: String,\n    pub qimei: Option<Qimei>,\n}\n\nimpl Device {\n    pub fn random() -> Self {\n        Self::random_with_rng(&mut rand::thread_rng())\n    }\n\n    pub fn random_with_rng<RNG: RngCore>(rng: &mut RNG) -> Self {\n        Self {\n            display: format!(\"RICQ.{}.001\", rng.gen_range(100000..999999)),\n            product: \"iarim\".into(),\n            device: \"sagit\".into(),\n            board: \"eomam\".into(),\n            model: \"MI 6\".into(),\n            finger_print: format!(\n                \"xiaomi/iarim/sagit:10/eomam.200122.001/{}:user/release-keys\",\n                rng.gen_range(1000000..9999999)\n            ),\n            boot_id: random_uuid(rng),\n            imei: random_imei(rng),\n            proc_version: format!(\n                \"Linux 5.4.0-54-generic-{} (android-build@google.com)\",\n                Alphanumeric.sample_string(rng, 8)\n            ),\n            brand: \"Xiaomi\".into(),\n            bootloader: \"U-boot\".into(),\n            base_band: \"\".into(),\n            version: OSVersion::default(),\n            sim_info: \"T-Mobile\".into(),\n            os_type: \"android\".into(),\n            mac_address: \"00:50:56:C0:00:08\".into(),\n            ip_address: vec![10, 0, 1, 3],\n            wifi_bssid: \"00:50:56:C0:00:08\".into(),\n            wifi_ssid: \"<unknown ssid>\".into(),\n            imsi_md5: md5::compute(rng.gen::<[u8; 16]>()).to_vec(),\n            android_id: encode_hex(&rng.gen::<[u8; 8]>()),\n            apn: \"wifi\".into(),\n            vendor_name: \"MIUI\".into(),\n            vendor_os_name: \"ricq\".into(),\n            qimei: None,\n        }\n    }\n\n    pub fn ksid(&self) -> Bytes {\n        Bytes::from(\n            format!(\"|{}|A8.2.7.27f6ea96\", self.imei)\n                .as_bytes()\n                .to_vec(),\n        )\n    }\n\n    pub fn set_qimei(&mut self, qimei: Qimei) {\n        self.qimei = Some(qimei)\n    }\n}\n\npub fn random_string(len: usize) -> String {\n    Alphanumeric.sample_string(&mut rand::thread_rng(), len)\n}\n\npub fn random_uuid<RNG: RngCore>(rng: &mut RNG) -> String {\n    let r = md5::compute(rng.gen::<[u8; 16]>()).to_vec();\n    format!(\n        \"{}-{}-{}-{}-{}\",\n        encode_hex(&r[0..4]),\n        encode_hex(&r[4..6]),\n        encode_hex(&r[6..8]),\n        encode_hex(&r[8..10]),\n        encode_hex(&r[10..16])\n    )\n}\n\npub fn random_imei<RNG: RngCore>(rng: &mut RNG) -> String {\n    let mut sum = 0;\n    let mut str = String::new();\n    for i in 0..14 {\n        let mut to_add = rng.gen_range(0..10);\n        if (i + 2) % 2 == 0 {\n            to_add *= 2;\n            if to_add >= 10 {\n                to_add = (to_add % 10) + 1\n            }\n        }\n        sum += to_add;\n        str.push_str(&to_add.to_string());\n    }\n    let ctrl_digit = (sum * 9) % 10;\n    str.push_str(&ctrl_digit.to_string());\n    str\n}\n"
  },
  {
    "path": "ricq-core/src/protocol/mod.rs",
    "content": "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",
    "content": "use bytes::{Buf, BufMut, Bytes, BytesMut};\nuse rand::Rng;\n\nuse crate::binary::BinaryWriter;\nuse crate::crypto::{qqtea_decrypt, EncryptECDH};\nuse crate::{RQError, RQResult};\n\n#[derive(Debug, derivative::Derivative)]\n#[derivative(Default)]\npub enum EncryptionMethod {\n    #[derivative(Default)]\n    ECDH,\n    ST,\n}\n\n#[derive(Default)]\npub struct Message {\n    pub uin: u32,\n    pub command: u16,\n    pub body: Bytes,\n    pub encryption_method: EncryptionMethod,\n}\n\npub struct Codec {\n    pub ecdh: EncryptECDH,\n    pub random_key: Bytes,\n    pub wt_session_ticket_key: Bytes,\n}\n\nimpl Default for Codec {\n    fn default() -> Self {\n        Self {\n            ecdh: Default::default(),\n            random_key: Bytes::from(rand::thread_rng().gen::<[u8; 16]>().to_vec()),\n            wt_session_ticket_key: Default::default(),\n        }\n    }\n}\n\nimpl Codec {\n    pub fn encode(&self, m: Message) -> Bytes {\n        let mut w = BytesMut::new();\n        w.put_u8(0x02);\n        w.put_u16(0); // TODO w.len()\n        w.put_u16(8001);\n        w.put_u16(m.command);\n        w.put_u16(1);\n        w.put_u32(m.uin);\n        w.put_u8(0x03);\n        match m.encryption_method {\n            EncryptionMethod::ECDH => w.put_u8(0x87),\n            EncryptionMethod::ST => w.put_u8(0x45),\n        }\n        w.put_u8(0);\n        w.put_u32(2);\n        w.put_u32(0);\n        w.put_u32(0);\n        match m.encryption_method {\n            EncryptionMethod::ECDH => {\n                w.put_u8(0x02);\n                w.put_u8(0x01);\n                w.put_slice(&self.random_key);\n                w.put_u16(0x01_31);\n                w.put_u16(self.ecdh.public_key_ver);\n                w.put_u16(self.ecdh.public_key.len() as u16);\n                w.put_slice(&self.ecdh.public_key);\n                w.encrypt_and_write(&self.ecdh.initial_share_key, &m.body);\n            }\n            EncryptionMethod::ST => {\n                w.put_u8(0x01);\n                w.put_u8(0x03);\n                w.put_slice(&self.random_key);\n                w.put_u16(0x0102);\n                w.put_u16(0x0000);\n                w.encrypt_and_write(&self.random_key, &m.body);\n            }\n        }\n        w.put_u8(0x03);\n\n        let len = w.len();\n        w[1..3].as_mut().put_u16(len as u16);\n        w.freeze()\n    }\n\n    pub fn decode<B>(&self, mut reader: B) -> RQResult<Message>\n    where\n        B: Buf,\n    {\n        let flag = reader.get_u8();\n        if flag != 2 {\n            return Err(RQError::UnknownFlag(flag));\n        }\n        let mut m = Message::default();\n        reader.get_u16(); // len\n        reader.get_u16(); // version\n        m.command = reader.get_u16();\n        reader.get_u16(); // 1\n        m.uin = reader.get_i32() as u32;\n        reader.get_u8();\n        let encrypt_type = reader.get_u8();\n        reader.get_u8();\n        match encrypt_type {\n            0 => {\n                let len = reader.remaining() - 1;\n                let d = reader.copy_to_bytes(len);\n                m.body = Bytes::from(qqtea_decrypt(&d, &self.ecdh.initial_share_key));\n            }\n            3 => {\n                let len = reader.remaining() - 1;\n                let d = reader.copy_to_bytes(len);\n                m.body = Bytes::from(qqtea_decrypt(&d, &self.wt_session_ticket_key));\n            }\n            _ => return Err(RQError::UnknownEncryptType),\n        }\n        Ok(m)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/protocol/packet.rs",
    "content": "use bytes::Bytes;\n\nuse crate::{RQError, RQResult};\n\n#[derive(PartialEq, derivative::Derivative, Eq)]\n#[derivative(Default, Debug, Clone)]\npub enum PacketType {\n    #[derivative(Default)]\n    Simple,\n    Login,\n}\n\nimpl PacketType {\n    pub fn value(&self) -> u32 {\n        match self {\n            PacketType::Login => 0x0A,\n            PacketType::Simple => 0x0B,\n        }\n    }\n\n    pub fn from_i32(v: i32) -> RQResult<Self> {\n        match v {\n            0x0A => Ok(Self::Login),\n            0x0B => Ok(Self::Simple),\n            _ => Err(RQError::InvalidPacketType),\n        }\n    }\n}\n\n#[derive(PartialEq, derivative::Derivative, Eq, Clone)]\n#[derivative(Default, Debug)]\npub enum EncryptType {\n    #[derivative(Default)]\n    NoEncrypt,\n    D2Key,\n    EmptyKey,\n}\n\nimpl EncryptType {\n    pub fn value(&self) -> u32 {\n        match self {\n            EncryptType::NoEncrypt => 0x00,\n            EncryptType::D2Key => 0x01,\n            EncryptType::EmptyKey => 0x02,\n        }\n    }\n    pub fn from_u8(v: u8) -> RQResult<Self> {\n        match v {\n            0x00 => Ok(Self::NoEncrypt),\n            0x01 => Ok(Self::D2Key),\n            0x02 => Ok(Self::EmptyKey),\n            _ => Err(RQError::InvalidEncryptType),\n        }\n    }\n}\n\n#[derive(Default, Debug, Clone)]\npub struct Packet {\n    pub packet_type: PacketType,\n    pub encrypt_type: EncryptType,\n    pub seq_id: i32,\n    pub body: Bytes,\n    pub command_name: String,\n    pub uin: i64,\n    pub message: String,\n    pub sign: Option<Bytes>,\n}\n\nimpl Packet {\n    pub fn check_command_name(self, command_name: &str) -> RQResult<Self> {\n        if self.command_name != command_name {\n            Err(RQError::CommandNameMismatch(\n                command_name.to_owned(),\n                self.command_name,\n            ))\n        } else {\n            Ok(self)\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/protocol/qimei.rs",
    "content": "use crate::hex::encode_hex;\nuse crate::protocol::device::Device;\nuse crate::protocol::version::Version;\nuse crate::{RQError, RQResult};\nuse aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};\nuse base64::Engine;\nuse rand::distributions::Slice;\nuse rand::{CryptoRng, Rng, RngCore};\nuse rsa::traits::PaddingScheme;\nuse serde::{Deserialize, Serialize};\nuse x509_cert::spki::DecodePublicKey;\n\nconst RSA_PUB_KEY: &str = r#\"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEIxgwoutfwoJxcGQeedgP7FG9\nqaIuS0qzfR8gWkrkTZKM2iWHn2ajQpBRZjMSoSf6+KJGvar2ORhBfpDXyVtZCKpq\nLQ+FLkpncClKVIrBwv6PHyUvuCb0rIarmgDnzkfQAqVufEtR64iazGDKatvJ9y6B\n9NMbHddGSAUmRTCrHQIDAQAB\n-----END PUBLIC KEY-----\"#;\n\nconst SECRET: &str = \"ZdJqM15EeO2zWc08\";\n\npub fn aes_decrypt(text: &[u8], key: &[u8]) -> RQResult<Vec<u8>> {\n    cbc::Decryptor::<aes::Aes128>::new_from_slices(key, key)?\n        .decrypt_padded_vec_mut::<Pkcs7>(text)\n        .map_err(Into::into)\n}\n\npub fn aes_encrypt(text: &[u8], key: &[u8]) -> RQResult<Vec<u8>> {\n    Ok(cbc::Encryptor::<aes::Aes128>::new_from_slices(key, key)?\n        .encrypt_padded_vec_mut::<Pkcs7>(text))\n}\n\npub fn rsa_pub_key() -> RQResult<rsa::RsaPublicKey> {\n    rsa::RsaPublicKey::from_public_key_pem(RSA_PUB_KEY).map_err(Into::into)\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\n#[serde(rename_all = \"camelCase\")]\npub struct DeviceReserved<'a> {\n    pub bod: &'a str,\n    pub brd: &'a str,\n    pub clone: &'a str,\n    pub containe: &'a str,\n    pub dv: &'a str,\n    pub first_level: &'a str,\n    pub harmony: &'a str,\n    pub host: &'a str,\n    pub kelong: &'a str,\n    pub kernel: &'a str,\n    pub manufact: &'a str,\n    pub multi_user: &'a str,\n    pub name: &'a str,\n    pub oo: &'a str,\n    pub oz: &'a str,\n    pub uptimes: String,\n}\n\nimpl<'a> DeviceReserved<'a> {\n    pub fn from_device<RNG: RngCore>(rng: &mut RNG, device: &'a Device) -> DeviceReserved<'a> {\n        let now = chrono::Local::now();\n        let offset = chrono::Duration::seconds(rng.gen_range(0..14400));\n        let uptimes = now - offset;\n        DeviceReserved {\n            bod: &device.board,\n            brd: &device.brand,\n            clone: \"0\",\n            containe: \"\",\n            dv: &device.device,\n            first_level: \"\",\n            harmony: \"0\",\n            host: \"se.infra\",\n            kelong: \"0\",\n            kernel: &device.proc_version,\n            manufact: &device.brand,\n            multi_user: \"0\",\n            name: &device.model,\n            oo: \"Xecjt+9S1+f8Pz2VLSxgpw==\",\n            oz: \"UhYmelwouA+V2nPWbOvLTgN2/m8jwGB+yUB5v9tysQg=\",\n            uptimes: uptimes.format(\"%F %T\").to_string(),\n        }\n    }\n}\n\npub fn rand_beacon_id<RNG: RngCore>(rng: &mut RNG) -> String {\n    let mut beacon_id = String::with_capacity(1024);\n    let month = chrono::Local::now().format(\"%Y-%m-01\").to_string();\n    let rand1 = rng.gen_range(100000..999999).to_string();\n    let rand2 = rng.gen_range(100000000..999999999).to_string();\n    for i in 1..=40 {\n        match i {\n            1 | 2 | 13 | 14 | 17 | 18 | 21 | 22 | 25 | 26 | 29 | 30 | 33 | 34 | 37 | 38 => {\n                beacon_id.push('k');\n                beacon_id.push_str(i.to_string().as_str());\n                beacon_id.push(':');\n                beacon_id.push_str(month.as_str());\n                beacon_id.push_str(rand1.as_str());\n                beacon_id.push('.');\n                beacon_id.push_str(rand2.as_str());\n            }\n            3 => {\n                beacon_id.push_str(\"k3:0000000000000000\");\n            }\n            4 => {\n                beacon_id.push_str(\"k4:\");\n                beacon_id.push_str(\n                    rng.sample_iter(Slice::new(\"123456789abcdef\".as_bytes()).expect(\"empty slice\"))\n                        .take(16)\n                        .map(|n| *n as char)\n                        .collect::<String>()\n                        .as_str(),\n                );\n            }\n            _ => {\n                beacon_id.push('k');\n                beacon_id.push_str(i.to_string().as_str());\n                beacon_id.push(':');\n                beacon_id.push_str(rng.gen_range(0..10000).to_string().as_str());\n            }\n        }\n        beacon_id.push(';');\n    }\n    beacon_id\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\n#[serde(rename_all = \"camelCase\")]\npub struct QimeiRequestPayload<'a> {\n    android_id: &'a str,\n    app_key: &'a str,\n    app_version: &'a str,\n    audit: &'a str,\n    beacon_id_src: String,\n    brand: &'a str,\n    channel_id: &'a str,\n    cid: &'a str,\n    device_type: &'a str,\n    imei: &'a str,\n    imsi: &'a str,\n    mac: &'a str,\n    model: &'a str,\n    network_type: &'a str,\n    oaid: &'a str,\n    os_version: String,\n    package_id: &'a str,\n    platform_id: i64,\n    qimei: &'a str,\n    qimei36: &'a str,\n    reserved: String,\n    sdk_name: &'a str,\n    sdk_version: &'a str,\n    user_id: &'a str,\n}\n\nimpl<'a> QimeiRequestPayload<'a> {\n    pub fn new<RNG: RngCore>(\n        rng: &mut RNG,\n        device: &'a Device,\n        version: &Version,\n    ) -> QimeiRequestPayload<'a> {\n        let device_reserved = DeviceReserved::from_device(rng, device);\n        let beacon_id = rand_beacon_id(rng);\n        QimeiRequestPayload {\n            android_id: &device.android_id,\n            app_key: version.app_key,\n            app_version: version.sort_version_name,\n            audit: \"\",\n            beacon_id_src: beacon_id,\n            brand: &device.brand,\n            channel_id: \"2017\",\n            cid: \"\",\n            device_type: \"\",\n            imei: &device.imei,\n            imsi: \"\",\n            mac: \"\",\n            model: &device.model,\n            network_type: \"unknown\",\n            oaid: \"\",\n            os_version: format!(\n                \"Android {},level {}\",\n                device.version.release, device.version.sdk\n            ),\n            package_id: version.apk_id,\n            platform_id: 1,\n            qimei: \"\",\n            qimei36: \"\",\n            reserved: serde_json::to_string(&device_reserved).expect(\"json\"),\n            sdk_name: \"\",\n            sdk_version: \"1.2.13.6\",\n            user_id: \"{}\",\n        }\n    }\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\npub struct QimeiRequest {\n    extra: String,\n    key: String,\n    nonce: String,\n    params: String,\n    sign: String,\n    time: i64,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\npub struct QimeiResponse {\n    code: i64,\n    data: String,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\npub struct Qimei {\n    pub q16: String,\n    pub q36: String,\n}\n\nimpl QimeiRequest {\n    pub fn new<RNG: RngCore + CryptoRng>(\n        rng: &mut RNG,\n        device: &Device,\n        version: &Version,\n        crypt_key: &[u8],\n    ) -> RQResult<QimeiRequest> {\n        let payload = serde_json::to_string(&QimeiRequestPayload::new(rng, device, version))?;\n        let ts = chrono::Local::now().timestamp() * 1000;\n        let nonce = rng\n            .sample_iter(Slice::new(\"abcdef1234567890\".as_bytes()).unwrap())\n            .take(16)\n            .map(|n| *n as char)\n            .collect::<String>();\n        let pub_key = rsa_pub_key()?;\n        let encrypted_aes_key = rsa::Pkcs1v15Encrypt.encrypt(rng, &pub_key, crypt_key)?;\n        let encrypted_payload = aes_encrypt(payload.as_bytes(), crypt_key)?;\n        let key = base64::engine::general_purpose::STANDARD.encode(encrypted_aes_key);\n        let params = base64::engine::general_purpose::STANDARD.encode(encrypted_payload);\n        Ok(QimeiRequest {\n            extra: \"\".to_string(),\n            sign: encode_hex(\n                &md5::compute(format!(\"{}{}{}{}{}\", key, params, ts, nonce, SECRET)).to_vec(),\n            ),\n            key,\n            nonce,\n            params,\n            time: ts,\n        })\n    }\n}\n\nimpl QimeiResponse {\n    pub fn to_payload(self, crypt_key: &[u8]) -> RQResult<Qimei> {\n        if self.code != 0 {\n            return Err(RQError::QimeiError(self.code));\n        }\n        let encrypted_response = base64::engine::general_purpose::STANDARD.decode(self.data)?;\n        let decrypted_response = aes_decrypt(&encrypted_response, crypt_key)?;\n        serde_json::from_slice(&decrypted_response).map_err(Into::into)\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/protocol/sig.rs",
    "content": "use std::collections::HashMap;\n\nuse bytes::Bytes;\n\nuse crate::protocol::device::Device;\n\n#[derive(Default, Debug)]\npub struct Sig {\n    pub login_bitmap: u64,\n    pub tgt: Bytes,\n    pub tgt_key: Bytes,\n\n    // study room manager | 0x16a\n    pub srm_token: Bytes,\n    pub t133: Bytes,\n    pub encrypted_a1: Bytes,\n    pub user_st_key: Bytes,\n    pub user_st_web_sig: Bytes,\n    pub s_key: Bytes,\n    pub s_key_expired_time: i64,\n    pub d2: Bytes,\n    pub d2key: Bytes,\n    // TODO 是不是可能None？\n    pub device_token: Bytes,\n    pub ps_key_map: HashMap<String, Bytes>,\n    pub pt4_token_map: HashMap<String, Bytes>,\n\n    pub out_packet_session_id: Bytes,\n    pub dpwd: Bytes,\n    pub t104: Bytes,\n    pub t547: Bytes,\n    pub t174: Bytes,\n    pub g: Bytes,\n    pub t402: Bytes,\n    pub rand_seed: Bytes, // t403\n\n    pub sync_const1: u32,\n    pub sync_const2: u32,\n    pub sync_const3: u32,\n    pub sync_cookie: Bytes,\n    pub pub_account_cookie: Bytes,\n\n    // device?\n    pub guid: Bytes,\n    pub tgtgt_key: Bytes,\n    pub ksid: Bytes,\n}\n\nimpl Sig {\n    pub fn new(device: &Device) -> Self {\n        let mut sig = Self::default();\n        sig.guid =\n            Bytes::from(md5::compute(device.android_id.to_owned() + &device.mac_address).to_vec());\n        sig.tgtgt_key = Bytes::from(md5::compute(&sig.guid).to_vec());\n        sig.ksid = Bytes::from(format!(\"|{}|A8.2.7.27f6ea96\", device.imei));\n        sig.sync_const1 = rand::random::<u32>();\n        sig.sync_const2 = rand::random::<u32>();\n        sig.sync_const3 = rand::random::<u32>();\n        sig\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/protocol/transport.rs",
    "content": "use std::io::Read;\n\nuse bytes::{Buf, BufMut, Bytes, BytesMut};\nuse flate2::read::ZlibDecoder;\n\nuse crate::binary::{BinaryReader, BinaryWriter};\nuse crate::command::common::PbToBytes;\nuse crate::crypto::{qqtea_decrypt, qqtea_encrypt};\nuse crate::protocol::{\n    device::Device,\n    packet::{EncryptType, Packet, PacketType},\n    sig::Sig,\n    version::Version,\n};\nuse crate::{oicq, pb, RQError, RQResult};\n\npub struct Transport {\n    pub sig: Sig,\n    pub device: Device,\n    pub version: Version,\n    pub oicq_codec: oicq::Codec,\n}\n\nimpl Transport {\n    pub fn new(device: Device, version: Version) -> Self {\n        Self {\n            sig: Sig::new(&device),\n            device,\n            version,\n            oicq_codec: Default::default(),\n        }\n    }\n}\n\nimpl Transport {\n    pub fn encode_packet(&self, mut pkt: Packet) -> Bytes {\n        if self.sig.d2.is_empty() {\n            pkt.encrypt_type = EncryptType::EmptyKey\n        }\n\n        let mut w = BytesMut::new();\n        // let pos = w.len();\n        // w.put_u32(0);\n\n        // vvv w.Write(head) vvv\n        w.put_u32(pkt.packet_type.value());\n        w.put_u8(pkt.encrypt_type.value() as u8);\n        match pkt.packet_type {\n            PacketType::Simple => w.put_u32(pkt.seq_id as u32),\n            PacketType::Login => match pkt.encrypt_type {\n                EncryptType::D2Key => {\n                    w.put_u32(self.sig.d2.len() as u32 + 4);\n                    w.put_slice(&self.sig.d2);\n                }\n                _ => w.put_u32(4),\n            },\n        }\n        w.put_u8(0x00);\n        w.write_string(&pkt.uin.to_string());\n        // ^^^ w.Write(head) ^^^\n\n        let mut w2 = BytesMut::new();\n        self.encode_body(&pkt, &mut w2);\n        let mut body = w2.freeze();\n        match pkt.encrypt_type {\n            EncryptType::D2Key => {\n                body = Bytes::from(qqtea_encrypt(&body, &self.sig.d2key));\n            }\n            EncryptType::EmptyKey => {\n                body = Bytes::from(qqtea_encrypt(&body, &[0; 16]));\n            }\n            EncryptType::NoEncrypt => {}\n        }\n        w.put_slice(&body);\n\n        // let len = w.len();\n        // w[pos..pos + 4].as_mut().put_u32(len as u32);\n        w.freeze()\n    }\n\n    pub fn decode_packet<B>(&self, mut r: B) -> RQResult<Packet>\n    where\n        B: Buf,\n    {\n        let mut pkt = Packet {\n            packet_type: PacketType::from_i32(r.get_i32())?,\n            encrypt_type: EncryptType::from_u8(r.get_u8())?,\n            ..Default::default()\n        };\n        r.get_u8(); // 0x00\n\n        pkt.uin = r.read_string().parse().unwrap_or_default();\n\n        let mut body = Bytes::from(r.chunk().to_owned());\n        match pkt.encrypt_type {\n            EncryptType::NoEncrypt => {}\n            EncryptType::D2Key => body = Bytes::from(qqtea_decrypt(&body, &self.sig.d2key)),\n            EncryptType::EmptyKey => body = Bytes::from(qqtea_decrypt(&body, &[0; 16])),\n        }\n\n        self.decode_sso_frame(&mut pkt, body)?;\n        if pkt.encrypt_type == EncryptType::EmptyKey {\n            // decrypt oicq_codec\n            pkt.body = self.oicq_codec.decode(pkt.body)?.body;\n        }\n        Ok(pkt)\n    }\n\n    fn encode_body(&self, pkt: &Packet, w: &mut BytesMut) {\n        let pos = w.len();\n        w.put_u32(0); // len\n\n        if pkt.packet_type == PacketType::Login {\n            w.put_u32(pkt.seq_id as u32);\n            w.put_u32(self.version.app_id);\n            w.put_u32(self.version.sub_app_id);\n            w.put_slice(&[\n                0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,\n            ]);\n            let tgt = &self.sig.tgt;\n            if tgt.is_empty() || tgt.len() == 4 {\n                w.put_u32(0x04);\n            } else {\n                w.put_u32(tgt.len() as u32 + 4);\n                w.put_slice(tgt);\n            }\n        }\n        w.write_string(&pkt.command_name);\n\n        w.put_u32(self.sig.out_packet_session_id.len() as u32 + 4);\n        w.put_slice(&self.sig.out_packet_session_id);\n        if pkt.packet_type == PacketType::Login {\n            w.write_string(&self.device.imei);\n            w.put_u32(0x04);\n            w.put_u16(self.device.ksid().len() as u16 + 2);\n            w.put_slice(&self.device.ksid());\n        }\n        if let Some(ref sign) = pkt.sign {\n            w.put_u32(sign.len() as u32 + 4);\n            w.put_slice(&sign);\n        }\n        w.put_u32(\n            0x04 + self\n                .device\n                .qimei\n                .as_ref()\n                .map(|qimei| qimei.q16.len())\n                .unwrap_or_default() as u32,\n        );\n        w.put_slice(\n            self.device\n                .qimei\n                .as_ref()\n                .map(|qimei| qimei.q16.as_bytes())\n                .unwrap_or_default(),\n        );\n\n        // write len\n        let len = w.len() - pos;\n        w[pos..pos + 4].as_mut().put_u32(len as u32);\n\n        w.put_u32(pkt.body.len() as u32 + 4);\n        w.put_slice(&pkt.body);\n    }\n\n    fn decode_sso_frame<B>(&self, pkt: &mut Packet, mut r: B) -> RQResult<()>\n    where\n        B: Buf,\n    {\n        let head_len = r.get_i32() as usize;\n        if head_len - 4 > r.remaining() {\n            return Err(RQError::PacketDropped);\n        }\n\n        let mut head = r.copy_to_bytes(head_len - 4);\n        pkt.seq_id = head.get_i32();\n\n        let ret_code = head.get_i32();\n        match ret_code {\n            0 => {}\n            -10008 => return Err(RQError::SessionExpired),\n            other => return Err(RQError::UnsuccessfulRetCode(other)),\n        }\n        pkt.message = head.read_string();\n        pkt.command_name = head.read_string();\n        if &pkt.command_name == \"Heartbeat.Alive\" {\n            return Ok(());\n        }\n\n        let session_id_len = head.get_i32() as usize - 4;\n        let _ = head.copy_to_bytes(session_id_len);\n\n        let compress_flag = head.get_i32();\n\n        let mut body_len = r.get_i32() as usize - 4;\n        body_len = if body_len > 0 && body_len <= r.remaining() {\n            body_len\n        } else {\n            r.remaining()\n        };\n        let mut body = r.copy_to_bytes(body_len);\n\n        if compress_flag == 1 {\n            let mut uncompressed = Vec::new();\n            ZlibDecoder::new(body.chunk()).read_to_end(&mut uncompressed)?;\n            body = Bytes::from(uncompressed)\n        }\n\n        pkt.body = body;\n        Ok(())\n    }\n\n    pub fn encode_oidb_packet(&self, cmd: i32, service_type: i32, body: Bytes) -> Bytes {\n        pb::oidb::OidbssoPkg {\n            command: cmd,\n            service_type,\n            bodybuffer: body.to_vec(),\n            client_version: format!(\"Android {}\", self.version.sort_version_name),\n            ..Default::default()\n        }\n        .to_bytes()\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/protocol/version.rs",
    "content": "use std::convert::TryFrom;\n// oicq/wlogin_sdk/request/WtloginHelper.java SigType\npub const WLOGIN_A2: u32 = 64;\npub const WLOGIN_A5: u32 = 2;\npub const WLOGIN_AQSIG: u32 = 2097152;\npub const WLOGIN_D2: u32 = 262144;\npub const WLOGIN_DA2: u32 = 33554432;\npub const WLOGIN_LHSIG: u32 = 4194304;\npub const WLOGIN_LSKEY: u32 = 512;\npub const WLOGIN_OPENKEY: u32 = 16384;\npub const WLOGIN_PAYTOKEN: u32 = 8388608;\npub const WLOGIN_PF: u32 = 16777216;\npub const WLOGIN_PSKEY: u32 = 1048576;\npub const WLOGIN_PT4_TOKEN: u32 = 134217728;\npub const WLOGIN_QRPUSH: u32 = 67108864;\npub const WLOGIN_RESERVED: u32 = 16;\npub const WLOGIN_SID: u32 = 524288;\npub const WLOGIN_SIG64: u32 = 8192;\npub const WLOGIN_SKEY: u32 = 4096;\npub const WLOGIN_ST: u32 = 128;\npub const WLOGIN_STWEB: u32 = 32;\npub const WLOGIN_TOKEN: u32 = 32768;\npub const WLOGIN_VKEY: u32 = 131072;\n\n#[derive(Debug, Clone, derivative::Derivative, serde::Deserialize)]\n#[derivative(Default)]\npub enum Protocol {\n    #[derivative(Default)]\n    IPad,\n    AndroidPhone,\n    AndroidWatch,\n    AndroidPad,\n    MacOS,\n    QiDian,\n}\n\n#[derive(Debug, Clone, serde::Deserialize)]\npub struct Version {\n    pub apk_sign: &'static [u8],\n    pub apk_id: &'static str,\n    pub app_key: &'static str,\n    pub sort_version_name: &'static str,\n    pub build_ver: &'static str,\n    pub sdk_version: &'static str,\n    pub app_id: u32,\n    pub sub_app_id: u32,\n    pub build_time: u32,\n    pub sso_version: u32,\n    pub misc_bitmap: u32,\n    pub sub_sig_map: u32,\n    pub main_sig_map: u32,\n    pub qua: &'static str,\n    pub protocol: Protocol,\n}\n\npub const fn get_version(p: Protocol) -> Version {\n    match p {\n        Protocol::IPad => IPAD,\n        Protocol::AndroidPhone => ANDROID_PHONE,\n        Protocol::AndroidWatch => ANDROID_WATCH,\n        Protocol::AndroidPad => ANDROID_PAD,\n        Protocol::MacOS => MACOS,\n        Protocol::QiDian => QIDIAN,\n    }\n}\n\nimpl From<Protocol> for Version {\n    fn from(p: Protocol) -> Version {\n        get_version(p)\n    }\n}\n\npub const ANDROID_PHONE: Version = Version {\n    apk_id: \"com.tencent.mobileqq\",\n    app_id: 537164840,\n    sub_app_id: 537164840,\n    app_key: \"0S200MNJT807V3GE\",\n    sort_version_name: \"8.9.63.11390\",\n    build_ver: \"8.9.63.11390\",\n    build_time: 1685069178,\n    apk_sign: &[\n        0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6,\n        0x8D,\n    ],\n    sdk_version: \"6.0.0.2546\",\n    sso_version: 20,\n    misc_bitmap: 150470524,\n    sub_sig_map: 0x10400,\n    // 16724722\n    main_sig_map: WLOGIN_A5\n        | WLOGIN_RESERVED\n        | WLOGIN_STWEB\n        | WLOGIN_A2\n        | WLOGIN_ST\n        | WLOGIN_LSKEY\n        | WLOGIN_SKEY\n        | WLOGIN_SIG64\n        | 1 << 16\n        | WLOGIN_VKEY\n        | WLOGIN_D2\n        | WLOGIN_SID\n        | WLOGIN_PSKEY\n        | WLOGIN_AQSIG\n        | WLOGIN_LHSIG\n        | WLOGIN_PAYTOKEN,\n    qua: \"V1_AND_SQ_8.9.63_4194_YYB_D\",\n    protocol: Protocol::AndroidPhone,\n};\n\npub const APAD: Version = Version {\n    apk_id: \"com.tencent.mobileqq\",\n    app_id: 537164888,\n    sub_app_id: 537164888,\n    sort_version_name: \"8.9.63.11390\",\n    build_ver: \"8.9.33.614\",\n    build_time: 1685069178,\n    apk_sign: &[\n        0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6,\n        0x8D,\n    ],\n    sdk_version: \"6.0.0.2546\",\n    sso_version: 20,\n    misc_bitmap: 150470524,\n    sub_sig_map: 0x10400,\n    // 16724722\n    main_sig_map: WLOGIN_A5\n        | WLOGIN_RESERVED\n        | WLOGIN_STWEB\n        | WLOGIN_A2\n        | WLOGIN_ST\n        | WLOGIN_LSKEY\n        | WLOGIN_SKEY\n        | WLOGIN_SIG64\n        | 1 << 16\n        | WLOGIN_VKEY\n        | WLOGIN_D2\n        | WLOGIN_SID\n        | WLOGIN_PSKEY\n        | WLOGIN_AQSIG\n        | WLOGIN_LHSIG\n        | WLOGIN_PAYTOKEN,\n    protocol: Protocol::AndroidPad,\n    app_key: \"0S200MNJT807V3GE\",\n    qua: \"V1_AND_SQ_8.9.63_4194_YYB_D\",\n};\n\npub const IPAD: Version = Version {\n    apk_id: \"com.tencent.minihd.qq\",\n    app_id: 537151363,\n    sub_app_id: 537151363,\n    sort_version_name: \"8.9.33.614\",\n    build_ver: \"8.9.33.614\",\n    build_time: 1595836208,\n    apk_sign: &[\n        170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199,\n    ],\n    sdk_version: \"6.0.0.2433\",\n    sso_version: 19,\n    misc_bitmap: 150470524,\n    sub_sig_map: 66560,\n    // 1970400\n    main_sig_map: WLOGIN_STWEB\n        | WLOGIN_A2\n        | WLOGIN_ST\n        | WLOGIN_SKEY\n        | WLOGIN_VKEY\n        | WLOGIN_D2\n        | WLOGIN_SID\n        | WLOGIN_PSKEY,\n    protocol: Protocol::IPad,\n    app_key: \"\",\n    qua: \"\",\n};\n\npub const ANDROID_WATCH: Version = Version {\n    apk_id: \"com.tencent.qqlite\",\n    app_id: 537064446,\n    sub_app_id: 537064446,\n    sort_version_name: \"2.0.5\",\n    build_ver: \"2.0.5\",\n    build_time: 1559564731,\n    apk_sign: &[\n        0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6,\n        0x8D,\n    ],\n    sdk_version: \"6.0.0.236\",\n    sso_version: 5,\n    misc_bitmap: 16252796,\n    sub_sig_map: 0x10400,\n    main_sig_map: 34869472,\n    protocol: Protocol::AndroidWatch,\n    qua: \"\",\n    app_key: \"\",\n};\n\npub const MACOS: Version = Version {\n    apk_id: \"com.tencent.qq\",              // ok\n    app_id: 0x2003ca32,                    // ok\n    sub_app_id: 0x2003ca32,                // ok\n    sort_version_name: \"6.7.9\",            // ok\n    build_ver: \"5.8.9.3460\",               // 6.7.9.xxx?\n    build_time: 0,                         // ok\n    apk_sign: \"com.tencent.qq\".as_bytes(), // ok\n    sdk_version: \"6.2.0.1023\",             // ok\n    sso_version: 7,                        // ok\n    misc_bitmap: 0x7ffc,                   // ok\n    sub_sig_map: 66560,                    // ?\n    main_sig_map: 1970400,                 // ?\n    protocol: Protocol::MacOS,\n    app_key: \"\",\n    qua: \"\",\n};\n\npub const QIDIAN: Version = Version {\n    apk_id: \"com.tencent.qidian\",\n    app_id: 537061386,\n    sub_app_id: 537036590,\n    sort_version_name: \"3.8.6\",\n    build_ver: \"8.8.38.2266\",\n    build_time: 1556628836,\n    apk_sign: &[\n        160, 30, 236, 171, 133, 233, 227, 186, 43, 15, 106, 21, 140, 133, 92, 41,\n    ],\n    sdk_version: \"6.0.0.2365\",\n    sso_version: 5,\n    misc_bitmap: 49807228,\n    sub_sig_map: 66560,\n    main_sig_map: 34869472,\n    protocol: Protocol::QiDian,\n    app_key: \"\",\n    qua: \"\",\n};\n\npub const ANDROID_PAD: Version = Version {\n    apk_id: \"com.tencent.mobileqq\",\n    app_id: 537154261,\n    sub_app_id: 537154261,\n    sort_version_name: \"8.9.38.10545\",\n    build_ver: \"8.8.38.2266\",\n    build_time: 1556628836,\n    apk_sign: &[\n        0xa6, 0xb7, 0x45, 0xbf, 0x24, 0xa2, 0xc2, 0x77, 0x52, 0x77, 0x16, 0xf6, 0xf3, 0x6e, 0xb6,\n        0x8d,\n    ],\n    sdk_version: \"6.0.0.2535\",\n    sso_version: 19,\n    misc_bitmap: 150470524,\n    sub_sig_map: 66560,\n    main_sig_map: 16724722,\n    protocol: Protocol::AndroidPad,\n    app_key: \"\",\n    qua: \"\",\n};\n\nimpl TryFrom<&str> for Protocol {\n    type Error = ();\n\n    fn try_from(s: &str) -> Result<Self, Self::Error> {\n        match s {\n            \"IPad\" => Ok(Protocol::IPad),\n            \"AndroidPhone\" | \"APhone\" => Ok(Protocol::AndroidPhone),\n            \"AndroidWatch\" | \"AWatch\" => Ok(Protocol::AndroidWatch),\n            \"AndroidPad\" | \"APad\" => Ok(Protocol::AndroidPad),\n            \"MacOS\" => Ok(Protocol::MacOS),\n            \"QiDian\" => Ok(Protocol::QiDian),\n            _ => Err(()),\n        }\n    }\n}\n\nimpl TryFrom<u8> for Protocol {\n    type Error = ();\n\n    fn try_from(u: u8) -> Result<Self, Self::Error> {\n        match u {\n            0 => Ok(Protocol::IPad), // default\n            1 => Ok(Protocol::AndroidPhone),\n            2 => Ok(Protocol::AndroidWatch),\n            3 => Ok(Protocol::MacOS),\n            4 => Ok(Protocol::QiDian),\n            5 => Ok(Protocol::IPad),\n            6 => Ok(Protocol::AndroidPad),\n            _ => Err(()),\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/structs.rs",
    "content": "use bytes::Bytes;\nuse std::time::Duration;\n\npub use crate::command::multi_msg::{ForwardMessage, ForwardNode, MessageNode};\npub use crate::command::oidb_svc::{\n    LinkShare, MusicShare, MusicVersion, ProfileDetailUpdate, ShareTarget,\n};\npub use crate::command::stat_svc::{CustomOnlineStatus, ExtOnlineStatus, OnlineStatus, Status};\nuse crate::msg::MessageChain;\nuse crate::{jce, pb};\n\n#[derive(Default, Debug)]\npub struct AccountInfo {\n    pub nickname: String,\n    pub age: u8,\n    pub gender: u8,\n}\n\n#[derive(Default, Debug)]\npub struct AddressInfo {\n    pub srv_sso_addrs: Vec<String>,\n    pub other_srv_addrs: Vec<String>,\n    pub file_storage_info: jce::FileStoragePushFSSvcList,\n}\n\n#[derive(Debug, Default)]\npub struct OtherClientInfo {\n    pub app_id: i64,\n    pub instance_id: i32,\n    pub sub_platform: String,\n    pub device_kind: String,\n}\n\npub struct QiDianAccountInfo {\n    pub master_uin: i64,\n    pub ext_name: String,\n    pub create_time: i64,\n\n    pub big_data_req_addrs: Vec<String>,\n    pub big_data_req_session: BigDataReqSessionInfo,\n}\n\n#[derive(Debug, Default)]\npub struct BigDataReqSessionInfo {\n    pub sig_session: Bytes,\n    pub session_key: Bytes,\n}\n\n#[derive(Debug, Default)]\npub struct GroupInfo {\n    pub uin: i64,\n    pub code: i64,\n    pub name: String,\n    pub memo: String,\n    pub owner_uin: i64,\n    pub group_create_time: u32,\n    pub group_level: u32,\n    pub member_count: u16,\n    pub max_member_count: u16,\n    // 全群禁言时间\n    pub shut_up_timestamp: i64,\n    // 自己被禁言时间\n    pub my_shut_up_timestamp: i64,\n    // 最后一条信息的SEQ,只有通过 GetGroupInfo 函数获取的 GroupInfo 才会有\n    pub last_msg_seq: i64,\n}\n\n#[derive(Debug, Default, Clone)]\npub struct GroupMemberInfo {\n    pub group_code: i64,\n    pub uin: i64,\n    pub gender: u8,\n    pub nickname: String,\n    pub card_name: String,\n    pub level: u16,\n    pub join_time: i64,\n    pub last_speak_time: i64,\n    pub special_title: String,\n    pub special_title_expire_time: i64,\n    pub shut_up_timestamp: i64,\n    pub permission: GroupMemberPermission,\n}\n\n#[derive(Debug, Clone, derivative::Derivative)]\n#[derivative(Default)]\npub enum GroupMemberPermission {\n    Owner = 1,\n    Administrator = 2,\n    #[derivative(Default)]\n    Member = 3,\n}\n\n/// 好友信息\n#[derive(Debug, Default, Clone)]\npub struct FriendInfo {\n    pub uin: i64,\n    pub nick: String,\n    pub remark: String,\n    pub face_id: i16,\n    pub group_id: u8,\n}\n\n/// 好友分组信息\n#[derive(Debug, Default, Clone)]\npub struct FriendGroupInfo {\n    pub group_id: u8,\n    pub group_name: String,\n    pub friend_count: i32,\n    pub online_friend_count: i32,\n    pub seq_id: u8,\n}\n\n#[derive(Debug, Default, Clone)]\npub struct SummaryCardInfo {\n    pub uin: i64,\n    pub sex: u8,\n    pub age: u8,\n    pub nickname: String,\n    pub level: i32,\n    pub city: String,\n    pub sign: String,\n    pub mobile: String,\n    pub login_days: i64,\n    /// 用于点赞\n    pub cookie: Bytes,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct FriendMessage {\n    pub seqs: Vec<i32>,\n    pub rands: Vec<i32>,\n    pub target: i64,\n    pub time: i32,\n    pub from_uin: i64,\n    pub from_nick: String,\n    pub elements: MessageChain,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupMessage {\n    pub seqs: Vec<i32>,\n    pub rands: Vec<i32>,\n    pub group_code: i64,\n    pub group_name: String,\n    pub group_card: String,\n    pub from_uin: i64,\n    pub time: i32,\n    pub elements: MessageChain,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupTempMessage {\n    pub seqs: Vec<i32>,\n    pub rands: Vec<i32>,\n    pub from_uin: i64,\n    pub from_nick: String,\n    pub time: i32,\n    pub elements: MessageChain,\n    pub group_code: i64,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct NewMember {\n    pub group_code: i64,\n    pub member_uin: i64,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupMute {\n    pub group_code: i64,\n    pub operator_uin: i64,\n    pub target_uin: i64,\n    pub duration: Duration,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct FriendMessageRecall {\n    pub msg_seq: i32,\n    pub friend_uin: i64,\n    pub time: i64,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupMessageRecall {\n    pub msg_seq: i32,\n    pub group_code: i64,\n    pub operator_uin: i64,\n    pub author_uin: i64,\n    pub time: i32,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupLeave {\n    pub group_code: i64,\n    pub member_uin: i64,\n    pub operator_uin: Option<i64>,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct FriendPoke {\n    pub sender: i64,\n    pub receiver: i64,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupPoke {\n    pub group_code: i64,\n    pub sender: i64,\n    pub receiver: i64,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupNameUpdate {\n    pub group_code: i64,\n    pub operator_uin: i64,\n    pub group_name: String,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct DeleteFriend {\n    pub uin: i64,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct MemberPermissionChange {\n    pub group_code: i64,\n    pub member_uin: i64,\n    pub new_permission: GroupMemberPermission,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupDisband {\n    pub group_code: i64,\n    pub operator_uin: i64,\n}\n\n// 用于撤回\n#[derive(Debug, Clone, Default)]\npub struct MessageReceipt {\n    pub seqs: Vec<i32>,\n    pub rands: Vec<i32>,\n    pub time: i64,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct GroupAudio(pub pb::msg::Ptt);\n\n#[derive(Debug, Clone, Default)]\npub struct GroupAudioMessage {\n    pub seqs: Vec<i32>,\n    pub rands: Vec<i32>,\n    pub group_code: i64,\n    pub group_name: String,\n    pub group_card: String,\n    pub from_uin: i64,\n    pub time: i32,\n    pub audio: GroupAudio,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct FriendAudio(pub pb::msg::Ptt);\n\n#[derive(Debug, Clone, Default)]\npub struct FriendAudioMessage {\n    pub seqs: Vec<i32>,\n    pub rands: Vec<i32>,\n    pub target: i64,\n    pub time: i32,\n    pub from_uin: i64,\n    pub from_nick: String,\n    pub audio: FriendAudio,\n}\n// 群文件总数\n#[derive(Debug, Clone, Default)]\npub struct GroupFileCount {\n    pub is_full: bool,\n    pub all_file_count: u32,\n    pub limit_count: u32,\n    pub file_too_many: bool,\n}\n\n// 群文件列表\n#[derive(Debug, Clone, Default)]\npub struct GroupFileList {\n    pub all_file_count: u32,\n    pub is_end: bool,\n    pub items: Vec<GroupFileItem>,\n    pub role: u32,\n    pub next_index: u32,\n}\n// 群文件列表\n#[derive(Debug, Clone, Default)]\npub struct GroupFileItem {\n    pub r#type: u32,\n    pub folder_info: GroupFolderInfo,\n    pub file_info: GroupFileInfo,\n}\n\n// 群文件夹\n#[derive(Debug, Clone, Default)]\npub struct GroupFolderInfo {\n    pub folder_id: String,\n    pub parent_folder_id: String,\n    pub folder_name: String,\n    pub create_time: u32,\n    pub modify_time: u32,\n    pub create_uin: u64,\n    pub creator_name: String,\n    pub total_file_count: u32,\n}\n// 群文件\n#[derive(Debug, Clone, Default)]\npub struct GroupFileInfo {\n    pub file_id: String,\n    pub file_name: String,\n    pub file_size: u64,\n    pub bus_id: u32,\n    pub uploaded_size: u64,\n    pub upload_time: u32,\n    pub dead_time: u32,\n    pub modify_time: u32,\n    pub download_times: u32,\n    pub sha: String,\n    pub sha3: Bytes,\n    pub md5: Bytes,\n    pub local_path: String,\n    pub uploader_name: String,\n    pub uploader_uin: u64,\n    pub parent_folder_id: String,\n}\n"
  },
  {
    "path": "ricq-core/src/token.rs",
    "content": "use serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize, Debug, Clone)]\npub struct Token {\n    pub uin: i64,\n    pub d2: Vec<u8>,\n    pub d2key: Vec<u8>,\n    pub tgt: Vec<u8>,\n    pub srm_token: Vec<u8>,\n    pub t133: Vec<u8>,\n    pub encrypted_a1: Vec<u8>,\n    pub out_packet_session_id: Vec<u8>,\n    pub tgtgt_key: Vec<u8>,\n    pub wt_session_ticket_key: Vec<u8>, // oicq\n}\n"
  },
  {
    "path": "ricq-core/src/utils/mod.rs",
    "content": "mod option_set;\n\npub use option_set::OptionSet;\n"
  },
  {
    "path": "ricq-core/src/utils/option_set.rs",
    "content": "pub trait OptionSet: Sized {\n    /// set value if is `Some(Self)` or do noting\n    fn option_set(&mut self, value: Option<Self>);\n}\n\nimpl<T: Sized> OptionSet for T {\n    #[inline]\n    fn option_set(&mut self, value: Option<Self>) {\n        if let Some(value) = value {\n            *self = value\n        }\n    }\n}\n"
  },
  {
    "path": "ricq-core/src/wtlogin.rs",
    "content": "use std::sync::atomic::Ordering;\n\nuse bytes::{BufMut, Bytes, BytesMut};\n\nuse crate::command::wtlogin::{\n    LoginDeviceLockLogin, LoginDeviceLocked, LoginNeedCaptcha, LoginResponse, LoginSuccess,\n    QRCodeConfirmed,\n};\nuse crate::protocol::device::random_string;\nuse crate::utils::OptionSet;\nuse crate::Transport;\n\nimpl super::Engine {\n    pub fn process_qrcode_confirmed(&mut self, resp: &QRCodeConfirmed) {\n        self.transport.sig.tgtgt_key = resp.tgtgt_key.clone();\n        self.uin.store(resp.uin, Ordering::Relaxed);\n    }\n\n    pub fn process_login_response(&mut self, login_response: &LoginResponse) {\n        match login_response {\n            LoginResponse::Success(resp) => self.process_login_success(resp.clone()),\n            LoginResponse::NeedCaptcha(resp) => self.process_need_captcha(resp),\n            LoginResponse::DeviceLocked(resp) => self.process_device_locked(resp),\n            LoginResponse::DeviceLockLogin(resp) => self.process_device_lock_login(resp.clone()),\n            _ => {}\n        }\n    }\n\n    fn process_login_success(&mut self, resp: LoginSuccess) {\n        let sig = &mut self.transport.sig;\n        let oicq_codec = &mut self.transport.oicq_codec;\n\n        // update\n        sig.rand_seed.option_set(resp.rand_seed);\n        sig.ksid.option_set(resp.ksid);\n\n        if let Some(v) = resp.t512 {\n            sig.ps_key_map = v.ps_key_map;\n            sig.pt4_token_map = v.pt4_token_map;\n        }\n\n        oicq_codec\n            .wt_session_ticket_key\n            .option_set(resp.wt_session_ticket_key);\n\n        sig.srm_token.option_set(resp.srm_token);\n        sig.t133.option_set(resp.t133);\n        sig.encrypted_a1.option_set(resp.encrypt_a1);\n        sig.tgt.option_set(resp.tgt);\n        sig.tgt_key.option_set(resp.tgt_key);\n        sig.user_st_key.option_set(resp.user_st_key);\n        sig.user_st_web_sig.option_set(resp.user_st_web_sig);\n        sig.s_key.option_set(resp.s_key);\n        sig.s_key_expired_time = resp.s_key_expired_time;\n        sig.d2.option_set(resp.d2);\n        sig.d2key.option_set(resp.d2key);\n        sig.device_token.option_set(resp.device_token);\n\n        if let Some(v) = resp.t402 {\n            set_t402(&mut self.transport, v)\n        }\n    }\n\n    fn process_need_captcha(&mut self, resp: &LoginNeedCaptcha) {\n        self.transport.sig.t104.option_set(resp.t104.clone());\n        self.transport.sig.t547.option_set(resp.t547.clone());\n    }\n\n    fn process_device_locked(&mut self, resp: &LoginDeviceLocked) {\n        self.transport.sig.t104.option_set(resp.t104.clone());\n        self.transport.sig.t174.option_set(resp.t174.clone());\n\n        if let Some(v) = &resp.t402 {\n            set_t402(&mut self.transport, v.clone())\n        }\n    }\n    fn process_device_lock_login(&mut self, resp: LoginDeviceLockLogin) {\n        self.transport.sig.rand_seed.option_set(resp.rand_seed);\n        self.transport.sig.t104.option_set(resp.t104);\n\n        if let Some(v) = resp.t402 {\n            set_t402(&mut self.transport, v)\n        }\n    }\n}\nfn set_t402(transport: &mut Transport, t402: Bytes) {\n    transport.sig.dpwd = random_string(16).into();\n    transport.sig.t402 = t402;\n    let mut v = BytesMut::new();\n    v.put_slice(&transport.sig.guid);\n    v.put_slice(&transport.sig.dpwd);\n    v.put_slice(&transport.sig.t402);\n    transport.sig.g = Bytes::from(md5::compute(&v).to_vec())\n}\n"
  },
  {
    "path": "ricq-guild/Cargo.toml",
    "content": "[package]\nname = \"ricq-guild\"\nversion = \"0.1.20\"\nedition = \"2021\"\ndescription = \"ricq-guild\"\nlicense = \"MPL-2.0\"\nhomepage = \"https://github.com/lz1998/ricq\"\nrepository = \"https://github.com/lz1998/ricq\"\nreadme = \"README.md\"\nkeywords = [\"qq\", \"protocol\", \"android\", \"mirai\"]\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.htmlc\n\n[dependencies]\nbytes.workspace = true\nprost = { workspace = true, features = [\"std\"], default-features = false }\nprost-types.workspace = true\ntracing.workspace = true\ndynamic-protobuf.workspace = true\nrand.workspace = true\nricq = { path = \"../ricq\" }\nricq-core = { path = \"../ricq-core\" }\ntokio = { workspace = true, features = [\"sync\"] }\n\n\n[build-dependencies]\nprost-build = \"0.9\""
  },
  {
    "path": "ricq-guild/README.md",
    "content": "# ricq-guild\n\n频道支持"
  },
  {
    "path": "ricq-guild/build.rs",
    "content": "use std::path::{Path, PathBuf};\n\nfn recurse_dir(v: &mut Vec<PathBuf>, dir: impl AsRef<Path>) {\n    for entry in\n        std::fs::read_dir(&dir).unwrap_or_else(|_| panic!(\"Unable to read dir: {:?}\", dir.as_ref()))\n    {\n        let path = entry.expect(\"Unable to get direntry\").path();\n        if path.is_dir() {\n            recurse_dir(v, path);\n        } else if let Some(true) = path.extension().map(|v| v == \"proto\") {\n            v.push(path);\n        }\n    }\n}\nfn main() {\n    let mut files = Vec::new();\n    recurse_dir(&mut files, \"src/protocol/protobuf\");\n\n    prost_build::Config::new()\n        .extern_path(\".msg\", \"::ricq_core::pb::msg\")\n        .compile_protos(&files, &[\"src/protocol/protobuf\", \"src/protocol/core\"])\n        .expect(\"Cannot compile protobuf files\");\n}\n"
  },
  {
    "path": "ricq-guild/src/client/builder.rs",
    "content": "use crate::protocol::protobuf;\nuse dynamic_protobuf::{dynamic_message, DynamicMessage};\nuse rand::Rng;\nuse ricq_core::command::common::PbToBytes;\nuse ricq_core::protocol::packet::Packet;\nuse std::sync::atomic::Ordering;\n\nimpl<'a> super::Engine<'a> {\n    pub fn build_sync_channel_first_view_packet(&self) -> Packet {\n        let req = protobuf::FirstViewReq {\n            last_msg_time: Some(0),\n            udc_flag: None,\n            seq: Some(0),\n            direct_message_flag: Some(1),\n        };\n\n        let b = req.to_bytes();\n        self.uni_packet(\"trpc.group_pro.synclogic.SyncLogic.SyncFirstView\", b)\n    }\n\n    pub fn build_get_user_profile_packet(&self, tiny_id: u64) -> Packet {\n        let mut flags = DynamicMessage::new();\n\n        for i in 3..=29 {\n            flags.set(i, 1u32)\n        }\n        flags.set(99, 1u32);\n        flags.set(100, 1u32);\n\n        let payload = {\n            let msg = dynamic_message! {\n                1 => flags,\n                3 => tiny_id,\n                4 => 0u32,\n            };\n\n            self.transport.encode_oidb_packet(0xf88, 1, msg.encode())\n        };\n\n        self.uni_packet(\"OidbSvcTrpcTcp.0xfc9_1\", payload)\n    }\n\n    pub fn build_send_channel_message_packet(\n        &self,\n        elems: Vec<ricq_core::pb::msg::Elem>,\n        guild_id: u64,\n        channel_id: u64,\n    ) -> Packet {\n        let routing = protobuf::ChannelRoutingHead {\n            guild_id: Some(guild_id),\n            channel_id: Some(channel_id),\n            from_uin: Some(self.uin.load(Ordering::Relaxed) as _),\n            from_tinyid: None,\n            guild_code: None,\n            from_appid: None,\n            direct_message_flag: None,\n        };\n\n        let mut rng = rand::thread_rng();\n        let random = rng.gen_range(0..i32::MAX);\n        let content = protobuf::ChannelContentHead {\n            r#type: Some(3840),\n            sub_type: None,\n            random: Some(random as _),\n            seq: None,\n            cnt_seq: None,\n            time: None,\n            meta: None,\n        };\n\n        let msg_head = protobuf::ChannelMsgHead {\n            routing_head: Some(routing),\n            content_head: Some(content),\n        };\n\n        let body = ricq_core::pb::msg::MessageBody {\n            rich_text: Some(ricq_core::pb::msg::RichText {\n                attr: None,\n                elems,\n                not_online_file: None,\n                ptt: None,\n            }),\n            msg_content: None,\n            msg_encrypt_content: None,\n        };\n\n        let content = protobuf::ChannelMsgContent {\n            head: Some(msg_head),\n            ctrl_head: None,\n            body: Some(body),\n            ext_info: None,\n        };\n\n        self.uni_packet(\n            \"MsgProxy.SendMsg\",\n            dynamic_message! {\n                1 => content.to_bytes(),\n            }\n            .encode(),\n        )\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn build_guild_image_store_packet(\n        &self,\n        channel_id: u64,\n        guild_code: u64,\n        file_name: String,\n        md5: Vec<u8>,\n        size: u64,\n        width: u32,\n        height: u32,\n        image_type: u32,\n    ) -> Packet {\n        let req = ricq_core::pb::cmd0x388::D388ReqBody {\n            net_type: Some(3),\n            subcmd: Some(1),\n            // TODO 支持多张图片？\n            tryup_img_req: vec![ricq_core::pb::cmd0x388::TryUpImgReq {\n                group_code: Some(channel_id),\n                src_uin: Some(self.uin() as u64),\n                file_id: Some(0),\n                file_md5: Some(md5),\n                file_size: Some(size),\n                file_name: Some(file_name.into_bytes()),\n                src_term: Some(5),\n                platform_type: Some(9),\n                bu_type: Some(211),\n                pic_type: Some(image_type),\n                pic_width: Some(width),\n                pic_height: Some(height),\n                build_ver: Some(self.transport.version.build_ver.as_bytes().to_vec()),\n                app_pic_type: Some(1050),\n                qqmeet_guild_id: Some(guild_code),\n                qqmeet_channel_id: Some(channel_id),\n                ..Default::default()\n            }],\n            command_id: Some(83),\n            ..Default::default()\n        };\n        self.uni_packet(\"ImgStore.QQMeetPicUp\", req.to_bytes())\n    }\n}\n"
  },
  {
    "path": "ricq-guild/src/client/decoder.rs",
    "content": "use bytes::Bytes;\nuse ricq_core::{RQError, RQResult};\n\nuse crate::protocol::protobuf::{self, FirstViewMsg, GuildUserProfile};\nuse crate::protocol::{FirstViewResponse, GuildImageStoreResp};\nuse crate::ricq_core::pb;\nuse prost::Message;\nuse ricq_core::common::RQAddr;\n\npub struct Decoder;\n\nimpl Decoder {\n    pub fn decode_guild_first_view_response(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<Option<FirstViewResponse>> {\n        let rep = protobuf::FirstViewRsp::decode(&*payload)?;\n\n        match rep {\n            protobuf::FirstViewRsp {\n                result: Some(r),\n                err_msg: Some(err),\n                ..\n            } => Err(RQError::Decode(format!(\n                \"FirstViewRsp decode error: {}, {}\",\n                r,\n                String::from_utf8_lossy(&err)\n            ))),\n            protobuf::FirstViewRsp {\n                guild_count: Some(guild_count),\n                self_tinyid: Some(self_tinyid),\n                direct_message_switch: Some(direct_message_switch),\n                direct_message_guild_count: Some(direct_message_guild_count),\n                ..\n            } => Ok(Some(FirstViewResponse {\n                guild_count,\n                self_tinyid,\n                direct_message_switch,\n                direct_message_guild_count,\n            })),\n            _ => Ok(None),\n        }\n    }\n\n    pub fn decode_first_view_msg(&self, payload: Bytes) -> RQResult<FirstViewMsg> {\n        let msg = FirstViewMsg::decode(&*payload)?;\n        Ok(msg)\n    }\n\n    pub fn decode_guild_user_profile(&self, payload: Bytes) -> RQResult<Option<GuildUserProfile>> {\n        let pkg = pb::oidb::OidbssoPkg::decode(&*payload)?;\n        let oidb = protobuf::ChannelOidb0xfc9Rsp::decode(&*pkg.bodybuffer)?;\n        Ok(oidb.profile)\n    }\n\n    pub fn decode_guild_image_store_response(\n        &self,\n        payload: Bytes,\n    ) -> RQResult<GuildImageStoreResp> {\n        let mut rsp = pb::cmd0x388::D388RspBody::decode(&*payload)?;\n        let rsp = rsp\n            .tryup_img_rsp\n            .pop()\n            .ok_or(RQError::EmptyField(\"tryup_img_rsp\"))?;\n        if rsp.result() != 0 {\n            return Err(RQError::Other(\n                String::from_utf8_lossy(&rsp.fail_msg.unwrap_or_default()).into_owned(),\n            ));\n        }\n\n        let download_index = rsp\n            .download_index\n            .ok_or(RQError::EmptyField(\"download_index\"))?;\n        Ok(if rsp.file_exit.unwrap_or_default() {\n            GuildImageStoreResp::Exist {\n                file_id: rsp.fileid.unwrap_or_default(),\n                addrs: rsp\n                    .up_ip\n                    .into_iter()\n                    .zip(rsp.up_port)\n                    .map(|(ip, port)| RQAddr(ip, port as u16))\n                    .collect(),\n                download_index,\n            }\n        } else {\n            GuildImageStoreResp::NotExist {\n                file_id: rsp.fileid.unwrap_or_default(),\n                upload_key: rsp.up_ukey.unwrap_or_default(),\n                upload_addrs: rsp\n                    .up_ip\n                    .into_iter()\n                    .zip(rsp.up_port)\n                    .map(|(ip, port)| RQAddr(ip, port as u16))\n                    .collect(),\n                download_index,\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "ricq-guild/src/client/mod.rs",
    "content": "use dynamic_protobuf::dynamic_message;\nuse std::collections::HashMap;\nuse std::ops::Deref;\nuse std::sync::Arc;\n\nuse tokio::sync::{broadcast, RwLockReadGuard};\nuse tokio::task::JoinHandle;\n\nuse ricq::structs::ImageInfo;\nuse ricq_core::highway::BdhInput;\n\nuse ricq_core::msg::MessageChain;\nuse ricq_core::protocol::packet::Packet;\nuse ricq_core::{RQError, RQResult};\n\nuse crate::client::decoder::Decoder;\nuse crate::protocol::protobuf::FirstViewMsg;\nuse crate::protocol::{\n    protobuf, FirstView, FirstViewMessage, GuildImage, GuildImageStoreResp, GuildSelfProfile,\n};\n\npub mod builder;\npub mod decoder;\npub mod processor;\n\n#[allow(dead_code)]\npub struct GuildClient {\n    rq_client: Arc<ricq::Client>,\n    listeners: HashMap<&'static str, broadcast::Receiver<Packet>>,\n}\n\nimpl GuildClient {\n    pub async fn new(rq_client: &Arc<ricq::Client>) -> Self {\n        let rq_client = rq_client.clone();\n\n        let listeners = HashMap::new();\n\n        Self {\n            rq_client,\n            listeners,\n        }\n    }\n\n    pub async fn engine(&self) -> Engine<'_> {\n        Engine::from_rq(self.rq_client.engine.read().await)\n    }\n\n    pub async fn fetch_guild_first_view(&self) -> RQResult<Option<FirstView>> {\n        let pkt = self.engine().await.build_sync_channel_first_view_packet();\n\n        let cli = self.rq_client.clone();\n        let first_view: JoinHandle<RQResult<FirstViewMsg>> = tokio::spawn(async move {\n            static COMMAND: &str = \"trpc.group_pro.synclogic.SyncLogic.PushFirstView\";\n\n            let mut rx = cli.listen_command(COMMAND).await;\n\n            let r = rx.recv().await.unwrap();\n            let mut first_view: FirstViewMsg = Decoder.decode_first_view_msg(r.body)?;\n\n            for _ in 0..2 {\n                let r = rx.recv().await.unwrap();\n                let msg = Decoder.decode_first_view_msg(r.body)?;\n\n                match msg {\n                    FirstViewMsg {\n                        push_flag,\n                        channel_msgs,\n                        get_msg_time,\n                        ..\n                    } if !channel_msgs.is_empty() => {\n                        first_view.push_flag = push_flag;\n                        first_view.channel_msgs = channel_msgs;\n                        first_view.get_msg_time = get_msg_time;\n                    }\n                    FirstViewMsg {\n                        direct_message_guild_nodes,\n                        ..\n                    } if !direct_message_guild_nodes.is_empty() => {\n                        first_view.direct_message_guild_nodes = direct_message_guild_nodes;\n                    }\n                    _ => {}\n                }\n            }\n            Ok(first_view)\n        });\n\n        let rsp = self.rq_client.send_and_wait(pkt).await?;\n\n        let first_view_msg = first_view.await.unwrap()?;\n        let first_view_rsp = Decoder.decode_guild_first_view_response(rsp.body)?;\n\n        let opt = match (first_view_msg, first_view_rsp) {\n            (\n                FirstViewMsg {\n                    push_flag: Some(push_flag),\n                    guild_nodes,\n                    channel_msgs,\n                    get_msg_time: Some(get_msg_time),\n                    direct_message_guild_nodes,\n                    ..\n                },\n                Some(response),\n            ) => {\n                let message = FirstViewMessage {\n                    push_flag,\n                    guild_nodes,\n                    channel_msgs,\n                    get_msg_time,\n                    direct_message_guild_nodes,\n                };\n\n                Some(FirstView { response, message })\n            }\n            _ => None,\n        };\n\n        Ok(opt)\n    }\n\n    pub async fn fetch_guild_self_profile(\n        &self,\n        tiny_id: u64,\n    ) -> RQResult<Option<GuildSelfProfile>> {\n        let pkt = self.engine().await.build_get_user_profile_packet(tiny_id);\n        let rsp = self.rq_client.send_and_wait(pkt).await?;\n        let usr = Decoder.decode_guild_user_profile(rsp.body)?;\n\n        let prof = match usr {\n            Some(protobuf::GuildUserProfile {\n                tiny_id: Some(tiny_id),\n                nickname: Some(nickname),\n                avatar_url: Some(avatar_url),\n                ..\n            }) => Some(GuildSelfProfile {\n                tiny_id,\n                nickname,\n                avatar_url,\n            }),\n            _ => None,\n        };\n\n        Ok(prof)\n    }\n\n    pub async fn send_channel_message(\n        &self,\n        elems: MessageChain,\n        guild_id: u64,\n        channel_id: u64,\n    ) -> RQResult<Packet> {\n        let pkt = self.engine().await.build_send_channel_message_packet(\n            elems.into(),\n            guild_id,\n            channel_id,\n        );\n\n        let ret = self.rq_client.send_and_wait(pkt).await?;\n\n        Ok(ret) // todo: decode receipt\n    }\n\n    pub async fn upload_channel_image(\n        &self,\n        guild_id: u64,\n        channel_id: u64,\n        image: &[u8],\n    ) -> RQResult<GuildImage> {\n        let info = ImageInfo::try_new(image)?;\n\n        let image_store = self\n            .get_guild_image_store(guild_id, channel_id, image)\n            .await?;\n\n        let fid;\n        let dn_index;\n        let server;\n        match image_store {\n            GuildImageStoreResp::Exist {\n                file_id,\n                mut addrs,\n                download_index,\n            } => {\n                fid = file_id;\n                dn_index = download_index;\n                server = addrs.pop().ok_or(RQError::EmptyField(\"Address\"))?;\n            }\n            GuildImageStoreResp::NotExist {\n                file_id,\n                upload_key,\n                mut upload_addrs,\n                download_index,\n            } => {\n                let addr = match self.rq_client.highway_addrs.read().await.first() {\n                    Some(addr) => *addr,\n                    None => upload_addrs\n                        .pop()\n                        .ok_or(RQError::EmptyField(\"upload_addrs\"))?,\n                };\n\n                self.rq_client\n                    .highway_upload_bdh(\n                        addr.into(),\n                        BdhInput {\n                            command_id: 83,\n                            ticket: upload_key,\n                            ext: dynamic_message! {\n                                11 => guild_id,\n                                12 => channel_id,\n                            }\n                            .encode()\n                            .to_vec(),\n                            encrypt: false,\n                            chunk_size: 256 * 1024,\n                            send_echo: true,\n                        },\n                        image,\n                    )\n                    .await?;\n\n                fid = file_id;\n                dn_index = download_index;\n                server = addr;\n            }\n        };\n\n        let guild_image = GuildImage {\n            file_id: fid,\n            file_name: info.filename,\n            size: info.size,\n            width: info.width,\n            height: info.height,\n            image_type: info.image_type,\n            download_index: dn_index,\n            md5: info.md5,\n            server_ip: server.0,\n            server_port: server.1,\n        };\n\n        Ok(guild_image)\n    }\n\n    pub async fn get_guild_image_store(\n        &self,\n        guild_id: u64,\n        channel_id: u64,\n        data: &[u8],\n    ) -> RQResult<GuildImageStoreResp> {\n        let image_info = ImageInfo::try_new(data)?;\n        let req = self.engine().await.build_guild_image_store_packet(\n            channel_id as _,\n            guild_id,\n            image_info.filename,\n            image_info.md5,\n            image_info.size as u64,\n            image_info.width,\n            image_info.height,\n            image_info.image_type as u32,\n        );\n        let resp = self.rq_client.send_and_wait(req).await?;\n        Decoder.decode_guild_image_store_response(resp.body)\n    }\n}\n\npub struct Engine<'a>(RwLockReadGuard<'a, ricq_core::Engine>);\n\nimpl<'a> Engine<'a> {\n    fn from_rq(engine: RwLockReadGuard<'a, ricq_core::Engine>) -> Self {\n        Self(engine)\n    }\n}\n\nimpl<'a> Deref for Engine<'a> {\n    type Target = ricq_core::Engine;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n"
  },
  {
    "path": "ricq-guild/src/client/processor.rs",
    "content": "impl super::GuildClient {}\n"
  },
  {
    "path": "ricq-guild/src/lib.rs",
    "content": "pub mod client;\npub mod protocol;\n\nextern crate ricq;\nextern crate ricq_core;\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/cmd0x346/cmd0x346.proto",
    "content": "syntax = \"proto3\";\n\npackage cmd0x346;\n\nmessage ApplyCleanTrafficRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n}\nmessage ApplyCopyFromReq {\n  int64 srcUin = 10;\n  int64 srcGroup = 20;\n  int32 srcSvcid = 30;\n  bytes srcParentfolder = 40;\n  bytes srcUuid = 50;\n  bytes fileMd5 = 60;\n  int64 dstUin = 70;\n  int64 fileSize = 80;\n  string fileName = 90;\n  int32 dangerLevel = 100;\n  int64 totalSpace = 110;\n}\nmessage ApplyCopyFromRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  bytes uuid = 30;\n  int64 totalSpace = 40;\n}\nmessage ApplyCopyToReq {\n  int64 dstId = 10;\n  int64 dstUin = 20;\n  int32 dstSvcid = 30;\n  int64 srcUin = 40;\n  int64 fileSize = 50;\n  string fileName = 60;\n  string localFilepath = 70;\n  bytes uuid = 80;\n}\nmessage ApplyCopyToRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  string fileKey = 30;\n}\nmessage ApplyDownloadAbsReq {\n  int64 uin = 10;\n  bytes uuid = 20;\n}\nmessage ApplyDownloadAbsRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  DownloadInfo downloadInfo = 30;\n}\nmessage ApplyDownloadReq {\n  int64 uin = 10;\n  bytes uuid = 20;\n  int32 ownerType = 30;\n  int32 extIntype = 500;\n  int32 need_https_url = 501;\n}\nmessage ApplyDownloadRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  DownloadInfo downloadInfo = 30;\n  FileInfo fileInfo = 40;\n}\nmessage ApplyForwardFileReq {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  bytes uuid = 30;\n  int32 dangerLevel = 40;\n  int64 totalSpace = 50;\n}\nmessage ApplyForwardFileRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 totalSpace = 30;\n  int64 usedSpace = 40;\n  bytes uuid = 50;\n}\nmessage ApplyGetTrafficReq {\n}\nmessage ApplyGetTrafficRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 useFileSize = 30;\n  int32 useFileNum = 40;\n  int64 allFileSize = 50;\n  int32 allFileNum = 60;\n}\nmessage ApplyListDownloadReq {\n  int64 uin = 10;\n  int32 beginIndex = 20;\n  int32 reqCount = 30;\n}\nmessage ApplyListDownloadRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int32 totalCount = 30;\n  int32 beginIndex = 40;\n  int32 rspCount = 50;\n  int32 isEnd = 60;\n  repeated FileInfo fileList = 70;\n}\nmessage ApplyUploadHitReq {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  string localFilepath = 60;\n  int32 dangerLevel = 70;\n  int64 totalSpace = 80;\n}\nmessage ApplyUploadHitReqV2 {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  bytes bytes_3sha = 60;\n  bytes sha = 70;\n  string localFilepath = 80;\n  int32 dangerLevel = 90;\n  int64 totalSpace = 100;\n}\nmessage ApplyUploadHitReqV3 {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  bytes sha = 60;\n  string localFilepath = 70;\n  int32 dangerLevel = 80;\n  int64 totalSpace = 90;\n}\nmessage ApplyUploadHitRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  string uploadIp = 30;\n  int32 uploadPort = 40;\n  string uploadDomain = 50;\n  bytes uuid = 60;\n  bytes uploadKey = 70;\n  int64 totalSpace = 80;\n  int64 usedSpace = 90;\n}\nmessage ApplyUploadHitRspV2 {\n  int32 retCode = 10;\n  string retMsg = 20;\n  string uploadIp = 30;\n  int32 uploadPort = 40;\n  string uploadDomain = 50;\n  bytes uuid = 60;\n  bytes uploadKey = 70;\n  int64 totalSpace = 80;\n  int64 usedSpace = 90;\n}\nmessage ApplyUploadHitRspV3 {\n  int32 retCode = 10;\n  string retMsg = 20;\n  string uploadIp = 30;\n  int32 uploadPort = 40;\n  string uploadDomain = 50;\n  bytes uuid = 60;\n  bytes uploadKey = 70;\n  int64 totalSpace = 80;\n  int64 usedSpace = 90;\n}\nmessage ApplyUploadReq {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int32 fileType = 30;\n  int64 fileSize = 40;\n  string fileName = 50;\n  bytes bytes_10mMd5 = 60;\n  string localFilepath = 70;\n  int32 dangerLevel = 80;\n  int64 totalSpace = 90;\n}\nmessage ApplyUploadReqV2 {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  bytes bytes_3sha = 60;\n  string localFilepath = 70;\n  int32 dangerLevel = 80;\n  int64 totalSpace = 90;\n}\nmessage ApplyUploadReqV3 {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  int64 fileSize = 30;\n  string fileName = 40;\n  bytes bytes_10mMd5 = 50;\n  bytes sha = 60;\n  string localFilepath = 70;\n  int32 dangerLevel = 80;\n  int64 totalSpace = 90;\n}\nmessage ApplyUploadRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 totalSpace = 30;\n  int64 usedSpace = 40;\n  int64 uploadedSize = 50;\n  string uploadIp = 60;\n  string uploadDomain = 70;\n  int32 uploadPort = 80;\n  bytes uuid = 90;\n  bytes uploadKey = 100;\n  bool boolFileExist = 110;\n  int32 packSize = 120;\n  repeated string uploadipList = 130;\n}\nmessage ApplyUploadRspV2 {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 totalSpace = 30;\n  int64 usedSpace = 40;\n  int64 uploadedSize = 50;\n  string uploadIp = 60;\n  string uploadDomain = 70;\n  int32 uploadPort = 80;\n  bytes uuid = 90;\n  bytes uploadKey = 100;\n  bool boolFileExist = 110;\n  int32 packSize = 120;\n  repeated string uploadipList = 130;\n  int32 httpsvrApiVer = 140;\n  bytes sha = 141;\n}\nmessage ApplyUploadRspV3 {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int64 totalSpace = 30;\n  int64 usedSpace = 40;\n  int64 uploadedSize = 50;\n  string uploadIp = 60;\n  string uploadDomain = 70;\n  int32 uploadPort = 80;\n  bytes uuid = 90;\n  bytes uploadKey = 100;\n  bool boolFileExist = 110;\n  int32 packSize = 120;\n  repeated string uploadIpList = 130;\n  int32 uploadHttpsPort = 140;\n  string uploadHttpsDomain = 150;\n  string uploadDns = 160;\n  string uploadLanip = 170;\n}\nmessage DelMessageReq {\n  int64 uinSender = 1;\n  int64 uinReceiver = 2;\n  int32 time = 10;\n  int32 random = 20;\n  int32 seqNo = 30;\n}\nmessage DeleteFileReq {\n  int64 uin = 10;\n  int64 peerUin = 20;\n  int32 deleteType = 30;\n  bytes uuid = 40;\n}\nmessage DeleteFileRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n}\nmessage DownloadInfo {\n  bytes downloadKey = 10;\n  string downloadIp = 20;\n  string downloadDomain = 30;\n  int32 port = 40;\n  string downloadUrl = 50;\n  repeated string downloadipList = 60;\n  string cookie = 70;\n}\nmessage DownloadSuccReq {\n  int64 uin = 10;\n  bytes uuid = 20;\n}\nmessage DownloadSuccRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  int32 downStat = 30;\n}\nmessage ExtensionReq {\n  int64 id = 1;\n  int64 type = 2;\n  string dstPhonenum = 3;\n  int32 phoneConvertType = 4;\n  bytes sig = 20;\n  int64 routeId = 100;\n  DelMessageReq delMessageReq = 90100;\n  int32 downloadUrlType = 90200;\n  int32 pttFormat = 90300;\n  int32 isNeedInnerIp = 90400;\n  int32 netType = 90500;\n  int32 voiceType = 90600;\n  int32 fileType = 90700;\n  int32 pttTime = 90800;\n}\nmessage ExtensionRsp {\n}\nmessage FileInfo {\n  int64 uin = 1;\n  int32 dangerEvel = 2;\n  int64 fileSize = 3;\n  int32 lifeTime = 4;\n  int32 uploadTime = 5;\n  bytes uuid = 6;\n  string fileName = 7;\n  int32 absFileType = 90;\n  bytes bytes_10mMd5 = 100;\n  bytes sha = 101;\n  int32 clientType = 110;\n  int64 ownerUin = 120;\n  int64 peerUin = 121;\n  int32 expireTime = 130;\n}\nmessage FileQueryReq {\n  int64 uin = 10;\n  bytes uuid = 20;\n}\nmessage FileQueryRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  FileInfo fileInfo = 30;\n}\nmessage RecallFileReq {\n  int64 uin = 1;\n  bytes uuid = 2;\n}\nmessage RecallFileRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n}\nmessage RecvListQueryReq {\n  int64 uin = 1;\n  int32 beginIndex = 2;\n  int32 reqCount = 3;\n}\nmessage RecvListQueryRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n  int32 fileTotCount = 3;\n  int32 beginIndex = 4;\n  int32 rspFileCount = 5;\n  int32 isEnd = 6;\n  repeated FileInfo fileList = 7;\n}\nmessage RenewFileReq {\n  int64 uin = 1;\n  bytes uuid = 2;\n  int32 addTtl = 3;\n}\nmessage RenewFileRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n}\nmessage C346ReqBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  RecvListQueryReq recvListQueryReq = 3;\n  SendListQueryReq sendListQueryReq = 4;\n  RenewFileReq renewFileReq = 5;\n  RecallFileReq recallFileReq = 6;\n  ApplyUploadReq applyUploadReq = 7;\n  ApplyUploadHitReq applyUploadHitReq = 8;\n  ApplyForwardFileReq applyForwardFileReq = 9;\n  UploadSuccReq uploadSuccReq = 10;\n  DeleteFileReq deleteFileReq = 11;\n  DownloadSuccReq downloadSuccReq = 12;\n  ApplyDownloadAbsReq applyDownloadAbsReq = 13;\n  ApplyDownloadReq applyDownloadReq = 14;\n  ApplyListDownloadReq applyListDownloadReq = 15;\n  FileQueryReq fileQueryReq = 16;\n  ApplyCopyFromReq applyCopyFromReq = 17;\n  ApplyUploadReqV2 applyUploadReqV2 = 18;\n  ApplyUploadReqV3 applyUploadReqV3 = 19;\n  ApplyUploadHitReqV2 applyUploadHitReqV2 = 20;\n  ApplyUploadHitReqV3 applyUploadHitReqV3 = 21;\n  int32 businessId = 101;\n  int32 clientType = 102;\n  ApplyCopyToReq applyCopyToReq = 90000;\n  //ApplyCleanTrafficReq applyCleanTrafficReq = 90001; empty message\n  ApplyGetTrafficReq applyGetTrafficReq = 90002;\n  ExtensionReq extensionReq = 99999;\n}\nmessage C346RspBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  RecvListQueryRsp recvListQueryRsp = 3;\n  SendListQueryRsp sendListQueryRsp = 4;\n  RenewFileRsp renewFileRsp = 5;\n  RecallFileRsp recallFileRsp = 6;\n  ApplyUploadRsp applyUploadRsp = 7;\n  ApplyUploadHitRsp applyUploadHitRsp = 8;\n  ApplyForwardFileRsp applyForwardFileRsp = 9;\n  UploadSuccRsp uploadSuccRsp = 10;\n  DeleteFileRsp deleteFileRsp = 11;\n  DownloadSuccRsp downloadSuccRsp = 12;\n  ApplyDownloadAbsRsp applyDownloadAbsRsp = 13;\n  ApplyDownloadRsp applyDownloadRsp = 14;\n  ApplyListDownloadRsp applyListDownloadRsp = 15;\n  FileQueryRsp fileQueryRsp = 16;\n  ApplyCopyFromRsp applyCopyFromRsp = 17;\n  ApplyUploadRspV2 applyUploadRspV2 = 18;\n  ApplyUploadRspV3 applyUploadRspV3 = 19;\n  ApplyUploadHitRspV2 applyUploadHitRspV2 = 20;\n  ApplyUploadHitRspV3 applyUploadHitRspV3 = 21;\n  int32 businessId = 101;\n  int32 clientType = 102;\n  ApplyCopyToRsp applyCopyToRsp = 90000;\n  ApplyCleanTrafficRsp applyCleanTrafficRsp = 90001;\n  ApplyGetTrafficRsp applyGetTrafficRsp = 90002;\n  ExtensionRsp extensionRsp = 99999;\n\n}\nmessage SendListQueryReq {\n  int64 uin = 1;\n  int32 beginIndex = 2;\n  int32 reqCount = 3;\n}\nmessage SendListQueryRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n  int32 fileTotCount = 3;\n  int32 beginIndex = 4;\n  int32 rspFileCount = 5;\n  int32 isEnd = 6;\n  int64 totLimit = 7;\n  int64 usedLimit = 8;\n  repeated FileInfo fileList = 9;\n}\nmessage UploadSuccReq {\n  int64 senderUin = 10;\n  int64 recverUin = 20;\n  bytes uuid = 30;\n}\nmessage UploadSuccRsp {\n  int32 retCode = 10;\n  string retMsg = 20;\n  FileInfo fileInfo = 30;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/cmd0x352/cmd0x352.proto",
    "content": "syntax = \"proto2\";\n\npackage cmd0x352;\n/*\nmessage DelImgReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n  optional uint32 reqTerm = 3;\n  optional uint32 reqPlatformType = 4;\n  optional uint32 buType = 5;\n  optional bytes buildVer = 6;\n  optional bytes fileResid = 7;\n  optional uint32 picWidth = 8;\n  optional uint32 picHeight = 9;\n}\n\nmessage DelImgRsp {\n  optional uint32 result = 1;\n  optional bytes failMsg = 2;\n  optional bytes fileResid = 3;\n}\n\nmessage GetImgUrlReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n  optional bytes fileResid = 3;\n  optional uint32 urlFlag = 4;\n  optional uint32 urlType = 6;\n  optional uint32 reqTerm = 7;\n  optional uint32 reqPlatformType = 8;\n  optional uint32 srcFileType = 9;\n  optional uint32 innerIp = 10;\n  optional bool addressBook = 11;\n  optional uint32 buType = 12;\n  optional bytes buildVer = 13;\n  optional uint32 picUpTimestamp = 14;\n  optional uint32 reqTransferType = 15;\n}\n\nmessage GetImgUrlRsp {\n  optional bytes fileResid = 1;\n  optional uint32 clientIp = 2;\n  optional uint32 result = 3;\n  optional bytes failMsg = 4;\n  repeated bytes thumbDownUrl = 5;\n  repeated bytes originalDownUrl = 6;\n  optional ImgInfo imgInfo = 7;\n  repeated uint32 downIp = 8;\n  repeated uint32 downPort = 9;\n  optional bytes thumbDownPara = 10;\n  optional bytes originalDownPara = 11;\n  optional bytes downDomain = 12;\n  repeated bytes bigDownUrl = 13;\n  optional bytes bigDownPara = 14;\n  optional bytes bigThumbDownPara = 15;\n  optional uint32 httpsUrlFlag = 16;\n  repeated IPv6Info downIp6 = 26;\n  optional bytes clientIp6 = 27;\n}\n\nmessage IPv6Info {\n  optional bytes ip6 = 1;\n  optional uint32 port = 2;\n}\n*/\n\nmessage ReqBody {\n  optional uint32 subcmd = 1;\n  repeated D352TryUpImgReq tryupImgReq = 2;\n  // repeated GetImgUrlReq getimgUrlReq = 3;\n  // repeated DelImgReq delImgReq = 4;\n  optional uint32 netType = 10;\n}\n\nmessage RspBody {\n  optional uint32 subcmd = 1;\n  repeated TryUpImgRsp tryupImgRsp = 2;\n  // repeated GetImgUrlRsp getimgUrlRsp = 3;\n  optional bool newBigchan = 4;\n  // repeated DelImgRsp delImgRsp = 5;\n  optional bytes failMsg = 10;\n}\n\nmessage D352TryUpImgReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n  optional uint64 fileId = 3;\n  optional bytes fileMd5 = 4;\n  optional uint64 fileSize = 5;\n  optional bytes fileName = 6;\n  optional uint32 srcTerm = 7;\n  optional uint32 platformType = 8;\n  optional uint32 innerIp = 9;\n  optional bool addressBook = 10;\n  optional uint32 retry = 11;\n  optional uint32 buType = 12;\n  optional bool picOriginal = 13;\n  optional uint32 picWidth = 14;\n  optional uint32 picHeight = 15;\n  optional uint32 picType = 16;\n  optional bytes buildVer = 17;\n  optional bytes fileIndex = 18;\n  optional uint32 storeDays = 19;\n  optional uint32 tryupStepflag = 20;\n  optional bool rejectTryfast = 21;\n  optional uint32 srvUpload = 22;\n  optional bytes transferUrl = 23;\n}\n\nmessage TryUpImgRsp {\n  optional uint64 fileId = 1;\n  optional uint32 clientIp = 2;\n  optional uint32 result = 3;\n  optional bytes failMsg = 4;\n  optional bool fileExit = 5;\n  // optional ImgInfo imgInfo = 6;\n  repeated uint32 upIp = 7;\n  repeated uint32 upPort = 8;\n  optional bytes upUkey = 9;\n  optional string upResid = 10;\n  optional bytes upUuid = 11;\n  optional uint64 upOffset = 12;\n  optional uint64 blockSize = 13;\n  optional bytes encryptDstip = 14;\n  optional uint32 roamdays = 15;\n  // repeated IPv6Info upIp6 = 26;\n  optional bytes clientIp6 = 27;\n  optional bytes thumbDownPara = 60;\n  optional bytes originalDownPara = 61;\n  optional bytes downDomain = 62;\n  optional bytes bigDownPara = 64;\n  optional bytes bigThumbDownPara = 65;\n  optional uint32 httpsUrlFlag = 66;\n  // optional TryUpInfo4Busi info4Busi = 1001;\n}\n\n/*\nmessage TryUpInfo4Busi {\n  optional bytes fileResid = 1;\n  optional bytes downDomain = 2;\n  optional bytes thumbDownUrl = 3;\n  optional bytes originalDownUrl = 4;\n  optional bytes bigDownUrl = 5;\n}\n*/"
  },
  {
    "path": "ricq-guild/src/protocol/core/cmd0x388/cmd0x388.proto",
    "content": "syntax = \"proto2\";\n\npackage cmd0x388;\n\nmessage DelImgReq {\n  optional uint64 srcUin = 1;\n  optional uint64 dstUin = 2;\n  optional uint32 reqTerm = 3;\n  optional uint32 reqPlatformType = 4;\n  optional uint32 buType = 5;\n  optional bytes buildVer = 6;\n  optional bytes fileResid = 7;\n  optional uint32 picWidth = 8;\n  optional uint32 picHeight = 9;\n}\n\nmessage DelImgRsp {\n  optional uint32 result = 1;\n  optional bytes failMsg = 2;\n  optional bytes fileResid = 3;\n}\n\nmessage ExpRoamExtendInfo {\n  optional bytes resid = 1;\n}\n\nmessage ExpRoamPicInfo {\n  optional uint32 shopFlag = 1;\n  optional uint32 pkgId = 2;\n  optional bytes picId = 3;\n}\n\nmessage ExtensionCommPicTryUp {\n  repeated bytes extinfo = 1;\n}\n\nmessage ExtensionExpRoamTryUp {\n  repeated ExpRoamPicInfo exproamPicInfo = 1;\n}\n\nmessage GetImgUrlReq {\n  optional uint64 groupCode = 1;\n  optional uint64 dstUin = 2;\n  optional uint64 fileid = 3;\n  optional bytes fileMd5 = 4;\n  optional uint32 urlFlag = 5;\n  optional uint32 urlType = 6;\n  optional uint32 reqTerm = 7;\n  optional uint32 reqPlatformType = 8;\n  optional uint32 innerIp = 9;\n  optional uint32 buType = 10;\n  optional bytes buildVer = 11;\n  optional uint64 fileId = 12;\n  optional uint64 fileSize = 13;\n  optional uint32 originalPic = 14;\n  optional uint32 retryReq = 15;\n  optional uint32 fileHeight = 16;\n  optional uint32 fileWidth = 17;\n  optional uint32 picType = 18;\n  optional uint32 picUpTimestamp = 19;\n  optional uint32 reqTransferType = 20;\n  optional uint64 qqmeetGuildId = 21;\n  optional uint64 qqmeetChannelId = 22;\n  optional bytes downloadIndex = 23;\n}\n\nmessage GetImgUrlRsp {\n  optional uint64 fileid = 1;\n  optional bytes fileMd5 = 2;\n  optional uint32 result = 3;\n  optional bytes failMsg = 4;\n  optional ImgInfo imgInfo = 5;\n  repeated bytes thumbDownUrl = 6;\n  repeated bytes originalDownUrl = 7;\n  repeated bytes bigDownUrl = 8;\n  repeated uint32 downIp = 9;\n  repeated uint32 downPort = 10;\n  optional bytes downDomain = 11;\n  optional bytes thumbDownPara = 12;\n  optional bytes originalDownPara = 13;\n  optional bytes bigDownPara = 14;\n  optional uint64 fileId = 15;\n  optional uint32 autoDownType = 16;\n  repeated uint32 orderDownType = 17;\n  optional bytes bigThumbDownPara = 19;\n  optional uint32 httpsUrlFlag = 20;\n  repeated IPv6Info downIp6 = 26;\n  optional bytes clientIp6 = 27;\n}\n\nmessage GetPttUrlReq {\n  optional uint64 groupCode = 1;\n  optional uint64 dstUin = 2;\n  optional uint64 fileid = 3;\n  optional bytes fileMd5 = 4;\n  optional uint32 reqTerm = 5;\n  optional uint32 reqPlatformType = 6;\n  optional uint32 innerIp = 7;\n  optional uint32 buType = 8;\n  optional bytes buildVer = 9;\n  optional uint64 fileId = 10;\n  optional bytes fileKey = 11;\n  optional uint32 codec = 12;\n  optional uint32 buId = 13;\n  optional uint32 reqTransferType = 14;\n  optional uint32 isAuto = 15;\n}\n\nmessage GetPttUrlRsp {\n  optional uint64 fileid = 1;\n  optional bytes fileMd5 = 2;\n  optional uint32 result = 3;\n  optional bytes failMsg = 4;\n  repeated bytes downUrl = 5;\n  repeated uint32 downIp = 6;\n  repeated uint32 downPort = 7;\n  optional bytes downDomain = 8;\n  optional bytes downPara = 9;\n  optional uint64 fileId = 10;\n  optional uint32 transferType = 11;\n  optional uint32 allowRetry = 12;\n  repeated IPv6Info downIp6 = 26;\n  optional bytes clientIp6 = 27;\n  optional string domain = 28;\n}\n\nmessage IPv6Info {\n  optional bytes ip6 = 1;\n  optional uint32 port = 2;\n}\n\nmessage ImgInfo {\n  optional bytes fileMd5 = 1;\n  optional uint32 fileType = 2;\n  optional uint64 fileSize = 3;\n  optional uint32 fileWidth = 4;\n  optional uint32 fileHeight = 5;\n}\n\nmessage PicSize {\n  optional uint32 original = 1;\n  optional uint32 thumb = 2;\n  optional uint32 high = 3;\n}\n\nmessage D388ReqBody {\n  optional uint32 netType = 1;\n  optional uint32 subcmd = 2;\n  repeated TryUpImgReq tryupImgReq = 3;\n  repeated GetImgUrlReq getimgUrlReq = 4;\n  repeated TryUpPttReq tryupPttReq = 5;\n  repeated GetPttUrlReq getpttUrlReq = 6;\n  optional uint32 commandId = 7;\n  repeated DelImgReq delImgReq = 8;\n  optional bytes extension = 1001;\n}\n\nmessage D388RspBody {\n  optional uint32 clientIp = 1;\n  optional uint32 subcmd = 2;\n  repeated D388TryUpImgRsp tryupImgRsp = 3;\n  repeated GetImgUrlRsp getimgUrlRsp = 4;\n  repeated TryUpPttRsp tryupPttRsp = 5;\n  repeated GetPttUrlRsp getpttUrlRsp = 6;\n  repeated DelImgRsp delImgRsp = 7;\n}\n\nmessage TryUpImgReq {\n  optional uint64 groupCode = 1;\n  optional uint64 srcUin = 2;\n  optional uint64 fileId = 3;\n  optional bytes fileMd5 = 4;\n  optional uint64 fileSize = 5;\n  optional bytes fileName = 6;\n  optional uint32 srcTerm = 7;\n  optional uint32 platformType = 8;\n  optional uint32 buType = 9;\n  optional uint32 picWidth = 10;\n  optional uint32 picHeight = 11;\n  optional uint32 picType = 12;\n  optional bytes buildVer = 13;\n  optional uint32 innerIp = 14;\n  optional uint32 appPicType = 15;\n  optional uint32 originalPic = 16;\n  optional bytes fileIndex = 17;\n  optional uint64 dstUin = 18;\n  optional uint32 srvUpload = 19;\n  optional bytes transferUrl = 20;\n  optional uint64 qqmeetGuildId = 21;\n  optional uint64 qqmeetChannelId = 22;\n}\n\nmessage D388TryUpImgRsp {\n  optional uint64 fileId = 1;\n  optional uint32 result = 2;\n  optional bytes failMsg = 3;\n  optional bool fileExit = 4;\n  optional ImgInfo imgInfo = 5;\n  repeated uint32 upIp = 6;\n  repeated uint32 upPort = 7;\n  optional bytes upUkey = 8;\n  optional uint64 fileid = 9;\n  optional uint64 upOffset = 10;\n  optional uint64 blockSize = 11;\n  optional bool newBigChan = 12;\n  repeated IPv6Info upIp6 = 26;\n  optional bytes clientIp6 = 27;\n  optional bytes downloadIndex = 28;\n  optional TryUpInfo4Busi info4Busi = 1001;\n}\n\nmessage TryUpInfo4Busi {\n  optional bytes downDomain = 1;\n  optional bytes thumbDownUrl = 2;\n  optional bytes originalDownUrl = 3;\n  optional bytes bigDownUrl = 4;\n  optional bytes fileResid = 5;\n}\n\nmessage TryUpPttReq {\n  optional uint64 groupCode = 1;\n  optional uint64 srcUin = 2;\n  optional uint64 fileId = 3;\n  optional bytes fileMd5 = 4;\n  optional uint64 fileSize = 5;\n  optional bytes fileName = 6;\n  optional uint32 srcTerm = 7;\n  optional uint32 platformType = 8;\n  optional uint32 buType = 9;\n  optional bytes buildVer = 10;\n  optional uint32 innerIp = 11;\n  optional uint32 voiceLength = 12;\n  optional bool newUpChan = 13;\n  optional uint32 codec = 14;\n  optional uint32 voiceType = 15;\n  optional uint32 buId = 16;\n}\n\nmessage TryUpPttRsp {\n  optional uint64 fileId = 1;\n  optional uint32 result = 2;\n  optional bytes failMsg = 3;\n  optional bool fileExit = 4;\n  repeated uint32 upIp = 5;\n  repeated uint32 upPort = 6;\n  optional bytes upUkey = 7;\n  optional uint64 fileid = 8;\n  optional uint64 upOffset = 9;\n  optional uint64 blockSize = 10;\n  optional bytes fileKey = 11;\n  optional uint32 channelType = 12;\n  repeated IPv6Info upIp6 = 26;\n  optional bytes clientIp6 = 27;\n}\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/cmd0x3bb/cmd0x3bb.proto",
    "content": "syntax = \"proto2\";\n\npackage cmd0x3bb;\n\nmessage AnonyMsg {\n  optional uint32 cmd = 1;\n  optional C3BBReqBody anonyReq = 10;\n  optional C3BBRspBody anonyRsp = 11;\n}\n\nmessage AnonyStatus {\n  optional uint32 forbidTalking = 1;\n  optional bytes errMsg = 10;\n}\n\nmessage C3BBReqBody {\n  optional uint64 uin = 1;\n  optional uint64 groupCode = 2;\n}\n\nmessage C3BBRspBody {\n  optional int32 ret = 1;\n  optional uint64 groupCode = 2;\n  optional bytes anonyName = 3;\n  optional uint32 portraitIndex = 4;\n  optional uint32 bubbleIndex = 5;\n  optional uint32 expiredTime = 6;\n  optional AnonyStatus anonyStatus = 10;\n  optional string color = 15;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/cmd0x6ff/smbcmd0x519.proto",
    "content": "syntax = \"proto2\";\npackage cmd0x6ff;\n\nmessage C519CRMMsgHead {\n  optional uint32 crmSubCmd = 1;\n  optional uint32 headLen = 2;\n  optional uint32 verNo = 3;\n  optional uint64 kfUin = 4;\n  optional uint32 seq = 5;\n  optional uint32 packNum = 6;\n  optional uint32 curPack = 7;\n  optional string bufSig = 8;\n  optional uint64 pubQq = 9;\n  optional uint32 clienttype = 10;\n  optional uint64 laborUin = 11;\n  optional string laborName = 12;\n  optional uint64 puin = 13;\n}\n\nmessage GetNavigationMenuReqBody {\n  optional uint64 puin = 1;\n  optional uint64 uin = 2;\n  optional uint32 verNo = 3;\n}\n\nmessage GetNavigationMenuRspBody {\n  optional C519RetInfo ret = 1;\n  optional int32 isShow = 2;\n  optional string uctMsg = 3;\n  optional uint32 verNo = 4;\n}\n\nmessage C519ReqBody {\n  optional uint32 subCmd = 1;\n  optional C519CRMMsgHead crmCommonHead = 2;\n  optional GetAddressDetailListReqBody getAddressDetailListReqBody = 33;\n  optional GetNavigationMenuReqBody getNavigationMenuReq = 35;\n}\n\nmessage C519RetInfo {\n  optional uint32 retCode = 1;\n  optional string errorMsg = 2;\n}\n\nmessage C519RspBody {\n  optional uint32 subCmd = 1;\n  optional C519CRMMsgHead crmCommonHead = 2;\n  optional GetAddressDetailListRspBody getAddressDetailListRspBody = 33;\n  optional GetNavigationMenuRspBody getNavigationMenuRsp = 35;\n}\n\nmessage GetAddressDetailListReqBody {\n  optional fixed32 timestamp = 1;\n  optional fixed64 timestamp2 = 2;\n}\n\nmessage GetAddressDetailListRspBody {\n  optional C519RetInfo ret = 1;\n  optional fixed32 timestamp = 2;\n  optional bool full = 3;\n  repeated AddressDetail addressDetail = 4;\n  optional fixed64 timestamp2 = 5;\n}\n\nmessage AddressDetail {\n  optional uint32 aid = 1;\n  optional fixed32 modifyTime = 2;\n  optional fixed32 createTime = 3;\n  optional uint32 status = 4;\n  optional uint32 groupid = 5;\n  optional bytes addGroupName = 6;\n  optional bytes name = 7;\n  optional uint32 gender = 8;\n  optional fixed32 birthday = 9;\n  optional bytes company0 = 10;\n  optional bytes companyPosition0 = 11;\n  optional bytes company1 = 12;\n  optional bytes companyPosition1 = 13;\n  optional bytes fixedPhone0 = 14;\n  optional bytes fixedPhone1 = 15;\n  optional bytes email0 = 16;\n  optional bytes email1 = 17;\n  optional bytes fax0 = 18;\n  optional bytes fax1 = 19;\n  optional bytes comment = 20;\n  optional bytes headUrl = 21;\n  repeated AddressMobileInfo mobilePhone = 22;\n  optional bool mobilePhoneUpdated = 23;\n  repeated AddressQQinfo qq = 24;\n  optional bool qqPhoneUpdated = 25;\n  optional fixed64 modifyTime2 = 26;\n  optional NewBizClientRegion clientRegion = 27;\n  optional NewBizClientRegionCode clientRegionCode = 28;\n}\n\nmessage AddressMobileInfo {\n  optional uint32 index = 1;\n  optional bytes account = 2;\n  optional bytes formattedAccount = 5;\n}\n\nmessage AddressQQinfo {\n  optional uint32 index = 1;\n  optional uint64 account = 2;\n}\n\nmessage NewBizClientRegion {\n  optional string clientNation = 1;\n  optional string clientProvince = 2;\n  optional string clientCity = 3;\n  optional string clientRegion = 4;\n}\n\nmessage NewBizClientRegionCode {\n  optional uint64 nationid = 1;\n  optional uint64 provinceid = 2;\n  optional uint64 cityid = 3;\n  optional uint64 regionid = 4;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/cmd0x6ff/subcmd0x501.proto",
    "content": "syntax = \"proto2\";\npackage cmd0x6ff;\n\nmessage C501ReqBody {\n  optional SubCmd0x501ReqBody ReqBody = 1281;\n}\nmessage C501RspBody {\n  optional SubCmd0x501RspBody RspBody = 1281;\n}\nmessage SubCmd0x501ReqBody {\n  optional uint64 uin = 1;\n  optional uint32 idcId = 2;\n  optional uint32 appid = 3;\n  optional uint32 loginSigType = 4;\n  optional bytes loginSigTicket = 5;\n  optional uint32 requestFlag = 6;\n  repeated uint32 serviceTypes = 7;\n  optional uint32 bid = 8;\n}\nmessage SubCmd0x501RspBody {\n  optional bytes sigSession = 1;\n  optional bytes sessionKey = 2;\n  repeated SrvAddrs addrs = 3;\n}\n\nmessage SrvAddrs {\n  optional uint32 serviceType = 1;\n  repeated IpAddr addrs = 2;\n}\n\nmessage IpAddr {\n  optional uint32 type = 1;\n  optional fixed32 ip = 2;\n  optional uint32 port = 3;\n  optional uint32 area = 4;\n}\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/cmd0x899/cmd0x899.proto",
    "content": "syntax = \"proto2\";\n\npackage cmd0x899;\n\nmessage ReqBody {\n  optional uint64 groupCode = 1;\n  optional uint64 startUin = 2;\n  optional uint32 identifyFlag = 3;\n  repeated uint64 uinList = 4;\n  optional memberlist memberlistOpt = 5;\n  optional uint32 memberNum = 6;\n  optional uint32 filterMethod = 7;\n  optional uint32 onlineFlag = 8;\n}\n\nmessage RspBody {\n  optional uint64 groupCode = 1;\n  optional uint64 startUin = 2;\n  optional uint32 identifyFlag = 3;\n  repeated memberlist memberlist = 4;\n  optional bytes errorinfo = 5;\n}\n\nmessage memberlist {\n  optional uint64 memberUin = 1;\n  optional uint32 uinFlag = 2;\n  optional uint32 uinFlagex = 3;\n  optional uint32 uinMobileFlag = 4;\n  optional uint32 uinArchFlag = 5;\n  optional uint32 joinTime = 6;\n  optional uint32 oldMsgSeq = 7;\n  optional uint32 newMsgSeq = 8;\n  optional uint32 lastSpeakTime = 9;\n  optional uint32 level = 10;\n  optional uint32 point = 11;\n  optional uint32 shutupTimestap = 12;\n  optional uint32 flagex2 = 13;\n  optional bytes specialTitle = 14;\n  optional uint32 specialTitleExpireTime = 15;\n  optional uint32 activeDay = 16;\n  optional bytes uinKey = 17;\n  optional uint32 privilege = 18;\n  optional bytes richInfo = 19;\n}\n\nmessage uin_key {\n  optional uint64 groupCode = 1;\n  optional uint64 memberUin = 2;\n  optional uint64 genTime = 3;\n  optional uint32 validTime = 4;\n  optional uint32 randNum = 5;\n}\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/data.proto",
    "content": "syntax = \"proto3\";\n\npackage pb;\n\n\nmessage DeviceInfo {\n  string bootloader = 1;\n  string procVersion = 2;\n  string codename = 3;\n  string incremental = 4;\n  string fingerprint = 5;\n  string bootId = 6;\n  string androidId = 7;\n  string baseBand = 8;\n  string innerVersion = 9;\n}\n\nmessage RequestBody {\n  repeated ConfigSeq rpt_config_list = 1;\n}\n\nmessage ConfigSeq {\n  int32 type = 1;\n  int32 version = 2;\n}\n\nmessage D50ReqBody {\n  int64 appid = 1;\n  int32 maxPkgSize = 2;\n  int32 startTime = 3;\n  int32 startIndex = 4;\n  int32 reqNum = 5;\n  repeated int64 uinList = 6;\n  int32 reqMusicSwitch = 91001;\n  int32 reqMutualmarkAlienation = 101001;\n  int32 reqMutualmarkScore = 141001;\n  int32 reqKsingSwitch = 151001;\n  int32 reqMutualmarkLbsshare = 181001;\n}\n\nmessage D388ReqBody {\n  int32 netType = 1;\n  int32 subcmd = 2;\n  repeated TryUpImgReq msgTryUpImgReq = 3;\n  repeated TryUpPttReq msgTryUpPttReq = 5;\n  repeated GetPttUrlReq msgGetPttReq = 6;\n  int32 commandId = 7;\n  bytes extension = 1001;\n}\n\nmessage D388RespBody {\n  int32 clientIp = 1;\n  int32 subCmd = 2;\n  repeated TryUpImgResp msgTryUpImgRsp = 3;\n  repeated TryUpPttResp msgTryUpPttRsp = 5;\n  repeated GetPttUrlRsp msgGetPttUrlRsp = 6;\n}\n\nmessage GetPttUrlReq {\n  int64 groupCode = 1;\n  int64 dstUin = 2;\n  int64 fileId = 3;\n  bytes fileMd5 = 4;\n  int32 reqTerm = 5;\n  int32 reqPlatformType = 6;\n  int32 innerIp = 7;\n  int32 buType = 8;\n  bytes buildVer = 9;\n  //int64 fileId = 10;\n  bytes fileKey = 11;\n  int32 codec = 12;\n  int32 buId = 13;\n  int32 reqTransferType = 14;\n  int32 isAuto = 15;\n}\n\nmessage GetPttUrlRsp {\n  int64 fileId = 1;\n  bytes fileMd5 = 2;\n  int32 result = 3;\n  bytes failMsg = 4;\n  bytes bytesDownUrl = 5;\n  repeated int32 uint32DownIp = 6;\n  repeated int32 uint32DownPort = 7;\n  bytes downDomain = 8;\n  bytes downPara = 9;\n  //int64 fileId = 10;\n  int32 transferType = 11;\n  int32 allowRetry = 12;\n  //repeated IPv6Info msgDownIp6 = 26;\n  bytes clientIp6 = 27;\n  string strDomain = 28;\n}\n\nmessage ReqDataHighwayHead {\n  DataHighwayHead msgBasehead = 1;\n  SegHead msgSeghead = 2;\n  bytes reqExtendinfo = 3;\n  int64 timestamp = 4;\n  //LoginSigHead? msgLoginSigHead = 5;\n}\n\nmessage RspDataHighwayHead {\n  DataHighwayHead msgBasehead = 1;\n  SegHead msgSeghead = 2;\n  int32 errorCode = 3;\n  int32 allowRetry = 4;\n  int32 cachecost = 5;\n  int32 htcost = 6;\n  bytes rspExtendinfo = 7;\n  int64 timestamp = 8;\n  int64 range = 9;\n  int32 isReset = 10;\n}\n\nmessage DataHighwayHead {\n  int32 version = 1;\n  string uin = 2;\n  string command = 3;\n  int32 seq = 4;\n  int32 retryTimes = 5;\n  int32 appid = 6;\n  int32 dataflag = 7;\n  int32 commandId = 8;\n  string buildVer = 9;\n  int32 localeId = 10;\n}\n\nmessage SegHead {\n  int32 serviceid = 1;\n  int64 filesize = 2;\n  int64 dataoffset = 3;\n  int32 datalength = 4;\n  int32 rtcode = 5;\n  bytes serviceticket = 6;\n  int32 flag = 7;\n  bytes md5 = 8;\n  bytes fileMd5 = 9;\n  int32 cacheAddr = 10;\n  int32 queryTimes = 11;\n  int32 updateCacheip = 12;\n}\n\nmessage TryUpImgReq {\n  int64 groupCode = 1;\n  int64 srcUin = 2;\n  int64 fileId = 3;\n  bytes fileMd5 = 4;\n  int64 fileSize = 5;\n  string fileName = 6;\n  int32 srcTerm = 7;\n  int32 platformType = 8;\n  int32 buType = 9;\n  int32 picWidth = 10;\n  int32 picHeight = 11;\n  int32 picType = 12;\n  string buildVer = 13;\n  int32 innerIp = 14;\n  int32 appPicType = 15;\n  int32 originalPic = 16;\n  bytes fileIndex = 17;\n  int64 dstUin = 18;\n  int32 srvUpload = 19;\n  bytes transferUrl = 20;\n}\n\nmessage TryUpImgResp {\n  int64 fileId = 1;\n  int32 result = 2;\n  string failMsg = 3;\n  bool boolFileExit = 4;\n  ImgInfo msgImgInfo = 5;\n  repeated uint32 uint32UpIp = 6;\n  repeated uint32 uint32UpPort = 7;\n  bytes upUkey = 8;\n  int64 fid = 9;\n}\n\nmessage TryUpPttReq {\n  int64 groupCode = 1;\n  int64 srcUin = 2;\n  int64 fileId = 3;\n  bytes fileMd5 = 4;\n  int64 fileSize = 5;\n  bytes fileName = 6;\n  int32 srcTerm = 7;\n  int32 platformType = 8;\n  int32 buType = 9;\n  string buildVer = 10;\n  int32 innerIp = 11;\n  int32 voiceLength = 12;\n  bool boolNewUpChan = 13;\n  int32 codec = 14;\n  int32 voiceType = 15;\n  int32 buId = 16;\n\n}\n\nmessage TryUpPttResp {\n  int64 fileId = 1;\n  int32 result = 2;\n  string failMsg = 3;\n  bool boolFileExit = 4;\n  repeated int32 uint32UpIp = 5;\n  repeated int32 uint32UpPort = 6;\n  bytes upUkey = 7;\n  int64 fileId2 = 8;\n  int64 upOffset = 9;\n  int64 blockSize = 10;\n  bytes fileKey = 11;\n  int32 channelType = 12;\n  //    List<IPv6Info>? msgUpIp6 = 26;\n  //    bytes clientIp6 = 27;\n\n}\n\nmessage ImgInfo {\n  bytes fileMd5 = 1;\n  int32 fileType = 2;\n  int64 fileSize = 3;\n  int32 fileWidth = 4;\n  int32 fileHeight = 5;\n}\n\nmessage DeleteMessageRequest {\n  repeated MessageItem items = 1;\n}\n\nmessage MessageItem {\n  int64 fromUin = 1;\n  int64 toUin = 2;\n  int32 msgType = 3;\n  int32 msgSeq = 4;\n  int64 msgUid = 5;\n  bytes sig = 7;\n}\n\nmessage SubD4 {\n  int64 uin = 1;\n}\n\nmessage Sub8A {\n  repeated Sub8AMsgInfo msg_info = 1;\n  int32 appId = 2;\n  int32 instId = 3;\n  int32 longMessageFlag = 4;\n  bytes reserved = 5;\n}\n\nmessage Sub8AMsgInfo {\n  int64 fromUin = 1;\n  int64 toUin = 2;\n  int32 msgSeq = 3;\n  int64 msgUid = 4;\n  int64 msgTime = 5;\n  int32 msgRandom = 6;\n  int32 pkgNum = 7;\n  int32 pkgIndex = 8;\n  int32 devSeq = 9;\n}\n\nmessage SubB3 {\n  int32 type = 1;\n  SubB3AddFrdNotify msgAddFrdNotify = 2;\n}\n\nmessage SubB3AddFrdNotify {\n  int64 uin = 1;\n  string nick = 5;\n}\n\nmessage Sub44 {\n  Sub44FriendSyncMsg friendSyncMsg = 1;\n  Sub44GroupSyncMsg groupSyncMsg = 2;\n}\n\nmessage Sub44FriendSyncMsg {\n  int64 uin = 1;\n  int64 fUin = 2;\n  int32 processType = 3;\n  int32 time = 4;\n  int32 processFlag = 5;\n  int32 sourceId = 6;\n  int32 sourceSubId = 7;\n  repeated string strWording = 8;\n}\n\nmessage Sub44GroupSyncMsg {\n  int32 msgType = 1;\n  int64 msgSeq = 2;\n  int64 grpCode = 3;\n  int64 gaCode = 4;\n  int64 optUin1 = 5;\n  int64 optUin2 = 6;\n  bytes msgBuf = 7;\n  bytes authKey = 8;\n  int32 msgStatus = 9;\n  int64 actionUin = 10;\n  int64 actionTime = 11;\n  int32 curMaxMemCount = 12;\n  int32 nextMaxMemCount = 13;\n  int32 curMemCount = 14;\n  int32 reqSrcId = 15;\n  int32 reqSrcSubId = 16;\n  int32 inviterRole = 17;\n  int32 extAdminNum = 18;\n  int32 processFlag = 19;\n}\n\nmessage GroupMemberReqBody {\n  int64 groupCode = 1;\n  int64 uin = 2;\n  bool newClient = 3;\n  int32 clientType = 4;\n  int32 richCardNameVer = 5;\n}\n\nmessage GroupMemberRspBody {\n  int64 groupCode = 1;\n  int32 selfRole = 2;\n  GroupMemberInfo memInfo = 3;\n  bool boolSelfLocationShared = 4;\n  int32 groupType = 5;\n}\n\nmessage GroupMemberInfo {\n  int64 uin = 1;\n  int32 result = 2;\n  bytes errmsg = 3;\n  bool IsFriend = 4;\n  bytes remark = 5;\n  bool IsConcerned = 6;\n  int32 credit = 7;\n  bytes card = 8;\n  int32 sex = 9;\n  bytes location = 10;\n  bytes nick = 11;\n  int32 age = 12;\n  bytes lev = 13;\n  int64 join = 14;\n  int64 lastSpeak = 15;\n  //repeated CustomEntry customEnties = 16;\n  //repeated GBarInfo gbarConcerned = 17;\n  bytes gbarTitle = 18;\n  bytes gbarUrl = 19;\n  int32 gbarCnt = 20;\n  bool isAllowModCard = 21;\n  bool isVip = 22;\n  bool isYearVip = 23;\n  bool isSuperVip = 24;\n  bool isSuperQq = 25;\n  int32 vipLev = 26;\n  int32 role = 27;\n  bool locationShared = 28;\n  int64 int64Distance = 29;\n  int32 concernType = 30;\n  bytes specialTitle = 31;\n  int32 specialTitleExpireTime = 32;\n  //FlowersEntry flowerEntry = 33;\n  //TeamEntry teamEntry = 34;\n  bytes phoneNum = 35;\n  bytes job = 36;\n  int32 medalId = 37;\n\n  int32 level = 39;\n\n  string honor = 41;\n}\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/longmsg/longmsg.proto",
    "content": "syntax = \"proto3\";\n\npackage longmsg;\n\nmessage LongMsgDeleteReq {\n  bytes msgResid = 1;\n  int32 msgType = 2;\n}\nmessage LongMsgDeleteRsp {\n  int32 result = 1;\n  bytes msgResid = 2;\n}\nmessage LongMsgDownReq {\n  int32 srcUin = 1;\n  bytes msgResid = 2;\n  int32 msgType = 3;\n  int32 needCache = 4;\n}\nmessage LongMsgDownRsp {\n  int32 result = 1;\n  bytes msgResid = 2;\n  bytes msgContent = 3;\n}\nmessage LongMsgUpReq {\n  int32 msgType = 1;\n  int64 dstUin = 2;\n  int32 msgId = 3;\n  bytes msgContent = 4;\n  int32 storeType = 5;\n  bytes msgUkey = 6;\n  int32 needCache = 7;\n}\nmessage LongMsgUpRsp {\n  int32 result = 1;\n  int32 msgId = 2;\n  bytes msgResid = 3;\n}\nmessage LongReqBody {\n  int32 subcmd = 1;\n  int32 termType = 2;\n  int32 platformType = 3;\n  repeated LongMsgUpReq msgUpReq = 4;\n  repeated LongMsgDownReq msgDownReq = 5;\n  repeated LongMsgDeleteReq msgDelReq = 6;\n  int32 agentType = 10;\n}\nmessage LongRspBody {\n  int32 subcmd = 1;\n  repeated LongMsgUpRsp msgUpRsp = 2;\n  repeated LongMsgDownRsp msgDownRsp = 3;\n  repeated LongMsgDeleteRsp msgDelRsp = 4;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/msf/register_proxy.proto",
    "content": "syntax = \"proto2\";\n\npackage msf;\n\nmessage DiscussList {\n  optional uint64 discussCode = 1;\n  optional uint64 discussSeq = 2;\n  optional uint64 memberSeq = 3;\n  optional uint64 infoSeq = 4;\n  optional bool bHotGroup = 5;\n  optional uint64 redpackTime = 6;\n  optional bool hasMsg = 7;\n  optional int64 dicussFlag = 8;\n}\n\nmessage GroupList {\n  optional uint64 groupCode = 1;\n  optional uint64 groupSeq = 2;\n  optional uint64 memberSeq = 3;\n  optional uint64 mask = 4;\n  optional uint64 redpackTime = 5;\n  optional bool hasMsg = 6;\n  optional int64 groupFlag = 7;\n  optional uint64 groupType = 8;\n  optional uint32 groupNameSeq = 9;\n  optional uint32 groupMemberSeq = 10;\n  optional uint32 uinFlagEx2 = 11;\n  optional uint32 importantMsgLatestSeq = 12;\n}\n\nmessage SvcPbResponsePullDisMsgProxy {\n  optional uint64 memberSeq = 1;\n  optional bytes content = 2;\n}\n\nmessage SvcRegisterProxyMsgResp {\n  optional uint32 result = 1;\n  optional bytes errMsg = 2;\n  optional uint32 flag = 3;\n  optional uint32 seq = 4;\n  optional SvcResponseMsgInfo info = 5;\n  repeated GroupList groupList = 6;\n  repeated DiscussList discussList = 7;\n  repeated SvcResponsePbPullGroupMsgProxy groupMsg = 8;\n  repeated SvcPbResponsePullDisMsgProxy discussMsg = 9;\n  optional bytes c2CMsg = 10;\n  optional bytes pubAccountMsg = 11;\n  optional uint32 discussListFlag = 12;\n}\n\nmessage SvcResponseMsgInfo {\n  optional uint32 groupNum = 1;\n  optional uint32 discussNum = 2;\n}\n\nmessage SvcResponsePbPullGroupMsgProxy {\n  optional uint64 memberSeq = 1;\n  optional bytes content = 2;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/msg/TextMsgExt.proto",
    "content": "syntax = \"proto2\";\n\npackage msg;\n\nmessage ExtChannelInfo {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n}\n\nmessage TextResvAttr {\n  optional bytes wording = 1;\n  optional uint32 textAnalysisResult = 2;\n  optional uint32 atType = 3;\n  optional uint64 atMemberUin = 4;\n  optional uint64 atMemberTinyid = 5;\n  optional ExtRoleInfo atMemberRoleInfo = 6;\n  optional ExtRoleInfo atRoleInfo = 7;\n  optional ExtChannelInfo atChannelInfo = 8;\n}\n\nmessage ExtRoleInfo {\n  optional uint64 id = 1;\n  optional bytes info = 2;\n  optional uint32 flag = 3;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/msg/head.proto",
    "content": "syntax = \"proto2\";\npackage msg;\n\nmessage C2CHead {\n  optional uint64 toUin = 1;\n  optional uint64 fromUin = 2;\n  optional uint32 ccType = 3;\n  optional uint32 ccCmd = 4;\n  optional bytes authPicSig = 5;\n  optional bytes authSig = 6;\n  optional bytes authBuf = 7;\n  optional uint32 serverTime = 8;\n  optional uint32 clientTime = 9;\n  optional uint32 rand = 10;\n  optional string phoneNumber = 11;\n}\n\nmessage CSHead {\n  optional uint64 uin = 1;\n  optional uint32 command = 2;\n  optional uint32 seq = 3;\n  optional uint32 version = 4;\n  optional uint32 retryTimes = 5;\n  optional uint32 clientType = 6;\n  optional uint32 pubno = 7;\n  optional uint32 localid = 8;\n  optional uint32 timezone = 9;\n  optional fixed32 clientIp = 10;\n  optional uint32 clientPort = 11;\n  optional fixed32 connIp = 12;\n  optional uint32 connPort = 13;\n  optional fixed32 interfaceIp = 14;\n  optional uint32 interfacePort = 15;\n  optional fixed32 actualIp = 16;\n  optional uint32 flag = 17;\n  optional fixed32 timestamp = 18;\n  optional uint32 subcmd = 19;\n  optional uint32 result = 20;\n  optional uint32 appId = 21;\n  optional uint32 instanceId = 22;\n  optional uint64 sessionId = 23;\n  optional uint32 idcId = 24;\n}\n\nmessage DeltaHead {\n  optional uint64 totalLen = 1;\n  optional uint64 offset = 2;\n  optional uint64 ackOffset = 3;\n  optional bytes cookie = 4;\n  optional bytes ackCookie = 5;\n  optional uint32 result = 6;\n  optional uint32 flags = 7;\n}\n\nmessage IMHead {\n  optional uint32 headType = 1;\n  optional CSHead csHead = 2;\n  optional S2CHead s2CHead = 3;\n  optional HttpConnHead httpconnHead = 4;\n  optional uint32 paintFlag = 5;\n  optional LoginSig loginSig = 6;\n  optional DeltaHead deltaHead = 7;\n  optional C2CHead c2CHead = 8;\n}\n\nmessage HttpConnHead {\n  optional uint64 uin = 1;\n  optional uint32 command = 2;\n  optional uint32 subCommand = 3;\n  optional uint32 seq = 4;\n  optional uint32 version = 5;\n  optional uint32 retryTimes = 6;\n  optional uint32 clientType = 7;\n  optional uint32 pubNo = 8;\n  optional uint32 localId = 9;\n  optional uint32 timeZone = 10;\n  optional fixed32 clientIp = 11;\n  optional uint32 clientPort = 12;\n  optional fixed32 qzhttpIp = 13;\n  optional uint32 qzhttpPort = 14;\n  optional fixed32 sppIp = 15;\n  optional uint32 sppPort = 16;\n  optional uint32 flag = 17;\n  optional bytes key = 18;\n  optional uint32 compressType = 19;\n  optional uint32 originSize = 20;\n  optional uint32 errorCode = 21;\n  optional RedirectMsg redirect = 22;\n  optional uint32 commandId = 23;\n  optional uint32 serviceCmdid = 24;\n  optional TransOidbHead oidbhead = 25;\n}\n\n\nmessage LoginSig {\n  optional uint32 type = 1;\n  optional bytes sig = 2;\n}\n\nmessage RedirectMsg {\n  optional fixed32 lastRedirectIp = 1;\n  optional uint32 lastRedirectPort = 2;\n  optional fixed32 redirectIp = 3;\n  optional uint32 redirectPort = 4;\n  optional uint32 redirectCount = 5;\n}\n\nmessage S2CHead {\n  optional uint32 subMsgtype = 1;\n  optional uint32 msgType = 2;\n  optional uint64 fromUin = 3;\n  optional uint32 msgId = 4;\n  optional fixed32 relayIp = 5;\n  optional uint32 relayPort = 6;\n  optional uint64 toUin = 7;\n}\n\nmessage TransOidbHead {\n  optional uint32 command = 1;\n  optional uint32 serviceType = 2;\n  optional uint32 result = 3;\n  optional string errorMsg = 4;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/msg/msg.proto",
    "content": "syntax = \"proto2\";\n\npackage msg;\n\nmessage GetMessageRequest {\n  optional SyncFlag syncFlag = 1;\n  optional bytes syncCookie = 2;\n  optional int32 rambleFlag = 3;\n  optional int32 latestRambleNumber = 4;\n  optional int32 otherRambleNumber = 5;\n  optional int32 onlineSyncFlag = 6;\n  optional int32 contextFlag = 7;\n  optional int32 whisperSessionId = 8;\n  optional int32 msgReqType = 9;\n  optional bytes pubaccountCookie = 10;\n  optional bytes msgCtrlBuf = 11;\n  optional bytes serverBuf = 12;\n}\n\nmessage SendMessageRequest {\n  optional RoutingHead routingHead = 1;\n  optional ContentHead contentHead = 2;\n  optional MessageBody msgBody = 3;\n  optional int32 msgSeq = 4;\n  optional int32 msgRand = 5;\n  optional bytes syncCookie = 6;\n  //MsgComm.AppShareInfo? appShare = 7;\n  optional int32 msgVia = 8;\n  optional int32 dataStatist = 9;\n  //MultiMsgAssist? multiMsgAssist = 10;\n  //PbInputNotifyInfo? inputNotifyInfo = 11;\n  optional MsgCtrl msgCtrl = 12;\n  //ImReceipt.ReceiptReq? receiptReq = 13;\n  optional int32 multiSendSeq = 14;\n}\n\nmessage SendMessageResponse {\n  optional int32 result = 1;\n  optional string errMsg = 2;\n}\n\nmessage MsgWithDrawReq {\n  repeated C2CMsgWithDrawReq c2cWithDraw = 1;\n  repeated GroupMsgWithDrawReq groupWithDraw = 2;\n}\n\nmessage C2CMsgWithDrawReq{\n  repeated C2CMsgInfo msgInfo = 1;\n  optional int32 longMessageFlag = 2;\n  optional bytes reserved = 3;\n  optional int32 subCmd = 4;\n}\n\nmessage GroupMsgWithDrawReq{\n  optional int32 subCmd = 1;\n  optional int32 groupType = 2;\n  optional int64 groupCode = 3;\n  repeated GroupMsgInfo msgList = 4;\n  optional bytes userDef = 5;\n}\n\nmessage MsgWithDrawResp {\n  repeated C2CMsgWithDrawResp c2cWithDraw = 1;\n  repeated GroupMsgWithDrawResp groupWithDraw = 2;\n}\n\nmessage C2CMsgWithDrawResp {\n  optional int32 result = 1;\n  optional string errMsg = 2;\n}\n\nmessage GroupMsgWithDrawResp {\n  optional int32 result = 1;\n  optional string errMsg = 2;\n}\n\nmessage GroupMsgInfo {\n  optional int32 msgSeq = 1;\n  optional int32 msgRandom = 2;\n  optional int32 msgType = 3;\n}\n\nmessage C2CMsgInfo {\n  optional int64 fromUin = 1;\n  optional int64 toUin = 2;\n  optional int32 msgSeq = 3;\n  optional int64 msgUid = 4;\n  optional int64 msgTime = 5;\n  optional int32 msgRandom = 6;\n  optional int32 pkgNum = 7;\n  optional int32 pkgIndex = 8;\n  optional int32 divSeq = 9;\n  optional int32 msgType = 10;\n  optional RoutingHead routingHead = 20;\n}\n\nmessage RoutingHead {\n  oneof RoutingHead{\n    C2C c2c = 1;\n    Grp grp = 2;\n    GrpTmp grpTmp = 3;\n    WPATmp wpaTmp = 6;\n  }\n  /*\n  Dis dis = 4;\n  DisTmp disTmp = 5;\n  SecretFileHead? secretFile = 7;\n  PublicPlat? publicPlat = 8;\n  TransMsg? transMsg = 9;\n  AddressListTmp? addressList = 10;\n  RichStatusTmp? richStatusTmp = 11;\n  TransCmd? transCmd = 12;\n  AccostTmp? accostTmp = 13;\n  PubGroupTmp? pubGroupTmp = 14;\n  Trans0x211? trans0x211 = 15;\n  BusinessWPATmp? businessWpaTmp = 16;\n  AuthTmp? authTmp = 17;\n  BsnsTmp? bsnsTmp = 18;\n  QQQueryBusinessTmp? qqQuerybusinessTmp = 19;\n  NearByDatingTmp? nearbyDatingTmp = 20;\n  NearByAssistantTmp? nearbyAssistantTmp = 21;\n  CommTmp? commTmp = 22;\n  */\n}\n\nmessage WPATmp {\n  optional uint64 toUin = 1;\n  optional bytes sig = 2;\n}\n\nmessage C2C {\n  optional int64 toUin = 1;\n}\n\nmessage Grp {\n  optional int64 groupCode = 1;\n}\n\nmessage GrpTmp {\n  optional int64 groupUin = 1;\n  optional int64 toUin = 2;\n}\n\nmessage MsgCtrl {\n  optional int32 msgFlag = 1;\n}\n\nmessage GetMessageResponse {\n  optional int32 result = 1;\n  optional string errorMessage = 2;\n  optional bytes syncCookie = 3;\n  optional SyncFlag syncFlag = 4;\n  repeated UinPairMessage uinPairMsgs = 5;\n  optional int64 bindUin = 6;\n  optional int32 msgRspType = 7;\n  optional bytes pubAccountCookie = 8;\n  optional bool isPartialSync = 9;\n  optional bytes msgCtrlBuf = 10;\n}\n\nmessage PushMessagePacket {\n  optional Message message = 1;\n  optional int32 svrip = 2;\n  optional bytes pushToken = 3;\n  optional int32 pingFLag = 4;\n  optional int32 generalFlag = 9;\n}\n\nmessage UinPairMessage {\n  optional int32 lastReadTime = 1;\n  optional int64 peerUin = 2;\n  optional int32 msgCompleted = 3;\n  repeated Message messages = 4;\n}\n\nmessage Message {\n  optional MessageHead head = 1;\n  optional ContentHead content = 2;\n  optional MessageBody body = 3;\n}\n\nmessage MessageBody {\n  optional RichText richText = 1;\n  optional bytes msgContent = 2;\n  optional bytes msgEncryptContent = 3;\n}\n\nmessage RichText {\n  optional Attr attr = 1;\n  repeated Elem elems = 2;\n  optional NotOnlineFile notOnlineFile = 3;\n  optional Ptt ptt = 4;\n}\n\nmessage Elem {\n  oneof elem {\n    Text text = 1;\n    Face face = 2;\n    OnlineImage onlineImage = 3;\n    NotOnlineImage notOnlineImage = 4;\n    TransElem transElemInfo = 5;\n    MarketFace marketFace = 6;\n    //ElemFlags elemFlags = 7;\n    CustomFace customFace = 8;\n    ElemFlags2 elemFlags2 = 9;\n    //FunFace funFace = 10;\n    //SecretFileMsg secretFile = 11;\n    RichMsg richMsg = 12;\n    GroupFile groupFile = 13;\n    //PubGroup pubGroup = 14;\n    //MarketTrans marketTrans = 15;\n    ExtraInfo extraInfo = 16;\n    //ShakeWindow? shakeWindow = 17;\n    //PubAccount? pubAccount = 18;\n    VideoFile videoFile = 19;\n    //TipsInfo? tipsInfo = 20;\n    AnonymousGroupMessage anonGroupMsg = 21;\n    //QQLiveOld? qqLiveOld = 22;\n    //LifeOnlineAccount? lifeOnline = 23;\n    QQWalletMsg QQWalletMsg = 24;\n    //CrmElem? crmElem = 25;\n    //ConferenceTipsInfo? conferenceTipsInfo = 26;\n    //RedBagInfo? redbagInfo = 27;\n    //LowVersionTips? lowVersionTips = 28;\n    //bytes bankcodeCtrlInfo = 29;\n    //NearByMessageType? nearByMsg = 30;\n    CustomElem customElem = 31;\n    //LocationInfo? locationInfo = 32;\n    //PubAccInfo? pubAccInfo = 33;\n    //SmallEmoji? smallEmoji = 34;\n    //FSJMessageElem? fsjMsgElem = 35;\n    //ArkAppElem? arkApp = 36;\n    GeneralFlags generalFlags = 37;\n    //CustomFace? hcFlashPic = 38;\n    //DeliverGiftMsg? deliverGiftMsg = 39;\n    //BitAppMsg? bitappMsg = 40;\n    //OpenQQData? openQqData = 41;\n    //ApolloActMsg? apolloMsg = 42;\n    //GroupPubAccountInfo? groupPubAccInfo = 43;\n    //BlessingMessage? blessMsg = 44;\n    SourceMsg srcMsg = 45;\n    //LolaMsg? lolaMsg = 46;\n    //GroupBusinessMsg? groupBusinessMsg = 47;\n    //WorkflowNotifyMsg? msgWorkflowNotify = 48;\n    //PatsElem? patElem = 49;\n    //GroupPostElem? groupPostElem = 50;\n    LightApp lightApp = 51;\n    //EIMInfo? eimInfo = 52;\n    CommonElem commonElem = 53;\n  }\n}\n\nmessage MarketFace {\n  optional bytes faceName = 1;\n  optional uint32 itemType = 2;\n  optional uint32 faceInfo = 3;\n  optional bytes faceId = 4;\n  optional uint32 tabId = 5;\n  optional uint32 subType = 6;\n  optional bytes key = 7;\n  optional bytes param = 8;\n  optional uint32 mediaType = 9;\n  optional uint32 imageWidth = 10;\n  optional uint32 imageHeight = 11;\n  optional bytes mobileparam = 12;\n  optional bytes pbReserve = 13;\n}\n\nmessage ElemFlags2 {\n  optional uint32 colorTextId = 1;\n  optional uint64 msgId = 2;\n  optional uint32 whisperSessionId = 3;\n  optional uint32 pttChangeBit = 4;\n  optional uint32 vipStatus = 5;\n  optional uint32 compatibleId = 6;\n  repeated Inst insts = 7;\n  optional uint32 msgRptCnt = 8;\n  optional Inst srcInst = 9;\n  optional uint32 longtitude = 10;\n  optional uint32 latitude = 11;\n  optional uint32 customFont = 12;\n  optional PcSupportDef pcSupportDef = 13;\n  optional uint32 crmFlags = 14;\n\n  message Inst {\n    optional uint32 appId = 1;\n    optional uint32 instId = 2;\n  }\n}\n\nmessage PcSupportDef {\n  optional uint32 pcPtlBegin = 1;\n  optional uint32 pcPtlEnd = 2;\n  optional uint32 macPtlBegin = 3;\n  optional uint32 macPtlEnd = 4;\n  repeated uint32 ptlsSupport = 5;\n  repeated uint32 ptlsNotSupport = 6;\n}\n\nmessage CommonElem {\n  optional int32 serviceType = 1;\n  optional bytes pbElem = 2;\n  optional int32 businessType = 3;\n}\n\nmessage QQWalletMsg {\n  optional QQWalletAioBody aioBody = 1;\n}\n\nmessage QQWalletAioBody {\n  optional uint64 sendUin = 1;\n  optional QQWalletAioElem sender = 2;\n  optional QQWalletAioElem receiver = 3;\n  optional sint32 ChannelId = 4;\n  optional sint32 templateId = 5;\n  optional uint32 resend = 6;\n  optional uint32 msgPriority = 7;\n  optional sint32 redType = 8;\n  optional bytes billNo = 9;\n  optional bytes authKey = 10;\n  optional sint32 sessionType = 11;\n  optional sint32 msgType = 12;\n  optional sint32 envelOpeId = 13;\n  optional bytes name = 14;\n  optional sint32 confType = 15;\n  optional sint32 msgFrom = 16;\n  optional bytes pcBody = 17;\n  optional bytes index = 18;\n  optional uint32 redChannel = 19;\n  repeated uint64 grapUin = 20;\n  optional bytes pbReserve = 21;\n}\n\nmessage QQWalletAioElem{\n  optional uint32 background = 1;\n  optional uint32 icon = 2;\n  optional string title = 3;\n  optional string subtitle = 4;\n  optional string content = 5;\n  optional bytes linkUrl = 6;\n  optional bytes blackStripe = 7;\n  optional bytes notice = 8;\n  optional uint32 titleColor = 9;\n  optional uint32 subtitleColor = 10;\n  optional bytes actionsPriority = 11;\n  optional bytes jumpUrl = 12;\n  optional bytes nativeIos = 13;\n  optional bytes nativeAndroid = 14;\n  optional bytes iconUrl = 15;\n  optional uint32 contentColor = 16;\n  optional uint32 contentBgColor = 17;\n  optional bytes aioImageLeft = 18;\n  optional bytes aioImageRight = 19;\n  optional bytes cftImage = 20;\n  optional bytes pbReserve = 21;\n}\n\nmessage RichMsg {\n  optional bytes template1 = 1;\n  optional int32 serviceId = 2;\n  optional bytes msgResId = 3;\n  optional int32 rand = 4;\n  optional int32 seq = 5;\n}\n\nmessage CustomElem {\n  optional bytes desc = 1;\n  optional bytes data = 2;\n  optional int32  enumType = 3;\n  optional bytes ext = 4;\n  optional bytes sound = 5;\n}\n\nmessage Text {\n  optional string str = 1;\n  optional string link = 2;\n  optional bytes attr6Buf = 3;\n  optional bytes attr7Buf = 4;\n  optional bytes buf = 11;\n  optional bytes pbReserve = 12;\n}\n\nmessage Attr {\n  optional int32 codePage = 1;\n  optional int32 time = 2;\n  optional int32 random = 3;\n  optional int32 color = 4;\n  optional int32 size = 5;\n  optional int32 effect = 6;\n  optional int32 charSet = 7;\n  optional int32 pitchAndFamily = 8;\n  optional string fontName = 9;\n  optional bytes reserveData = 10;\n}\n\nmessage Ptt {\n  optional int32 fileType = 1;\n  optional int64 srcUin = 2;\n  optional bytes fileUuid = 3;\n  optional bytes fileMd5 = 4;\n  optional string fileName = 5;\n  optional int32 fileSize = 6;\n  optional bytes reserve = 7;\n  optional int32 fileId = 8;\n  optional int32 serverIp = 9;\n  optional int32 serverPort = 10;\n  optional bool boolValid = 11;\n  optional bytes signature = 12;\n  optional bytes shortcut = 13;\n  optional bytes fileKey = 14;\n  optional int32 magicPttIndex = 15;\n  optional int32 voiceSwitch = 16;\n  optional bytes pttUrl = 17;\n  optional bytes groupFileKey = 18;\n  optional int32 time = 19;\n  optional bytes downPara = 20;\n  optional int32 format = 29;\n  optional bytes pbReserve = 30;\n  repeated bytes bytesPttUrls = 31;\n  optional int32 downloadFlag = 32;\n}\n\nmessage OnlineImage {\n  optional bytes guid = 1;\n  optional bytes filePath = 2;\n  optional  bytes oldVerSendFile = 3;\n}\n\nmessage NotOnlineImage {\n  optional string filePath = 1;\n  optional uint32 fileLen = 2;\n  optional string downloadPath = 3;\n  optional bytes oldVerSendFile = 4;\n  optional int32 imgType = 5;\n  optional bytes previewsImage = 6;\n  optional bytes picMd5 = 7;\n  optional uint32 picHeight = 8;\n  optional uint32 picWidth = 9;\n  optional string resId = 10;\n  optional bytes flag = 11;\n  optional string thumbUrl = 12;\n  optional int32 original = 13;\n  optional string bigUrl = 14;\n  optional string origUrl = 15;\n  optional int32 bizType = 16;\n  optional int32 result = 17;\n  optional int32 index = 18;\n  optional bytes opFaceBuf = 19;\n  optional bool oldPicMd5 = 20;\n  optional int32 thumbWidth = 21;\n  optional int32 thumbHeight = 22;\n  optional int32 fileId = 23;\n  optional int32 showLen = 24;\n  optional int32 downloadLen = 25;\n  optional bytes pbReserve = 29;\n}\n\nmessage NotOnlineFile {\n  optional int32 fileType = 1;\n  optional bytes sig = 2;\n  optional bytes fileUuid = 3;\n  optional bytes fileMd5 = 4;\n  optional bytes fileName = 5;\n  optional int64 fileSize = 6;\n  optional bytes note = 7;\n  optional int32 reserved = 8;\n  optional int32 subcmd = 9;\n  optional int32 microCloud = 10;\n  repeated bytes bytesFileUrls = 11;\n  optional int32 downloadFlag = 12;\n  optional int32 dangerEvel = 50;\n  optional int32 lifeTime = 51;\n  optional int32 uploadTime = 52;\n  optional int32 absFileType = 53;\n  optional int32 clientType = 54;\n  optional int32 expireTime = 55;\n  optional bytes pbReserve = 56;\n}\n\nmessage TransElem {\n  optional int32 elemType = 1;\n  optional bytes elemValue = 2;\n}\n\nmessage ExtraInfo {\n  optional bytes nick = 1;\n  optional bytes groupCard = 2;\n  optional int32 level = 3;\n  optional int32 flags = 4;\n  optional int32 groupMask = 5;\n  optional int32 msgTailId = 6;\n  optional bytes senderTitle = 7;\n  optional bytes apnsTips = 8;\n  optional int64 uin = 9;\n  optional int32 msgStateFlag = 10;\n  optional int32 apnsSoundType = 11;\n  optional int32 newGroupFlag = 12;\n}\n\nmessage GroupFile {\n  optional bytes filename = 1;\n  optional int64 fileSize = 2;\n  optional bytes fileId = 3;\n  optional bytes batchId = 4;\n  optional bytes fileKey = 5;\n  optional bytes mark = 6;\n  optional int64 sequence = 7;\n  optional bytes batchItemId = 8;\n  optional int32 feedMsgTime = 9;\n  optional bytes pbReserve = 10;\n}\n\nmessage AnonymousGroupMessage {\n  optional int32 flags = 1;\n  optional bytes anonId = 2;\n  optional bytes anonNick = 3;\n  optional int32 headPortrait = 4;\n  optional int32 expireTime = 5;\n  optional int32 bubbleId = 6;\n  optional bytes rankColor = 7;\n}\n\nmessage VideoFile {\n  optional bytes fileUuid = 1;\n  optional bytes fileMd5 = 2;\n  optional string fileName = 3;\n  optional int32 fileFormat = 4;\n  optional int32 fileTime = 5;\n  optional int32 fileSize = 6;\n  optional int32 thumbWidth = 7;\n  optional int32 thumbHeight = 8;\n  optional bytes thumbFileMd5 = 9;\n  optional bytes source = 10;\n  optional int32 thumbFileSize = 11;\n  optional int32 busiType = 12;\n  optional int32 fromChatType = 13;\n  optional int32 toChatType = 14;\n  optional bool boolSupportProgressive = 15;\n  optional int32 fileWidth = 16;\n  optional int32 fileHeight = 17;\n  optional int32 subBusiType = 18;\n  optional int32 videoAttr = 19;\n  repeated bytes bytesThumbFileUrls = 20;\n  repeated bytes bytesVideoFileUrls = 21;\n  optional int32 thumbDownloadFlag = 22;\n  optional int32 videoDownloadFlag = 23;\n  optional bytes pbReserve = 24;\n}\n\nmessage SourceMsg {\n  repeated int32 origSeqs = 1;\n  optional int64 senderUin = 2;\n  optional int32 time = 3;\n  optional int32 flag = 4;\n  repeated Elem elems = 5;\n  optional int32 type = 6;\n  optional bytes richMsg = 7;\n  optional bytes pbReserve = 8;\n  optional bytes srcMsg = 9;\n  optional int64 toUin = 10;\n  optional bytes troopName = 11;\n}\n\nmessage Face {\n  optional int32 index = 1;\n  optional bytes old = 2;\n  optional bytes buf = 11;\n}\n\nmessage LightApp {\n  optional bytes data = 1;\n  optional bytes msgResid = 2;\n}\n\nmessage CustomFace {\n  optional bytes guid = 1;\n  optional  string filePath = 2;\n  optional string shortcut = 3;\n  optional bytes buffer = 4;\n  optional bytes flag = 5;\n  optional bytes oldData = 6;\n  optional int32 fileId = 7;\n  optional uint32 serverIp = 8;\n  optional uint32 serverPort = 9;\n  optional int32 fileType = 10;\n  optional bytes signature = 11;\n  optional int32 useful = 12;\n  optional bytes md5 = 13;\n  optional string thumbUrl = 14;\n  optional string bigUrl = 15;\n  optional string origUrl = 16;\n  optional int32 bizType = 17;\n  optional int32 repeatIndex = 18;\n  optional int32 repeatImage = 19;\n  optional int32 imageType = 20;\n  optional int32 index = 21;\n  optional uint32 width = 22;\n  optional uint32 height = 23;\n  optional int32 source = 24;\n  optional uint32 size = 25;\n  optional int32 origin = 26;\n  optional int32 thumbWidth = 27;\n  optional int32 thumbHeight = 28;\n  optional int32 showLen = 29;\n  optional int32 downloadLen = 30;\n  optional string x400Url = 31;//x\n  optional int32 x400Width = 32;//x\n  optional int32 x400Height = 33;//x\n  optional bytes pbReserve = 34;\n}\n\nmessage ContentHead {\n  optional int32 pkgNum = 1;\n  optional int32 pkgIndex = 2;\n  optional int32 divSeq = 3;\n  optional int32 autoReply = 4;\n}\n\nmessage MessageHead {\n  optional int64 fromUin = 1;\n  optional int64 toUin = 2;\n  optional int32 msgType = 3;\n  optional int32 c2cCmd = 4;\n  optional int32 msgSeq = 5;\n  optional int32 msgTime = 6;\n  optional int64 msgUid = 7;\n  optional C2CTempMessageHead c2cTmpMsgHead = 8;\n  optional GroupInfo groupInfo = 9;\n  optional int32 fromAppid = 10;\n  optional int32 fromInstid = 11;\n  optional int32 userActive = 12;\n  optional DiscussInfo discussInfo = 13;\n  optional string fromNick = 14;\n  optional int64 authUin = 15;\n  optional string authNick = 16;\n  optional int32 msgFlag = 17;\n  optional string authRemark = 18;\n  optional string groupName = 19;\n  optional MutilTransHead mutiltransHead = 20;\n  optional InstCtrl msgInstCtrl = 21;\n  optional int32 publicAccountGroupSendFlag = 22;\n  optional int32 wseqInC2cMsghead = 23;\n  optional int64 cpid = 24;\n  optional ExtGroupKeyInfo extGroupKeyInfo = 25;\n  optional string multiCompatibleText = 26;\n  optional int32 authSex = 27;\n  optional bool isSrcMsg = 28;\n}\n\nmessage GroupInfo {\n  optional int64 groupCode = 1;\n  optional int32 groupType = 2;\n  optional int64 groupInfoSeq = 3;\n  optional bytes groupCard = 4;\n  optional bytes groupRank = 5;\n  optional int32 groupLevel = 6;\n  optional int32 groupCardType = 7;\n  optional bytes groupName = 8;\n}\n\nmessage DiscussInfo {\n  optional int64 discussUin = 1;\n  optional int32 discussType = 2;\n  optional int64 discussInfoSeq = 3;\n  optional bytes discussRemark = 4;\n  optional bytes discussName = 5;\n}\n\nmessage MutilTransHead{\n  optional int32 status = 1;\n  optional int32 msgId = 2;\n}\n\nmessage C2CTempMessageHead {\n  optional int32 c2cType = 1;\n  optional int32 serviceType = 2;\n  optional int64 groupUin = 3;\n  optional int64 groupCode = 4;\n  optional bytes sig = 5;\n  optional int32 sigType = 6;\n  optional string fromPhone = 7;\n  optional string toPhone = 8;\n  optional int32 lockDisplay = 9;\n  optional int32 directionFlag = 10;\n  optional bytes reserved = 11;\n}\n\nmessage InstCtrl {\n  repeated InstInfo msgSendToInst = 1;\n  repeated InstInfo msgExcludeInst = 2;\n  optional InstInfo msgFromInst = 3;\n}\n\nmessage InstInfo {\n  optional int32 apppid = 1;\n  optional int32 instid = 2;\n  optional int32 platform = 3;\n  optional int32 enumDeviceType = 10;\n}\n\nmessage ExtGroupKeyInfo {\n  optional int32 curMaxSeq = 1;\n  optional int64 curTime = 2;\n}\n\nmessage SyncCookie {\n  optional int64 time1 = 1;\n  optional int64 time = 2;\n  optional int64 ran1 = 3;\n  optional int64 ran2 = 4;\n  optional int64 const1 = 5;\n  optional int64 const2 = 11;\n  optional int64 const3 = 12;\n  optional int64 lastSyncTime = 13;\n  optional int64 const4 = 14;\n}\n\nmessage TransMsgInfo {\n  optional int64 fromUin = 1;\n  optional int64 toUin = 2;\n  optional int32 msgType = 3;\n  optional int32 msgSubtype = 4;\n  optional int32 msgSeq = 5;\n  optional int64 msgUid = 6;\n  optional int32 msgTime = 7;\n  optional int32 realMsgTime = 8;\n  optional string nickName = 9;\n  optional bytes msgData = 10;\n  optional int32 svrIp = 11;\n  optional ExtGroupKeyInfo extGroupKeyInfo = 12;\n  optional int32 generalFlag = 17;\n}\n\nmessage GeneralFlags {\n  optional int32 bubbleDiyTextId = 1;\n  optional int32 groupFlagNew = 2;\n  optional int64 uin = 3;\n  optional bytes rpId = 4;\n  optional int32 prpFold = 5;\n  optional int32 longTextFlag = 6;\n  optional string longTextResid = 7;\n  optional int32 groupType = 8;\n  optional int32 toUinFlag = 9;\n  optional int32 glamourLevel = 10;\n  optional int32 memberLevel = 11;\n  optional int64 groupRankSeq = 12;\n  optional int32 olympicTorch = 13;\n  optional bytes babyqGuideMsgCookie = 14;\n  optional int32 uin32ExpertFlag = 15;\n  optional int32 bubbleSubId = 16;\n  optional int64 pendantId = 17;\n  optional bytes rpIndex = 18;\n  optional bytes pbReserve = 19;\n}\n\n\nmessage PbMultiMsgItem {\n  optional string fileName = 1;\n  optional PbMultiMsgNew buffer = 2;\n}\nmessage PbMultiMsgNew {\n  repeated Message msg = 1;\n}\nmessage PbMultiMsgTransmit {\n  repeated Message msg = 1;\n  repeated PbMultiMsgItem pbItemList = 2;\n}\n\nmessage MsgElemInfo_servtype3 {\n  optional CustomFace flash_troop_pic = 1;\n  optional NotOnlineImage flash_c2c_pic = 2;\n}\n\nmessage MsgElemInfo_servtype33 {\n  optional uint32 index = 1;\n  optional bytes text = 2;\n  optional bytes compat = 3;\n  optional bytes buf = 4;\n}\n\nmessage SubMsgType0x4Body {\n  optional NotOnlineFile notOnlineFile = 1;\n  optional uint32 msgTime = 2;\n  optional uint32 onlineFileForPolyToOffline = 3;\n  // fileImageInfo\n}\n\nenum SyncFlag {\n  START = 0;\n  CONTINUME = 1;\n  STOP = 2;\n}\n\nmessage ResvAttr {\n  optional uint32 imageBizType = 1;\n  optional AnimationImageShow image_show = 7;\n}\n\nmessage AnimationImageShow {\n  optional int32 effect_id = 1;\n  optional bytes animation_param = 2;\n}\n\nmessage UinTypeUserDef {\n  optional int32 fromUinType = 1;\n  optional int64 fromGroupCode = 2;\n  optional string fileUuid = 3;\n}\n\nmessage GetGroupMsgReq {\n  optional uint64 groupCode = 1;\n  optional uint64 beginSeq = 2;\n  optional uint64 endSeq = 3;\n  optional uint32 filter = 4;\n  optional uint64 memberSeq = 5;\n  optional bool publicGroup = 6;\n  optional uint32 shieldFlag = 7;\n  optional uint32 saveTrafficFlag = 8;\n}\n\nmessage GetGroupMsgResp {\n  optional uint32 result = 1;\n  optional string errmsg = 2;\n  optional uint64 groupCode = 3;\n  optional uint64 returnBeginSeq = 4;\n  optional uint64 returnEndSeq = 5;\n  repeated Message msg = 6;\n}\n\nmessage PbGetOneDayRoamMsgReq {\n  optional uint64 peerUin = 1;\n  optional uint64 lastMsgTime = 2;\n  optional uint64 random = 3;\n  optional uint32 readCnt = 4;\n}\n\nmessage PbGetOneDayRoamMsgResp {\n  optional uint32 result = 1;\n  optional string errMsg = 2;\n  optional uint64 peerUin = 3;\n  optional uint64 lastMsgTime = 4;\n  optional uint64 random = 5;\n  repeated Message msg = 6;\n  optional uint32 isComplete = 7;\n}\n\nmessage PbPushMsg {\n  optional Message msg = 1;\n  optional int32 svrip = 2;\n  optional bytes pushToken = 3;\n  optional uint32 pingFlag = 4;\n  optional uint32 generalFlag = 9;\n  optional uint64 bindUin = 10;\n}\n\nmessage MsgElemInfo_servtype37 {\n  optional bytes packid = 1;\n  optional bytes stickerid = 2;\n  optional uint32 qsid = 3;\n  optional uint32 sourcetype = 4;\n  optional uint32 stickertype = 5;\n  optional bytes resultid = 6;\n  optional bytes text = 7;\n  optional bytes surpriseid = 8;\n  optional uint32 randomtype = 9;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/msg/objmsg.proto",
    "content": "syntax = \"proto3\";\n\npackage msg;\n\nmessage MsgPic {\n  bytes smallPicUrl = 1;\n  bytes originalPicUrl = 2;\n  int32 localPicId = 3;\n}\nmessage ObjMsg {\n  int32 msgType = 1;\n  bytes title = 2;\n  bytes bytesAbstact = 3;\n  bytes titleExt = 5;\n  repeated MsgPic msgPic = 6;\n  repeated MsgContentInfo msgContentInfo = 7;\n  int32 reportIdShow = 8;\n}\nmessage MsgContentInfo {\n  bytes contentInfoId = 1;\n  MsgFile msgFile = 2;\n}\nmessage MsgFile {\n  int32 busId = 1;\n  bytes filePath = 2;\n  int64 fileSize = 3;\n  string fileName = 4;\n  int64 int64DeadTime = 5;\n  bytes fileSha1 = 6;\n  bytes ext = 7;\n}\n   "
  },
  {
    "path": "ricq-guild/src/protocol/core/msg/report.proto",
    "content": "syntax = \"proto2\";\n\npackage msg;\n\nmessage PbMsgReadedReportReq {\n  repeated PbGroupReadedReportReq grpReadReport = 1;\n  repeated PbDiscussReadedReportReq disReadReport = 2;\n  optional PbC2CReadedReportReq c2CReadReport = 3;\n  //optional PbBindUinMsgReadedConfirmReq bindUinReadReport = 4;\n}\n\nmessage PbMsgReadedReportResp {\n  repeated PbGroupReadedReportResp grpReadReport = 1;\n  repeated PbDiscussReadedReportResp disReadReport = 2;\n  optional PbC2CReadedReportResp c2CReadReport = 3;\n  //optional PbBindUinMsgReadedConfirmResp bindUinReadReport = 4;\n}\n\nmessage PbGroupReadedReportReq {\n  optional uint64 groupCode = 1;\n  optional uint64 lastReadSeq = 2;\n}\n\nmessage PbDiscussReadedReportReq {\n  optional uint64 confUin = 1;\n  optional uint64 lastReadSeq = 2;\n}\n\nmessage PbC2CReadedReportReq {\n  optional bytes syncCookie = 1;\n  repeated UinPairReadInfo pairInfo = 2;\n}\n\nmessage UinPairReadInfo {\n  optional uint64 peerUin = 1;\n  optional uint32 lastReadTime = 2;\n  optional bytes crmSig = 3;\n  optional uint32 peerType = 4;\n  optional uint32 chatType = 5;\n  optional uint64 cpid = 6;\n  optional uint32 aioType = 7;\n  optional uint64 toTinyId = 9;\n}\n\nmessage PbGroupReadedReportResp {\n  optional uint32 result = 1;\n  optional string errmsg = 2;\n  optional uint64 groupCode = 3;\n  optional uint64 memberSeq = 4;\n  optional uint64 groupMsgSeq = 5;\n}\n\nmessage PbDiscussReadedReportResp {\n  optional uint32 result = 1;\n  optional string errmsg = 2;\n  optional uint64 confUin = 3;\n  optional uint64 memberSeq = 4;\n  optional uint64 confSeq = 5;\n}\n\nmessage PbC2CReadedReportResp {\n  optional uint32 result = 1;\n  optional string errmsg = 2;\n  optional bytes syncCookie = 3;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/msgtype0x210/subMsgType0x27.proto",
    "content": "syntax = \"proto2\";\n\npackage msgtype0x210;\n\nmessage AddGroup {\n  optional uint32 groupid = 1;\n  optional uint32 sortid = 2;\n  optional bytes groupname = 3;\n}\n\nmessage AppointmentNotify {\n  optional uint64 fromUin = 1;\n  optional string appointId = 2;\n  optional uint32 notifytype = 3;\n  optional string tipsContent = 4;\n  optional uint32 unreadCount = 5;\n  optional string joinWording = 6;\n  optional string viewWording = 7;\n  optional bytes sig = 8;\n  optional bytes eventInfo = 9;\n  optional bytes nearbyEventInfo = 10;\n  optional bytes feedEventInfo = 11;\n}\n\nmessage BinaryMsg {\n  optional uint32 opType = 1;\n  optional bytes opValue = 2;\n}\n\nmessage ChatMatchInfo {\n  optional bytes sig = 1;\n  optional uint64 uin = 2;\n  optional uint64 matchUin = 3;\n  optional bytes tipsWording = 4;\n  optional uint32 leftChatTime = 5;\n  optional uint64 timeStamp = 6;\n  optional uint32 matchExpiredTime = 7;\n  optional uint32 c2CExpiredTime = 8;\n  optional uint32 matchCount = 9;\n  optional bytes nick = 10;\n}\n\nmessage ConfMsgRoamFlag {\n  optional uint64 confid = 1;\n  optional uint32 flag = 2;\n  optional uint64 timestamp = 3;\n}\n\nmessage DaRenNotify {\n  optional uint64 uin = 1;\n  optional uint32 loginDays = 2;\n  optional uint32 days = 3;\n  optional uint32 isYestodayLogin = 4;\n  optional uint32 isTodayLogin = 5;\n}\n\nmessage DelFriend {\n  repeated uint64 uins = 1;\n}\n\nmessage DelGroup {\n  optional uint32 groupid = 1;\n}\n\nmessage FanpaiziNotify {\n  optional uint64 fromUin = 1;\n  optional string fromNick = 2;\n  optional bytes tipsContent = 3;\n  optional bytes sig = 4;\n}\n\nmessage ForwardBody {\n  optional uint32 notifyType = 1;\n  optional uint32 opType = 2;\n  optional AddGroup addGroup = 3;\n  optional DelGroup delGroup = 4;\n  optional ModGroupName modGroupName = 5;\n  optional ModGroupSort modGroupSort = 6;\n  optional ModFriendGroup modFriendGroup = 7;\n  optional ModProfile modProfile = 8;\n  optional ModFriendRemark modFriendRemark = 9;\n  optional ModLongNick modLongNick = 10;\n  optional ModCustomFace modCustomFace = 11;\n  optional ModGroupProfile modGroupProfile = 12;\n  optional ModGroupMemberProfile modGroupMemberProfile = 13;\n  optional DelFriend delFriend = 14;\n  optional ModFrdRoamPriv roamPriv = 15;\n  optional GrpMsgRoamFlag grpMsgRoamFlag = 16;\n  optional ConfMsgRoamFlag confMsgRoamFlag = 17;\n  optional ModLongNick modRichLongNick = 18;\n  optional BinaryMsg binPkg = 19;\n  optional ModSnsGeneralInfo modFriendRings = 20;\n  optional ModConfProfile modConfProfile = 21;\n  optional SnsUpdateFlag modFriendFlag = 22;\n  optional AppointmentNotify appointmentNotify = 23;\n  optional DaRenNotify darenNotify = 25;\n  optional NewComeinUserNotify newComeinUserNotify = 26;\n  optional PushSearchDev pushSearchDev = 200;\n  optional PushReportDev pushReportDev = 201;\n  optional QQPayPush qqPayPush = 202;\n  optional bytes redpointInfo = 203;\n  optional HotFriendNotify hotFriendNotify = 204;\n  optional PraiseRankNotify praiseRankNotify = 205;\n  optional MQQCampusNotify campusNotify = 210;\n  optional ModLongNick modRichLongNickEx = 211;\n  optional ChatMatchInfo chatMatchInfo = 212;\n  optional FrdCustomOnlineStatusChange frdCustomOnlineStatusChange = 214;\n  optional FanpaiziNotify fanpanziNotify = 2000;\n}\n\nmessage FrdCustomOnlineStatusChange {\n  optional uint64 uin = 1;\n}\n\nmessage FriendGroup {\n  optional uint64 fuin = 1;\n  repeated uint32 oldGroupId = 2;\n  repeated uint32 newGroupId = 3;\n}\n\nmessage FriendRemark {\n  optional uint32 type = 1;\n  optional uint64 fuin = 2;\n  optional bytes rmkName = 3;\n  optional uint64 groupCode = 4;\n}\n\nmessage GPS {\n  optional int32 lat = 1;\n  optional int32 lon = 2;\n  optional int32 alt = 3;\n  optional int32 type = 4;\n}\n\nmessage GroupMemberProfileInfo {\n  optional uint32 field = 1;\n  optional bytes value = 2;\n}\n\nmessage GroupProfileInfo {\n  optional uint32 field = 1;\n  optional bytes value = 2;\n}\n\nmessage GroupSort {\n  optional uint32 groupid = 1;\n  optional uint32 sortid = 2;\n}\n\nmessage GrpMsgRoamFlag {\n  optional uint64 groupcode = 1;\n  optional uint32 flag = 2;\n  optional uint64 timestamp = 3;\n}\n\nmessage HotFriendNotify {\n  optional uint64 dstUin = 1;\n  optional uint32 praiseHotLevel = 2;\n  optional uint32 chatHotLevel = 3;\n  optional uint32 praiseHotDays = 4;\n  optional uint32 chatHotDays = 5;\n  optional uint32 closeLevel = 6;\n  optional uint32 closeDays = 7;\n  optional uint32 praiseFlag = 8;\n  optional uint32 chatFlag = 9;\n  optional uint32 closeFlag = 10;\n  optional uint64 notifyTime = 11;\n  optional uint64 lastPraiseTime = 12;\n  optional uint64 lastChatTime = 13;\n  optional uint32 qzoneHotLevel = 14;\n  optional uint32 qzoneHotDays = 15;\n  optional uint32 qzoneFlag = 16;\n  optional uint64 lastQzoneTime = 17;\n}\n\nmessage MQQCampusNotify {\n  optional uint64 fromUin = 1;\n  optional string wording = 2;\n  optional string target = 3;\n  optional uint32 type = 4;\n  optional string source = 5;\n}\n\nmessage ModConfProfile {\n  optional uint64 uin = 1;\n  optional uint32 confUin = 2;\n  repeated ProfileInfo profileInfos = 3;\n}\n\nmessage ModCustomFace {\n  optional uint32 type = 1;\n  optional uint64 uin = 2;\n  optional uint64 groupCode = 3;\n  optional uint64 cmdUin = 4;\n}\n\nmessage ModFrdRoamPriv {\n  repeated OneRoamPriv roamPriv = 1;\n}\n\nmessage ModFriendGroup {\n  repeated FriendGroup frdGroup = 1;\n}\n\nmessage ModFriendRemark {\n  repeated FriendRemark frdRmk = 1;\n}\n\nmessage ModGroupMemberProfile {\n  optional uint64 groupUin = 1;\n  optional uint64 uin = 2;\n  repeated GroupMemberProfileInfo groupMemberProfileInfos = 3;\n  optional uint64 groupCode = 4;\n}\n\nmessage ModGroupName {\n  optional uint32 groupid = 1;\n  optional bytes groupname = 2;\n}\n\nmessage ModGroupProfile {\n  optional uint64 groupUin = 1;\n  repeated GroupProfileInfo groupProfileInfos = 2;\n  optional uint64 groupCode = 3;\n  optional uint64 cmdUin = 4;\n}\n\nmessage ModGroupSort {\n  repeated GroupSort groupsort = 1;\n}\n\nmessage ModLongNick {\n  optional uint64 uin = 1;\n  optional bytes value = 2;\n}\n\nmessage ModProfile {\n  optional uint64 uin = 1;\n  repeated ProfileInfo profileInfos = 2;\n}\n\nmessage ModSnsGeneralInfo {\n  repeated SnsUpateBuffer snsGeneralInfos = 1;\n}\n\nmessage SubMsg0x27Body {\n  repeated ForwardBody modInfos = 1;\n}\n\nmessage NewComeinUser {\n  optional uint64 uin = 1;\n  optional uint32 isFrd = 2;\n  optional bytes remark = 3;\n  optional bytes nick = 4;\n}\n\nmessage NewComeinUserNotify {\n  optional uint32 msgType = 1;\n  optional bool ongNotify = 2;\n  optional uint32 pushTime = 3;\n  optional NewComeinUser newComeinUser = 4;\n  optional NewGroup newGroup = 5;\n  optional NewGroupUser newGroupUser = 6;\n}\n\nmessage NewGroup {\n  optional uint64 groupCode = 1;\n  optional bytes groupName = 2;\n  optional uint64 ownerUin = 3;\n  optional bytes ownerNick = 4;\n  optional bytes distance = 5;\n}\n\nmessage NewGroupUser {\n  optional uint64 uin = 1;\n  optional int32 sex = 2;\n  optional int32 age = 3;\n  optional string nick = 4;\n  optional bytes distance = 5;\n}\n\nmessage OneRoamPriv {\n  optional uint64 fuin = 1;\n  optional uint32 privTag = 2;\n  optional uint32 privValue = 3;\n}\n\nmessage PraiseRankNotify {\n  optional uint32 isChampion = 11;\n  optional uint32 rankNum = 12;\n  optional string msg = 13;\n}\n\nmessage ProfileInfo {\n  optional uint32 field = 1;\n  optional bytes value = 2;\n}\n\nmessage PushReportDev {\n  optional uint32 msgType = 1;\n  optional bytes cookie = 4;\n  optional uint32 reportMaxNum = 5;\n  optional bytes sn = 6;\n}\n\nmessage PushSearchDev {\n  optional uint32 msgType = 1;\n  optional GPS gpsInfo = 2;\n  optional uint32 devTime = 3;\n  optional uint32 pushTime = 4;\n  optional uint64 din = 5;\n  optional string data = 6;\n}\n\nmessage QQPayPush {\n  optional uint64 uin = 1;\n  optional bool payOk = 2;\n}\n\nmessage SnsUpateBuffer {\n  optional uint64 uin = 1;\n  optional uint64 code = 2;\n  optional uint32 result = 3;\n  repeated SnsUpdateItem snsUpdateItem = 400;\n  repeated uint32 idlist = 401;\n}\n\nmessage SnsUpdateFlag {\n  repeated SnsUpdateOneFlag updateSnsFlag = 1;\n}\n\nmessage SnsUpdateItem {\n  optional uint32 updateSnsType = 1;\n  optional bytes value = 2;\n}\n\nmessage SnsUpdateOneFlag {\n  optional uint64 XUin = 1;\n  optional uint64 id = 2;\n  optional uint32 flag = 3;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/multimsg/multimsg.proto",
    "content": "syntax = \"proto3\";\n\npackage multimsg;\n\nmessage ExternMsg {\n  int32 channelType = 1;\n}\nmessage MultiMsgApplyDownReq {\n  bytes msgResid = 1;\n  int32 msgType = 2;\n  int64 srcUin = 3;\n}\nmessage MultiMsgApplyDownRsp {\n  int32 result = 1;\n  bytes thumbDownPara = 2;\n  bytes msgKey = 3;\n  repeated uint32 downIp = 4;\n  repeated uint32 downPort = 5;\n  bytes msgResid = 6;\n  ExternMsg msgExternInfo = 7;\n  repeated bytes bytesDownIpV6 = 8;\n  repeated int32 uint32DownV6Port = 9;\n}\nmessage MultiMsgApplyUpReq {\n  int64 dstUin = 1;\n  int64 msgSize = 2;\n  bytes msgMd5 = 3;\n  int32 msgType = 4;\n  int32 applyId = 5;\n}\nmessage MultiMsgApplyUpRsp {\n  int32 result = 1;\n  string msgResid = 2;\n  bytes msgUkey = 3;\n  repeated int32 uint32UpIp = 4;\n  repeated int32 uint32UpPort = 5;\n  int64 blockSize = 6;\n  int64 upOffset = 7;\n  int32 applyId = 8;\n  bytes msgKey = 9;\n  bytes msgSig = 10;\n  ExternMsg msgExternInfo = 11;\n  repeated bytes bytesUpIpV6 = 12;\n  repeated int32 uint32UpV6Port = 13;\n}\nmessage MultiReqBody {\n  int32 subcmd = 1;\n  int32 termType = 2;\n  int32 platformType = 3;\n  int32 netType = 4;\n  string buildVer = 5;\n  repeated MultiMsgApplyUpReq multimsgApplyupReq = 6;\n  repeated MultiMsgApplyDownReq multimsgApplydownReq = 7;\n  int32 buType = 8;\n  int32 reqChannelType = 9;\n}\nmessage MultiRspBody {\n  int32 subcmd = 1;\n  repeated MultiMsgApplyUpRsp multimsgApplyupRsp = 2;\n  repeated MultiMsgApplyDownRsp multimsgApplydownRsp = 3;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/notify/group0x857.proto",
    "content": "syntax = \"proto3\";\n\npackage notify;\n\nmessage NotifyMsgBody {\n  AIOGrayTipsInfo optMsgGrayTips = 5;\n  RedGrayTipsInfo optMsgRedTips = 9;\n  MessageRecallReminder optMsgRecall = 11;\n  GeneralGrayTipInfo optGeneralGrayTip = 26;\n  QQGroupDigestMsg qqGroupDigestMsg = 33;\n  int32 serviceType = 13;\n}\n\nmessage AIOGrayTipsInfo{\n  uint32 showLatest = 1;\n  bytes content = 2;\n  uint32 remind = 3;\n  bytes brief = 4;\n  uint64 receiverUin = 5;\n  uint32 reliaoAdminOpt = 6;\n}\n\nmessage GeneralGrayTipInfo {\n  uint64 busiType = 1;\n  uint64 busiId = 2;\n  uint32 ctrlFlag = 3;\n  uint32 c2cType = 4;\n  uint32 serviceType = 5;\n  uint64 templId = 6;\n  repeated TemplParam msgTemplParam = 7;\n  string content = 8;\n}\n\nmessage TemplParam {\n  string name = 1;\n  string value = 2;\n}\n\nmessage MessageRecallReminder {\n  int64 uin = 1;\n  bytes nickname = 2;\n  repeated RecalledMessageMeta recalledMsgList = 3;\n  bytes reminderContent = 4;\n  bytes userdef = 5;\n  int32 groupType = 6;\n  int32 opType = 7;\n}\n\nmessage RecalledMessageMeta {\n  int32 seq = 1;\n  int32 time = 2;\n  int32 msgRandom = 3;\n  int32 msgType = 4;\n  int32 msgFlag = 5;\n  int64 authorUin = 6;\n}\n\nmessage RedGrayTipsInfo {\n  uint32 showLatest = 1;\n  uint64 senderUin = 2;\n  uint64 receiverUin = 3;\n  string senderRichContent = 4;\n  string receiverRichContent = 5;\n  bytes authKey = 6;\n  sint32 msgType = 7;\n  uint32 luckyFlag = 8;\n  uint32 hideFlag = 9;\n  uint64 luckyUin = 12;\n}\n\nmessage QQGroupDigestMsg {\n  uint64 groupCode = 1;\n  uint32 seq = 2;\n  uint32 random = 3;\n  int32 opType = 4;\n  uint64 sender = 5;\n  uint64 digestOper = 6;\n  uint32 opTime = 7;\n  uint32 lastestMsgSeq = 8;\n  bytes operNick = 9;\n  bytes senderNick = 10;\n  int32 extInfo = 11;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage OIDBSSOPkg {\n  int32 command = 1;\n  int32 serviceType = 2;\n  int32 result = 3;\n  bytes bodybuffer = 4;\n  string errorMsg = 5;\n  string clientVersion = 6;\n}\n\nmessage D8A0RspBody {\n  int64 optUint64GroupCode = 1;\n  repeated D8A0KickResult msgKickResult = 2;\n}\nmessage D8A0KickResult {\n  int32 optUint32Result = 1;\n  int64 optUint64MemberUin = 2;\n}\nmessage D8A0KickMemberInfo {\n  int32 optUint32Operate = 1;\n  int64 optUint64MemberUin = 2;\n  int32 optUint32Flag = 3;\n  bytes optBytesMsg = 4;\n}\nmessage D8A0ReqBody {\n  int64 optUint64GroupCode = 1;\n  repeated D8A0KickMemberInfo msgKickList = 2;\n  repeated int64 kickList = 3;\n  int32 kickFlag = 4;\n  bytes kickMsg = 5;\n}\n\nmessage D89AReqBody {\n  int64 groupCode = 1;\n  D89AGroupinfo stGroupInfo = 2;\n  int64 originalOperatorUin = 3;\n  int32 reqGroupOpenAppid = 4;\n}\n\nmessage D89AGroupinfo {\n  int32 groupExtAdmNum = 1;\n  int32 flag = 2;\n  bytes ingGroupName = 3;\n  bytes ingGroupMemo = 4;\n  bytes ingGroupFingerMemo = 5;\n  bytes ingGroupAioSkinUrl = 6;\n  bytes ingGroupBoardSkinUrl = 7;\n  bytes ingGroupCoverSkinUrl = 8;\n  int32 groupGrade = 9;\n  int32 activeMemberNum = 10;\n  int32 certificationType = 11;\n  bytes ingCertificationText = 12;\n  bytes ingGroupRichFingerMemo = 13;\n  D89AGroupNewGuidelinesInfo stGroupNewguidelines = 14;\n  int32 groupFace = 15;\n  int32 addOption = 16;\n  oneof shutupTime {\n    int32 val = 17;\n  }\n  int32 groupTypeFlag = 18;\n  bytes stringGroupTag = 19;\n  D89AGroupGeoInfo msgGroupGeoInfo = 20;\n  int32 groupClassExt = 21;\n  bytes ingGroupClassText = 22;\n  int32 appPrivilegeFlag = 23;\n  int32 appPrivilegeMask = 24;\n  D89AGroupExInfoOnly stGroupExInfo = 25;\n  int32 groupSecLevel = 26;\n  int32 groupSecLevelInfo = 27;\n  int64 subscriptionUin = 28;\n  int32 allowMemberInvite = 29;\n  bytes ingGroupQuestion = 30;\n  bytes ingGroupAnswer = 31;\n  int32 groupFlagext3 = 32;\n  int32 groupFlagext3Mask = 33;\n  int32 groupOpenAppid = 34;\n  int32 noFingerOpenFlag = 35;\n  int32 noCodeFingerOpenFlag = 36;\n  int64 rootId = 37;\n  int32 msgLimitFrequency = 38;\n}\nmessage D89AGroupNewGuidelinesInfo {\n  bool boolEnabled = 1;\n  bytes ingContent = 2;\n}\nmessage D89AGroupExInfoOnly {\n  int32 tribeId = 1;\n  int32 moneyForAddGroup = 2;\n}\n\nmessage D89AGroupGeoInfo {\n  int32 cityId = 1;\n  int64 longtitude = 2;\n  int64 latitude = 3;\n  bytes ingGeoContent = 4;\n  int64 poiId = 5;\n}\n\nmessage DED3ReqBody {\n  int64 toUin = 1;\n  int64 groupCode = 2;\n  int32 msgSeq = 3;\n  int32 msgRand = 4;\n  int64 aioUin = 5;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0x6d6.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage DeleteFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string parentFolderId = 4;\n  optional string fileId = 5;\n}\nmessage DeleteFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n}\nmessage DownloadFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string fileId = 4;\n  optional bool boolThumbnailReq = 5;\n  optional int32 urlType = 6;\n  optional bool boolPreviewReq = 7;\n}\nmessage DownloadFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional string downloadIp = 4;\n  optional bytes downloadDns = 5;\n  optional bytes downloadUrl = 6;\n  optional bytes sha = 7;\n  optional bytes sha3 = 8;\n  optional bytes md5 = 9;\n  optional bytes cookieVal = 10;\n  optional string saveFileName = 11;\n  optional int32 previewPort = 12;\n}\nmessage MoveFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string fileId = 4;\n  optional string parentFolderId = 5;\n  optional string destFolderId = 6;\n}\nmessage MoveFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional string parentFolderId = 4;\n}\nmessage RenameFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string fileId = 4;\n  optional string parentFolderId = 5;\n  optional string newFileName = 6;\n}\nmessage RenameFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n}\nmessage D6D6ReqBody {\n  optional UploadFileReqBody uploadFileReq = 1;\n  optional ResendReqBody resendFileReq = 2;\n  optional DownloadFileReqBody downloadFileReq = 3;\n  optional DeleteFileReqBody deleteFileReq = 4;\n  optional RenameFileReqBody renameFileReq = 5;\n  optional MoveFileReqBody moveFileReq = 6;\n}\nmessage ResendReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional string fileId = 4;\n  optional bytes sha = 5;\n}\nmessage ResendRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional string uploadIp = 4;\n  optional bytes fileKey = 5;\n  optional bytes checkKey = 6;\n}\nmessage D6D6RspBody {\n  optional UploadFileRspBody uploadFileRsp = 1;\n  optional ResendRspBody resendFileRsp = 2;\n  optional DownloadFileRspBody downloadFileRsp = 3;\n  optional DeleteFileRspBody deleteFileRsp = 4;\n  optional RenameFileRspBody renameFileRsp = 5;\n  optional MoveFileRspBody moveFileRsp = 6;\n}\nmessage UploadFileReqBody {\n  optional int64 groupCode = 1;\n  optional int32 appId = 2;\n  optional int32 busId = 3;\n  optional int32 entrance = 4;\n  optional string parentFolderId = 5;\n  optional string fileName = 6;\n  optional string localPath = 7;\n  optional int64 int64FileSize = 8;\n  optional bytes sha = 9;\n  optional bytes sha3 = 10;\n  optional bytes md5 = 11;\n  optional bool supportMultiUpload = 15;\n}\nmessage UploadFileRspBody {\n  optional int32 retCode = 1;\n  optional string retMsg = 2;\n  optional string clientWording = 3;\n  optional string uploadIp = 4;\n  optional string serverDns = 5;\n  optional int32 busId = 6;\n  optional string fileId = 7;\n  optional bytes fileKey = 8;\n  optional bytes checkKey = 9;\n  optional bool boolFileExist = 10;\n  repeated string uploadIpLanV4 = 12;\n  repeated string uploadIpLanV6 = 13;\n  optional int32 uploadPort = 14;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0x758.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage InviteUinInfo {\n  optional uint64 uin = 1;\n  optional uint64 judgeGroupCode = 2;\n  optional uint64 judgeConfCode = 3;\n}\n\nmessage D758ReqBody {\n  optional uint64 joinGroupCode = 1;\n  repeated InviteUinInfo beInvitedUinInfo = 2;\n  optional string msg = 3;\n  optional uint32 mainSourceId = 4;\n  optional uint32 subSourceId = 5;\n  optional string verifyToken = 6;\n  optional uint32 verifyType = 7;\n}\n\nmessage D758RspBody {\n  optional uint64 groupCode = 1;\n  optional uint64 currentMaxMsgseq = 2;\n  optional string verifyUrl = 3;\n  optional uint32 verifyType = 4;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0x769.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage CPU {\n  optional string model = 1;\n  optional uint32 cores = 2;\n  optional uint32 frequency = 3;\n}\n\nmessage Camera {\n  optional uint64 primary = 1;\n  optional uint64 secondary = 2;\n  optional bool flash = 3;\n}\n\nmessage D769ConfigSeq {\n  optional uint32 type = 1;\n  optional uint32 version = 2;\n}\n\nmessage Content {\n  optional uint32 taskId = 1;\n  optional uint32 compress = 2;\n  optional bytes content = 10;\n}\n\nmessage D769DeviceInfo {\n  optional string brand = 1;\n  optional string model = 2;\n  optional C41219OS os = 3;\n  optional CPU cpu = 4;\n  optional Memory memory = 5;\n  optional Storage storage = 6;\n  optional Screen screen = 7;\n  optional Camera camera = 8;\n}\n\nmessage Memory {\n  optional uint64 total = 1;\n  optional uint64 process = 2;\n}\n\nmessage C41219OS {\n  optional uint32 type = 1;\n  optional string version = 2;\n  optional string sdk = 3;\n  optional string kernel = 4;\n  optional string rom = 5;\n}\n\nmessage QueryUinPackageUsageReq {\n  optional uint32 type = 1;\n  optional uint64 uinFileSize = 2;\n}\n\nmessage QueryUinPackageUsageRsp {\n  optional uint32 status = 1;\n  optional uint64 leftUinNum = 2;\n  optional uint64 maxUinNum = 3;\n  optional uint32 proportion = 4;\n  repeated UinPackageUsedInfo uinPackageUsedList = 10;\n}\n\nmessage D769ReqBody {\n  repeated D769ConfigSeq configList = 1;\n  optional D769DeviceInfo deviceInfo = 2;\n  optional string info = 3;\n  optional string province = 4;\n  optional string city = 5;\n  optional int32 reqDebugMsg = 6;\n  optional QueryUinPackageUsageReq queryUinPackageUsageReq = 101;\n}\n\nmessage D769RspBody {\n  optional uint32 result = 1;\n  repeated D769ConfigSeq configList = 2;\n  optional QueryUinPackageUsageRsp queryUinPackageUsageRsp = 101;\n}\n\nmessage Screen {\n  optional string model = 1;\n  optional uint32 width = 2;\n  optional uint32 height = 3;\n  optional uint32 dpi = 4;\n  optional bool multiTouch = 5;\n}\n\nmessage Storage {\n  optional uint64 builtin = 1;\n  optional uint64 external = 2;\n}\n\nmessage UinPackageUsedInfo {\n  optional uint32 ruleId = 1;\n  optional string author = 2;\n  optional string url = 3;\n  optional uint64 uinNum = 4;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0x88d.proto",
    "content": "syntax = \"proto2\"; // 似乎查询服务端是通过 exists flag 来返回 group info 的 这地方只能用 proto2\n\npackage oidb;\n\nmessage D88DGroupHeadPortraitInfo\n{\n  optional uint32 picId = 1;\n}\n\nmessage D88DGroupHeadPortrait\n{\n  optional uint32 picCount = 1;\n  repeated D88DGroupHeadPortraitInfo msgInfo = 2;\n  optional uint32 defaultId = 3;\n  optional uint32 verifyingPicCnt = 4;\n  repeated D88DGroupHeadPortraitInfo msgVerifyingPicInfo = 5;\n}\n\nmessage D88DGroupExInfoOnly\n{\n  optional uint32 tribeId = 1;\n  optional uint32 moneyForAddGroup = 2;\n};\n\nmessage D88DGroupInfo\n{\n  optional uint64 groupOwner = 1;\n  optional uint32 groupCreateTime = 2;\n  optional uint32 groupFlag = 3;\n  optional uint32 groupFlagExt = 4;\n  optional uint32 groupMemberMaxNum = 5;\n  optional uint32 groupMemberNum = 6;\n  optional uint32 groupOption = 7;\n  optional uint32 groupClassExt = 8;\n  optional uint32 groupSpecialClass = 9;\n  optional uint32 groupLevel = 10;\n  optional uint32 groupFace = 11;\n  optional uint32 groupDefaultPage = 12;\n  optional uint32 groupInfoSeq = 13;\n  optional uint32 groupRoamingTime = 14;\n  optional bytes  groupName = 15;\n  optional bytes  groupMemo = 16;\n  optional bytes  groupFingerMemo = 17;\n  optional bytes  groupClassText = 18;\n  repeated uint32 groupAllianceCode = 19;\n  optional uint32 groupExtraAadmNum = 20;\n  optional uint64 groupUin = 21;\n  optional uint32 groupCurMsgSeq = 22;\n  optional uint32 groupLastMsgTime = 23;\n  optional bytes  groupQuestion = 24;\n  optional bytes  groupAnswer = 25;\n  optional uint32 groupVisitorMaxNum = 26;\n  optional uint32 groupVisitorCurNum = 27;\n  optional uint32 levelNameSeq = 28;\n  optional uint32 groupAdminMaxNum = 29;\n  optional uint32 groupAioSkinTimestamp = 30;\n  optional uint32 groupBoardSkinTimestamp = 31;\n  optional bytes  groupAioSkinUrl = 32;\n  optional bytes  groupBoardSkinUrl = 33;\n  optional uint32 groupCoverSkinTimestamp = 34;\n  optional bytes  groupCoverSkinUrl = 35;\n  optional uint32 groupGrade = 36;\n  optional uint32 activeMemberNum = 37;\n  optional uint32 certificationType = 38;\n  optional bytes  certificationText = 39;\n  optional bytes  groupRichFingerMemo = 40;\n  repeated D88DTagRecord tagRecord = 41;\n  optional D88DGroupGeoInfo groupGeoInfo = 42;\n  optional uint32 headPortraitSeq = 43;\n  optional D88DGroupHeadPortrait msgHeadPortrait = 44;\n  optional uint32 shutupTimestamp = 45 ;\n  optional uint32 shutupTimestampMe = 46 ;\n  optional uint32 createSourceFlag = 47 ;\n  optional uint32 cmduinMsgSeq = 48;\n  optional uint32 cmduinJoinTime = 49;\n  optional uint32 cmduinUinFlag = 50;\n  optional uint32 cmduinFlagEx = 51;\n  optional uint32 cmduinNewMobileFlag = 52;\n  optional uint32 cmduinReadMsgSeq = 53;\n  optional uint32 cmduinLastMsgTime = 54;\n  optional uint32 groupTypeFlag = 55;\n  optional uint32 appPrivilegeFlag = 56;\n  optional D88DGroupExInfoOnly stGroupExInfo = 57;\n  optional uint32 groupSecLevel = 58;\n  optional uint32 groupSecLevelInfo = 59;\n  optional uint32 cmduinPrivilege = 60;\n  optional bytes  poidInfo = 61;\n  optional uint32 cmduinFlagEx2 = 62;\n  optional uint64 confUin = 63;\n  optional uint32 confMaxMsgSeq = 64;\n  optional uint32 confToGroupTime = 65;\n  optional uint32 passwordRedbagTime = 66;\n  optional uint64 subscriptionUin = 67;\n  optional uint32 memberListChangeSeq = 68;\n  optional uint32 membercardSeq = 69;\n  optional uint64 rootId = 70;\n  optional uint64 parentId = 71;\n  optional uint32 teamSeq = 72;\n  optional uint64 historyMsgBeginTime = 73;\n  optional uint64 inviteNoAuthNumLimit = 74;\n  optional uint32 cmduinHistoryMsgSeq = 75;\n  optional uint32 cmduinJoinMsgSeq = 76;\n  optional uint32 groupFlagext3 = 77;\n  optional uint32 groupOpenAppid = 78;\n  optional uint32 isConfGroup = 79;\n  optional uint32 isModifyConfGroupFace = 80;\n  optional uint32 isModifyConfGroupName = 81;\n  optional uint32 noFingerOpenFlag = 82;\n  optional uint32 noCodeFingerOpenFlag = 83;\n};\n\nmessage ReqGroupInfo\n{\n  optional uint64 groupCode = 1;\n  optional D88DGroupInfo stgroupinfo = 2;\n  optional uint32 lastGetGroupNameTime = 3;\n};\n\nmessage D88DReqBody\n{\n  optional uint32 appId = 1;\n  repeated ReqGroupInfo reqGroupInfo = 2;\n  optional uint32 pcClientVersion = 3;\n};\n\nmessage RspGroupInfo\n{\n  optional uint64 groupCode = 1;\n  optional uint32 result = 2;\n  optional D88DGroupInfo groupInfo = 3;\n};\n\nmessage D88DRspBody\n{\n  repeated RspGroupInfo rspGroupInfo = 1;\n  optional bytes  strErrorInfo = 2;\n};\n\nmessage D88DTagRecord\n{\n  optional uint64 fromUin = 1;\n  optional uint64 groupCode = 2;\n  optional bytes  tagId = 3;\n  optional uint64 setTime = 4;\n  optional uint32 goodNum = 5;\n  optional uint32 badNum = 6;\n  optional uint32 tagLen = 7;\n  optional bytes  tagValue = 8;\n};\n\nmessage D88DGroupGeoInfo\n{\n  optional uint64 owneruin = 1;\n  optional uint32 settime = 2;\n  optional uint32 cityid = 3;\n  optional int64 longitude = 4;\n  optional int64 latitude = 5;\n  optional bytes  geocontent = 6;\n  optional uint64 poiId = 7;\n};\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0x8a7.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage D8A7ReqBody {\n  optional uint32 subCmd = 1;\n  optional uint32 limitIntervalTypeForUin = 2;\n  optional uint32 limitIntervalTypeForGroup = 3;\n  optional uint64 uin = 4;\n  optional uint64 groupCode = 5;\n}\nmessage D8A7RspBody {\n  optional bool canAtAll = 1;\n  optional uint32 remainAtAllCountForUin = 2;\n  optional uint32 remainAtAllCountForGroup = 3;\n  optional bytes promptMsg1 = 4;\n  optional bytes promptMsg2 = 5;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0x8fc.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\nmessage D8FCReqBody {\n  optional int64 groupCode = 1;\n  optional int32 showFlag = 2;\n  repeated D8FCMemberInfo memLevelInfo = 3;\n  repeated D8FCLevelName levelName = 4;\n  optional int32 updateTime = 5;\n  optional int32 officeMode = 6;\n  optional int32 groupOpenAppid = 7;\n  optional D8FCClientInfo msgClientInfo = 8;\n  optional bytes authKey = 9;\n}\n\nmessage D8FCMemberInfo {\n  optional int64 uin = 1;\n  optional int32 point = 2;\n  optional int32 activeDay = 3;\n  optional int32 level = 4;\n  optional bytes specialTitle = 5;\n  optional int32 specialTitleExpireTime = 6;\n  optional bytes uinName = 7;\n  optional bytes memberCardName = 8;\n  optional bytes phone = 9;\n  optional bytes email = 10;\n  optional bytes remark = 11;\n  optional int32 gender = 12;\n  optional bytes job = 13;\n  optional int32 tribeLevel = 14;\n  optional int32 tribePoint = 15;\n  repeated D8FCCardNameElem richCardName = 16;\n  optional bytes commRichCardName = 17;\n}\n\nmessage D8FCCardNameElem {\n  optional int32 enumCardType = 1;\n  optional bytes value = 2;\n}\n\nmessage D8FCLevelName {\n  optional int32 level = 1;\n  optional string name = 2;\n}\n\nmessage D8FCClientInfo {\n  optional int32 implat = 1;\n  optional string ingClientver = 2;\n}\n\nmessage D8FCCommCardNameBuf {\n  repeated D8FCRichCardNameElem richCardName = 1;\n}\n\nmessage D8FCRichCardNameElem {\n  optional bytes ctrl = 1;\n  optional bytes text = 2;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0x990.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage TranslateReqBody {\n  // TranslateReq translate_req = 1;\n  BatchTranslateReq batch_translate_req = 2;\n}\n\nmessage TranslateRspBody {\n  // TranslateRsp translate_rsp = 1;\n  BatchTranslateRsp batch_translate_rsp = 2;\n}\n\nmessage BatchTranslateReq {\n  string src_language = 1;\n  string dst_language = 2;\n  repeated string src_text_list = 3;\n}\n\nmessage BatchTranslateRsp {\n  int32 error_code = 1;\n  bytes error_msg = 2;\n  string src_language = 3;\n  string dst_language = 4;\n  repeated string src_text_list = 5;\n  repeated string dst_text_list = 6;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0xb77.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage DB77ReqBody {\n  uint64 appId = 1;\n  uint32 appType = 2;\n  uint32 msgStyle = 3;\n  uint64 senderUin = 4;\n  DB77ClientInfo clientInfo = 5;\n  string textMsg = 6;\n  DB77ExtInfo extInfo = 7;\n  uint32 sendType = 10;\n  uint64 recvUin = 11;\n  DB77RichMsgBody richMsgBody = 12;\n  uint64 recvGuildId = 19;\n}\n\nmessage DB77ClientInfo {\n  uint32 platform = 1;\n  string sdkVersion = 2;\n  string androidPackageName = 3;\n  string androidSignature = 4;\n  string iosBundleId = 5;\n  string pcSign = 6;\n}\n\nmessage DB77ExtInfo {\n  repeated uint32 customFeatureId = 11;\n  string apnsWording = 12;\n  uint32 groupSaveDbFlag = 13;\n  uint32 receiverAppId = 14;\n  uint64 msgSeq = 15;\n}\n\nmessage DB77RichMsgBody {\n  string title = 10;\n  string summary  = 11;\n  string brief = 12;\n  string url = 13;\n  string pictureUrl = 14;\n  string action = 15;\n  string musicUrl = 16;\n  //ImageInfo imageInfo = 17;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0xe07.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\nmessage DE07ReqBody {\n  int32 version = 1;\n  int32 client = 2;\n  int32 entrance = 3;\n  OCRReqBody ocrReqBody = 10;\n}\n\nmessage OCRReqBody {\n  string imageUrl = 1;\n  string languageType = 2;\n  string scene = 3;\n  string originMd5 = 10;\n  string afterCompressMd5 = 11;\n  int32 afterCompressFileSize = 12;\n  int32 afterCompressWeight = 13;\n  int32 afterCompressHeight = 14;\n  bool isCut = 15;\n}\n\nmessage DE07RspBody {\n  int32 retCode = 1;\n  string errMsg = 2;\n  string wording = 3;\n  OCRRspBody ocrRspBody = 10;\n}\n\nmessage TextDetection {\n  string detectedText = 1;\n  int32 confidence = 2;\n  Polygon polygon = 3;\n  string advancedInfo = 4;\n}\n\nmessage Polygon {\n  repeated Coordinate coordinates = 1;\n}\n\nmessage Coordinate {\n  int32 X = 1;\n  int32 Y = 2;\n}\n\nmessage Language {\n  string language = 1;\n  string languageDesc = 2;\n}\n\nmessage OCRRspBody {\n  repeated TextDetection textDetections = 1;\n  string language = 2;\n  string requestId = 3;\n  repeated string ocrLanguageList = 101;\n  repeated string dstTranslateLanguageList = 102;\n  repeated Language languageList = 103;\n  int32 afterCompressWeight = 111;\n  int32 afterCompressHeight = 112;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0xeac.proto",
    "content": "syntax = \"proto3\";\n\npackage oidb;\n\n/*\nmessage ArkMsg {\n  optional string appName = 1;\n  optional string json = 2;\n}\n\nmessage BatchReqBody {\n  optional uint64 groupCode = 1;\n  repeated MsgInfo msgs = 2;\n}\n\nmessage BatchRspBody {\n  optional string wording = 1;\n  optional uint32 errorCode = 2;\n  optional int32 succCnt = 3;\n  repeated MsgProcessInfo procInfos = 4;\n  optional uint32 digestTime = 5;\n}\n\nmessage DigestMsg {\n  optional uint64 groupCode = 1;\n  optional uint32 seq = 2;\n  optional uint32 random = 3;\n  repeated MsgElem content = 4;\n  optional uint64 textSize = 5;\n  optional uint64 picSize = 6;\n  optional uint64 videoSize = 7;\n  optional uint64 senderUin = 8;\n  optional uint32 senderTime = 9;\n  optional uint64 addDigestUin = 10;\n  optional uint32 addDigestTime = 11;\n  optional uint32 startTime = 12;\n  optional uint32 latestMsgSeq = 13;\n  optional uint32 opType = 14;\n}\n\nmessage FaceMsg {\n  optional uint32 index = 1;\n  optional string text = 2;\n}\n\nmessage GroupFileMsg {\n  optional bytes fileName = 1;\n  optional uint32 busId = 2;\n  optional string fileId = 3;\n  optional uint64 fileSize = 4;\n  optional uint64 deadTime = 5;\n  optional bytes fileSha1 = 6;\n  optional bytes ext = 7;\n  optional bytes fileMd5 = 8;\n}\n\nmessage ImageMsg {\n  optional string md5 = 1;\n  optional string uuid = 2;\n  optional uint32 imgType = 3;\n  optional uint32 fileSize = 4;\n  optional uint32 width = 5;\n  optional uint32 height = 6;\n  optional uint32 fileId = 101;\n  optional uint32 serverIp = 102;\n  optional uint32 serverPort = 103;\n  optional string filePath = 104;\n  optional string thumbUrl = 201;\n  optional string originalUrl = 202;\n  optional string resaveUrl = 203;\n}\n\nmessage MsgElem {\n  optional uint32 type = 1;\n  optional TextMsg textMsg = 11;\n  optional FaceMsg faceMsg = 12;\n  optional ImageMsg imageMsg = 13;\n  optional GroupFileMsg groupFileMsg = 14;\n  optional ShareMsg shareMsg = 15;\n  optional RichMsg richMsg = 16;\n  optional ArkMsg arkMsg = 17;\n}\n\nmessage MsgInfo {\n  optional uint32 seq = 1;\n  optional uint32 random = 2;\n}\n\nmessage MsgProcessInfo {\n  optional MsgInfo msg = 1;\n  optional uint32 errorCode = 2;\n  optional uint64 digestUin = 3;\n  optional uint32 digestTime = 4;\n}\n*/\n\nmessage EACReqBody {\n  optional uint64 groupCode = 1;\n  optional uint32 seq = 2;\n  optional uint32 random = 3;\n}\n\n/*\nmessage RichMsg {\n  optional uint32 serviceId = 1;\n  optional string xml = 2;\n  optional string longMsgResid = 3;\n}\n*/\n\nmessage EACRspBody {\n  optional string wording = 1;\n  optional uint64 digestUin = 2;\n  optional uint32 digestTime = 3;\n  //optional DigestMsg msg = 4;\n  optional uint32 errorCode = 10;\n}\n\n/*\nmessage ShareMsg {\n  optional string type = 1;\n  optional string title = 2;\n  optional string summary = 3;\n  optional string brief = 4;\n  optional string url = 5;\n  optional string pictureUrl = 6;\n  optional string action = 7;\n  optional string source = 8;\n  optional string sourceUrl = 9;\n}\n\nmessage TextMsg {\n  optional bytes str = 1;\n}\n */"
  },
  {
    "path": "ricq-guild/src/protocol/core/oidb/oidb0xeb7.proto",
    "content": "syntax = \"proto2\";\n\npackage oidb;\n\n// DEB7 prefix\nmessage DEB7ReqBody {\n  optional StSignInStatusReq signInStatusReq = 1;\n  optional StSignInWriteReq signInWriteReq = 2;\n}\n\nmessage DEB7Ret {\n  optional uint32 code = 1;\n  optional string msg = 2;\n}\n\nmessage DEB7RspBody {\n  optional StSignInStatusRsp signInStatusRsp = 1;\n  optional StSignInWriteRsp signInWriteRsp = 2;\n}\n\nmessage SignInStatusBase {\n  optional uint32 status = 1;\n  optional int64 currentTimeStamp = 2;\n}\n\nmessage SignInStatusDoneInfo {\n  optional string leftTitleWrod = 1;\n  optional string rightDescWord = 2;\n  repeated string belowPortraitWords = 3;\n  optional string recordUrl = 4;\n}\n\nmessage SignInStatusGroupScore {\n  optional string groupScoreWord = 1;\n  optional string scoreUrl = 2;\n}\n\nmessage SignInStatusNotInfo {\n  optional string buttonWord = 1;\n  optional string signDescWordLeft = 2;\n  optional string signDescWordRight = 3;\n}\n\nmessage SignInStatusYesterdayFirst {\n  optional string yesterdayFirstUid = 1;\n  optional string yesterdayWord = 2;\n  optional string yesterdayNick = 3;\n}\n\nmessage StDaySignedInfo {\n  optional string uid = 1;\n  optional string uidGroupNick = 2;\n  optional int64 signedTimeStamp = 3;\n  optional int32 signInRank = 4;\n}\n\nmessage StDaySignedListReq {\n  optional string dayYmd = 1;\n  optional string uid = 2;\n  optional string groupId = 3;\n  optional int32 offset = 4;\n  optional int32 limit = 5;\n}\n\nmessage StDaySignedListRsp {\n  optional DEB7Ret ret = 1;\n  repeated StDaySignedPage page = 2;\n}\n\nmessage StDaySignedPage {\n  repeated StDaySignedInfo infos = 1;\n  optional int32 offset = 2;\n  optional int32 total = 3;\n}\n\nmessage StKingSignedInfo {\n  optional string uid = 1;\n  optional string groupNick = 2;\n  optional int64 signedTimeStamp = 3;\n  optional int32 signedCount = 4;\n}\n\nmessage StKingSignedListReq {\n  optional string uid = 1;\n  optional string groupId = 2;\n}\n\nmessage StKingSignedListRsp {\n  optional DEB7Ret ret = 1;\n  optional StKingSignedInfo yesterdayFirst = 2;\n  repeated StKingSignedInfo topSignedTotal = 3;\n  repeated StKingSignedInfo topSignedContinue = 4;\n}\n\nmessage StSignInRecordDaySigned {\n  optional float daySignedRatio = 1;\n  optional int32 dayTotalSignedUid = 2;\n  optional StDaySignedPage daySignedPage = 3;\n  optional string daySignedUrl = 4;\n}\n\nmessage StSignInRecordKing {\n  optional StKingSignedInfo yesterdayFirst = 1;\n  repeated StKingSignedInfo topSignedTotal = 2;\n  repeated StKingSignedInfo topSignedContinue = 3;\n  optional string kingUrl = 4;\n}\n\nmessage StSignInRecordReq {\n  optional string dayYmd = 1;\n  optional string uid = 2;\n  optional string groupId = 3;\n}\n\nmessage StSignInRecordRsp {\n  optional DEB7Ret ret = 1;\n  optional SignInStatusBase base = 2;\n  optional StSignInRecordUser userRecord = 3;\n  optional StSignInRecordDaySigned daySigned = 4;\n  optional StSignInRecordKing kingRecord = 5;\n  optional StViewGroupLevel level = 6;\n}\n\nmessage StSignInRecordUser {\n  optional int32 totalSignedDays = 2;\n  optional int64 earliestSignedTimeStamp = 3;\n  optional int64 continueSignedDays = 4;\n  repeated string historySignedDays = 5;\n  optional string groupName = 6;\n}\n\nmessage StSignInStatusReq {\n  optional string uid = 1;\n  optional string groupId = 2;\n  optional uint32 scene = 3;\n  optional string clientVersion = 4;\n}\n\nmessage StSignInStatusRsp {\n  optional DEB7Ret ret = 1;\n  optional SignInStatusBase base = 2;\n  optional SignInStatusYesterdayFirst yesterday = 3;\n  optional SignInStatusNotInfo notInfo = 4;\n  optional SignInStatusDoneInfo doneInfo = 5;\n  optional SignInStatusGroupScore groupScore = 6;\n  optional string mantleUrl = 7;\n  optional string backgroundUrl = 8;\n}\n\nmessage StSignInWriteReq {\n  optional string uid = 1;\n  optional string groupId = 2;\n  optional string clientVersion = 3;\n}\n\nmessage StSignInWriteRsp {\n  optional DEB7Ret ret = 1;\n  optional SignInStatusDoneInfo doneInfo = 2;\n  optional SignInStatusGroupScore groupScore = 3;\n}\n\nmessage StViewGroupLevel {\n  optional string title = 1;\n  optional string url = 2;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/online_status/OnlineStatusExtInfo.java.proto",
    "content": "syntax = \"proto2\";\n\npackage online_status;\n\nmessage AutoStateBizInfo {\n  optional uint64 updateTime = 1;\n}\n\nmessage CustomStatus {\n  optional uint64 faceIndex = 1;\n  optional string wording = 2;\n  optional uint64 faceType = 3;\n}\n\nmessage WeatherBizInfo {\n  optional string weatherType = 1;\n  optional string weatherTypeId = 2;\n  optional uint32 adcode = 3;\n  optional uint64 updateTime = 4;\n  optional string city = 5;\n  optional string area = 6;\n  optional string temper = 7;\n  optional uint32 flag = 8;\n  optional string weatherDesc = 9;\n}\n\nmessage ZodiacBizInfo {\n  optional string todayTrend = 1;\n  optional string tomorrowTrend = 2;\n  optional string miniapp = 3;\n  optional string todayDate = 4;\n  optional string luckyColor = 5;\n  optional string luckyNumber = 6;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/profilecard/busi.proto",
    "content": "syntax = \"proto2\";\npackage profilecard;\n\nmessage BusiColor {\n  optional int32 r = 1;\n  optional int32 g = 2;\n  optional int32 b = 3;\n}\n\nmessage BusiComm {\n  optional int32 ver = 1;\n  optional int32 seq = 2;\n  optional int64 fromuin = 3;\n  optional int64 touin = 4;\n  optional int32 service = 5;\n  optional int32 sessionType = 6;\n  optional bytes sessionKey = 7;\n  optional int32 clientIp = 8;\n  optional BusiUi display = 9;\n  optional int32 result = 10;\n  optional string errMsg = 11;\n  optional int32 platform = 12;\n  optional string qqver = 13;\n  optional int32 build = 14;\n  optional BusiLoginSig msgLoginSig = 15;\n  optional int32 version = 17;\n  optional BusiUinInfo msgUinInfo = 18;\n  optional BusiRichUi msgRichDisplay = 19;\n}\nmessage BusiCommonReq {\n  optional string serviceCmd = 1;\n  optional BusiVisitorCountReq vcReq = 2;\n  optional BusiHideRecordsReq hrReq = 3;\n}\nmessage BusiDetailRecord {\n  optional int32 fuin = 1;\n  optional int32 source = 2;\n  optional int32 vtime = 3;\n  optional int32 mod = 4;\n  optional int32 hideFlag = 5;\n}\nmessage BusiHideRecordsReq {\n  optional int32 huin = 1;\n  optional int32 fuin = 2;\n  repeated BusiDetailRecord records = 3;\n}\nmessage BusiLabel {\n  optional bytes name = 1;\n  optional int32 enumType = 2;\n  optional BusiColor textColor = 3;\n  optional BusiColor edgingColor = 4;\n  optional int32 labelAttr = 5;\n  optional int32 labelType = 6;\n}\nmessage BusiLoginSig {\n  optional int32 type = 1;\n  optional bytes sig = 2;\n  optional int32 appid = 3;\n}\nmessage BusiRichUi {\n  optional string name = 1;\n  optional string serviceUrl = 2;\n  //repeated UiInfo uiList = 3;\n}\nmessage BusiUi {\n  optional string url = 1;\n  optional string title = 2;\n  optional string content = 3;\n  optional string jumpUrl = 4;\n}\n\nmessage BusiUinInfo {\n  optional int64 int64Longitude = 1;\n  optional int64 int64Latitude = 2;\n}\nmessage BusiVisitorCountReq {\n  optional int32 requireuin = 1;\n  optional int32 operuin = 2;\n  optional int32 mod = 3;\n  optional int32 reportFlag = 4;\n}\nmessage BusiVisitorCountRsp {\n  optional int32 requireuin = 1;\n  optional int32 totalLike = 2;\n  optional int32 totalView = 3;\n  optional int32 hotValue = 4;\n  optional int32 redValue = 5;\n  optional int32 hotDiff = 6;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/profilecard/gate.proto",
    "content": "syntax = \"proto2\";\npackage profilecard;\n\nmessage GateCommTaskInfo {\n  optional int32 appid = 1;\n  optional bytes taskData = 2;\n}\nmessage GateGetGiftListReq {\n  optional int32 uin = 1;\n}\nmessage GateGetGiftListRsp {\n  repeated string giftUrl = 1;\n  optional string customUrl = 2;\n  optional string desc = 3;\n  optional bool isOn = 4;\n}\nmessage GateGetVipCareReq {\n  optional int64 uin = 1;\n}\nmessage GateGetVipCareRsp {\n  optional int32 buss = 1;\n  optional int32 notice = 2;\n}\nmessage GateOidbFlagInfo {\n  optional int32 fieled = 1;\n  optional bytes byetsValue = 2;\n}\nmessage GatePrivilegeBaseInfoReq {\n  optional int64 uReqUin = 1;\n}\nmessage GatePrivilegeBaseInfoRsp {\n  optional bytes msg = 1;\n  optional bytes jumpUrl = 2;\n  repeated GatePrivilegeInfo vOpenPriv = 3;\n  repeated GatePrivilegeInfo vClosePriv = 4;\n  optional int32 uIsGrayUsr = 5;\n}\nmessage GatePrivilegeInfo {\n  optional int32 iType = 1;\n  optional int32 iSort = 2;\n  optional int32 iFeeType = 3;\n  optional int32 iLevel = 4;\n  optional int32 iFlag = 5;\n  optional bytes iconUrl = 6;\n  optional bytes deluxeIconUrl = 7;\n  optional bytes jumpUrl = 8;\n  optional int32 iIsBig = 9;\n}\nmessage GateVaProfileGateReq {\n  optional int32 uCmd = 1;\n  optional GatePrivilegeBaseInfoReq stPrivilegeReq = 2;\n  optional GateGetGiftListReq stGiftReq = 3;\n  repeated GateCommTaskInfo taskItem = 4;\n  repeated GateOidbFlagInfo oidbFlag = 5;\n  optional GateGetVipCareReq stVipCare = 6;\n}\n\nmessage GateQidInfoItem {\n  optional string qid = 1;\n  optional string url = 2;\n  optional string color = 3;\n  optional string logoUrl = 4;\n}\n\nmessage GateVaProfileGateRsp {\n  optional int32 iRetCode = 1;\n  optional bytes sRetMsg = 2;\n  optional GatePrivilegeBaseInfoRsp stPrivilegeRsp = 3;\n  optional GateGetGiftListRsp stGiftRsp = 4;\n  repeated GateCommTaskInfo taskItem = 5;\n  repeated GateOidbFlagInfo oidbFlag = 6;\n  optional GateGetVipCareRsp stVipCare = 7;\n  optional GateQidInfoItem qidInfo = 9;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/short_video/short_video.proto",
    "content": "syntax = \"proto3\";\npackage short_video;\n\nmessage ShortVideoReqBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  ShortVideoUploadReq pttShortVideoUploadReq = 3;\n  ShortVideoDownloadReq pttShortVideoDownloadReq = 4;\n  repeated ShortVideoExtensionReq extensionReq = 100;\n}\n\nmessage ShortVideoRspBody {\n  int32 cmd = 1;\n  int32 seq = 2;\n  ShortVideoUploadRsp pttShortVideoUploadRsp = 3;\n  ShortVideoDownloadRsp pttShortVideoDownloadRsp = 4;\n}\n\nmessage ShortVideoUploadReq {\n  int64 fromUin = 1;\n  int64 toUin = 2;\n  int32 chatType = 3;\n  int32 clientType = 4;\n  ShortVideoFileInfo info = 5;\n  int64 groupCode = 6;\n  int32 agentType = 7;\n  int32 businessType = 8;\n  int32 supportLargeSize = 20;\n}\nmessage ShortVideoDownloadReq {\n  int64 fromUin = 1;\n  int64 toUin = 2;\n  int32 chatType = 3;\n  int32 clientType = 4;\n  string fileId = 5;\n  int64 groupCode = 6;\n  int32 agentType = 7;\n  bytes fileMd5 = 8;\n  int32 businessType = 9;\n  int32 fileType = 10;\n  int32 downType = 11;\n  int32 sceneType = 12;\n}\n\nmessage ShortVideoDownloadRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n  repeated ShortVideoIpList sameAreaOutAddr = 3;\n  repeated ShortVideoIpList diffAreaOutAddr = 4;\n  bytes downloadKey = 5;\n  bytes fileMd5 = 6;\n  repeated ShortVideoIpList sameAreaInnerAddr = 7;\n  repeated ShortVideoIpList diffAreaInnerAddr = 8;\n  ShortVideoAddr downloadAddr = 9;\n  bytes encryptKey = 10;\n}\n\nmessage ShortVideoUploadRsp {\n  int32 retCode = 1;\n  string retMsg = 2;\n  repeated ShortVideoIpList sameAreaOutAddr = 3;\n  repeated ShortVideoIpList diffAreaOutAddr = 4;\n  bytes fileId = 5;\n  bytes uKey = 6;\n  int32 fileExists = 7;\n  repeated ShortVideoIpList sameAreaInnerAddr = 8;\n  repeated ShortVideoIpList diffAreaInnerAddr = 9;\n  repeated DataHole dataHole = 10;\n}\n\nmessage ShortVideoFileInfo {\n  string fileName = 1;\n  bytes fileMd5 = 2;\n  bytes thumbFileMd5 = 3;\n  int64 fileSize = 4;\n  int32 fileResLength = 5;\n  int32 fileResWidth = 6;\n  int32 fileFormat = 7;\n  int32 fileTime = 8;\n  int64 thumbFileSize = 9;\n}\n\nmessage DataHole {\n  int64 begin = 1;\n  int64 end = 2;\n}\n\nmessage ShortVideoIpList {\n  int32 ip = 1;\n  int32 port = 2;\n}\n\nmessage ShortVideoAddr {\n  repeated string host = 10;\n  string urlArgs = 11;\n  //repeated string domain = 13;\n}\n\nmessage ShortVideoExtensionReq {\n  int32 subBusiType = 1;\n  int32 userCnt = 2;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/core/sig_act/sig_act.proto",
    "content": "syntax = \"proto2\";\npackage sig_act;\n\nmessage Platform {\n  optional int64 platform = 1;\n  optional string osver = 2;\n  optional string mqqver = 3;\n}\n\nmessage ReqBody {\n  optional uint32 cmd = 1;\n  optional uint64 seq = 2;\n  optional Platform plf = 3;\n  optional SigactReq req = 4;\n  optional SigauthReq authReq = 5;\n  optional uint32 source = 6;\n}\n\nmessage RspBody {\n  optional int32 ret = 1;\n  optional string desc = 2;\n  optional uint32 cmd = 3;\n  optional uint64 seq = 4;\n  optional SigactRsp rsp = 5;\n  optional SigauthRsp authRsp = 6;\n}\n\nmessage SigactReq {\n  optional uint64 uinDisable = 1;\n  optional int32 actid = 2;\n  optional int32 acttype = 3;\n}\n\nmessage SigactRsp {\n  optional uint64 uin = 1;\n  optional uint32 rank = 2;\n}\n\nmessage SigauthReq {\n  optional uint64 uinDisable = 1;\n  optional int32 itemid = 2;\n  optional int32 len = 3;\n  optional bytes data = 4;\n  optional int32 fontid = 5;\n}\n\nmessage SigauthRsp {\n  optional bytes result = 1;\n  optional string url = 2;\n  optional TipsInfo tipsInfo = 3;\n  optional int32 authfailedAppid = 4;\n\n  message TipsInfo {\n    optional bool valid = 1;\n    optional int32 ret = 2;\n    optional uint32 type = 3;\n    optional string titleWording = 4;\n    optional string wording = 5;\n    optional string rightBtnWording = 6;\n    optional string leftBtnWording = 7;\n    optional string vipType = 8;\n    optional uint32 vipMonth = 9;\n    optional string url = 10;\n  }\n}"
  },
  {
    "path": "ricq-guild/src/protocol/core/structmsg/structmsg.proto",
    "content": "syntax = \"proto3\";\n\n//option go_package = \"./;structmsg\";\npackage structmsg;\n\nmessage AddFrdSNInfo {\n  int32 notSeeDynamic = 1;\n  int32 setSn = 2;\n}\n\nmessage FlagInfo {\n  int32 grpMsgKickAdmin = 1;\n  int32 grpMsgHiddenGrp = 2;\n  int32 grpMsgWordingDown = 3;\n  int32 frdMsgGetBusiCard = 4;\n  int32 grpMsgGetOfficialAccount = 5;\n  int32 grpMsgGetPayInGroup = 6;\n  int32 frdMsgDiscuss2ManyChat = 7;\n  int32 grpMsgNotAllowJoinGrpInviteNotFrd = 8;\n  int32 frdMsgNeedWaitingMsg = 9;\n  int32 frdMsgUint32NeedAllUnreadMsg = 10;\n  int32 grpMsgNeedAutoAdminWording = 11;\n  int32 grpMsgGetTransferGroupMsgFlag = 12;\n  int32 grpMsgGetQuitPayGroupMsgFlag = 13;\n  int32 grpMsgSupportInviteAutoJoin = 14;\n  int32 grpMsgMaskInviteAutoJoin = 15;\n  int32 grpMsgGetDisbandedByAdmin = 16;\n  int32 grpMsgGetC2cInviteJoinGroup = 17;\n}\n\nmessage FriendInfo {\n  string msgJointFriend = 1;\n  string msgBlacklist = 2;\n}\n\nmessage SGroupInfo {\n  int32 groupAuthType = 1;\n  int32 displayAction = 2;\n  string msgAlert = 3;\n  string msgDetailAlert = 4;\n  string msgOtherAdminDone = 5;\n  int32 appPrivilegeFlag = 6;\n}\n\nmessage MsgInviteExt {\n  int32 srcType = 1;\n  int64 srcCode = 2;\n  int32 waitState = 3;\n}\n\nmessage MsgPayGroupExt {\n  int64 joinGrpTime = 1;\n  int64 quitGrpTime = 2;\n}\n\nmessage ReqNextSystemMsg {\n  int32 msgNum = 1;\n  int64 followingFriendSeq = 2;\n  int64 followingGroupSeq = 3;\n  int32  checktype = 4;\n  FlagInfo flag = 5;\n  int32 language = 6;\n  int32 version = 7;\n  int64 friendMsgTypeFlag = 8;\n}\n\nmessage ReqSystemMsg {\n  int32 msgNum = 1;\n  int64 latestFriendSeq = 2;\n  int64 latestGroupSeq = 3;\n  int32 version = 4;\n  int32 language = 5;\n}\n\nmessage ReqSystemMsgAction {\n  int32  msgType = 1;\n  int64 msgSeq = 2;\n  int64 reqUin = 3;\n  int32 subType = 4;\n  int32 srcId = 5;\n  int32 subSrcId = 6;\n  int32 groupMsgType = 7;\n  SystemMsgActionInfo actionInfo = 8;\n  int32 language = 9;\n}\n\nmessage ReqSystemMsgNew {\n  int32 msgNum = 1;\n  int64 latestFriendSeq = 2;\n  int64 latestGroupSeq = 3;\n  int32 version = 4;\n  int32 checktype = 5;\n  FlagInfo flag = 6;\n  int32 language = 7;\n  bool isGetFrdRibbon = 8;\n  bool isGetGrpRibbon = 9;\n  int64 friendMsgTypeFlag = 10;\n  int32 reqMsgType = 11;\n}\nmessage ReqSystemMsgRead {\n  int64 latestFriendSeq = 1;\n  int64 latestGroupSeq = 2;\n  int32 type = 3;\n  int32 checktype = 4;\n}\nmessage RspHead {\n  int32 result = 1;\n  string msgFail = 2;\n}\nmessage RspNextSystemMsg {\n  RspHead head = 1;\n  repeated StructMsg msgs = 2;\n  int64 followingFriendSeq = 3;\n  int64 followingGroupSeq = 4;\n  int32  checktype = 5;\n  string gameNick = 100;\n  bytes undecidForQim = 101;\n  int32 unReadCount3 = 102;\n}\nmessage RspSystemMsg {\n  RspHead head = 1;\n  repeated StructMsg msgs = 2;\n  int32 unreadCount = 3;\n  int64 latestFriendSeq = 4;\n  int64 latestGroupSeq = 5;\n  int64 followingFriendSeq = 6;\n  int64 followingGroupSeq = 7;\n  string msgDisplay = 8;\n}\n\nmessage RspSystemMsgAction {\n  RspHead head = 1;\n  string msgDetail = 2;\n  int32 type = 3;\n  string msgInvalidDecided = 5;\n  int32 remarkResult = 6;\n}\nmessage RspSystemMsgNew {\n  RspHead head = 1;\n  int32 unreadFriendCount = 2;\n  int32 unreadGroupCount = 3;\n  int64 latestFriendSeq = 4;\n  int64 latestGroupSeq = 5;\n  int64 followingFriendSeq = 6;\n  int64 followingGroupSeq = 7;\n  repeated StructMsg friendmsgs = 9;\n  repeated StructMsg groupmsgs = 10;\n  StructMsg msgRibbonFriend = 11;\n  StructMsg msgRibbonGroup = 12;\n  string msgDisplay = 13;\n  string grpMsgDisplay = 14;\n  int32 over = 15;\n  int32  checktype = 20;\n  string gameNick = 100;\n  bytes undecidForQim = 101;\n  int32 unReadCount3 = 102;\n}\nmessage RspSystemMsgRead {\n  RspHead head = 1;\n  int32 type = 2;\n  int32  checktype = 3;\n}\nmessage StructMsg {\n  int32 version = 1;\n  int32  msgType = 2;\n  int64 msgSeq = 3;\n  int64 msgTime = 4;\n  int64 reqUin = 5;\n  int32 unreadFlag = 6;\n  SystemMsg msg = 50;\n}\nmessage SystemMsg {\n  int32 subType = 1;\n  string msgTitle = 2;\n  string msgDescribe = 3;\n  string msgAdditional = 4;\n  string msgSource = 5;\n  string msgDecided = 6;\n  int32 srcId = 7;\n  int32 subSrcId = 8;\n  repeated SystemMsgAction actions = 9;\n  int64 groupCode = 10;\n  int64 actionUin = 11;\n  int32 groupMsgType = 12;\n  int32 groupInviterRole = 13;\n  FriendInfo friendInfo = 14;\n  SGroupInfo groupInfo = 15;\n  int64 actorUin = 16;\n  string msgActorDescribe = 17;\n  string msgAdditionalList = 18;\n  int32 relation = 19;\n  int32 reqsubtype = 20;\n  int64 cloneUin = 21;\n  int64 discussUin = 22;\n  int64 eimGroupId = 23;\n  MsgInviteExt msgInviteExtinfo = 24;\n  MsgPayGroupExt msgPayGroupExtinfo = 25;\n  int32 sourceFlag = 26;\n  bytes gameNick = 27;\n  bytes gameMsg = 28;\n  int32 groupFlagext3 = 29;\n  int64 groupOwnerUin = 30;\n  int32 doubtFlag = 31;\n  bytes warningTips = 32;\n  bytes nameMore = 33;\n  int32 reqUinFaceid = 50;\n  string reqUinNick = 51;\n  string groupName = 52;\n  string actionUinNick = 53;\n  string msgQna = 54;\n  string msgDetail = 55;\n  int32 groupExtFlag = 57;\n  string actorUinNick = 58;\n  string picUrl = 59;\n  string cloneUinNick = 60;\n  string reqUinBusinessCard = 61;\n  string eimGroupIdName = 63;\n  string reqUinPreRemark = 64;\n  string actionUinQqNick = 65;\n  string actionUinRemark = 66;\n  int32 reqUinGender = 67;\n  int32 reqUinAge = 68;\n  int32 c2cInviteJoinGroupFlag = 69;\n  int32 cardSwitch = 101;\n}\nmessage SystemMsgAction {\n  string name = 1;\n  string result = 2;\n  int32 action = 3;\n  SystemMsgActionInfo actionInfo = 4;\n  string detailName = 5;\n}\nmessage SystemMsgActionInfo {\n  int32  type = 1;\n  int64 groupCode = 2;\n  bytes sig = 3;\n  string msg = 50;\n  int32 groupId = 51;\n  string remark = 52;\n  bool blacklist = 53;\n  AddFrdSNInfo addFrdSNInfo = 54;\n}\n   "
  },
  {
    "path": "ricq-guild/src/protocol/mod.rs",
    "content": "use crate::protocol::protobuf::{ChannelMsg, GuildNode};\nuse bytes::Bytes;\nuse dynamic_protobuf::{dynamic_message, DynamicMessage};\nuse ricq_core::common::RQAddr;\nuse ricq_core::msg::{MessageChainBuilder, MessageElem};\n\n#[derive(Clone, Debug, Default)]\npub struct FirstViewResponse {\n    pub guild_count: u32,\n    pub self_tinyid: u64,\n    pub direct_message_switch: u32,\n    pub direct_message_guild_count: u32,\n}\n\n#[derive(Clone, Debug, Default)]\npub struct FirstViewMessage {\n    pub push_flag: u32,\n    pub guild_nodes: Vec<GuildNode>,\n    pub channel_msgs: Vec<ChannelMsg>,\n    pub get_msg_time: u64,\n    pub direct_message_guild_nodes: Vec<GuildNode>,\n}\n\n#[derive(Clone, Debug, Default)]\npub struct FirstView {\n    pub response: FirstViewResponse,\n    pub message: FirstViewMessage,\n}\n\n#[derive(Clone, Debug, Default)]\npub struct GuildUserProfile {\n    pub tiny_id: u64,\n    pub nickname: String,\n    pub avatar_url: String,\n    pub join_time: i64,\n}\n\n#[derive(Clone, Debug, Default)]\npub struct GuildSelfProfile {\n    pub tiny_id: u64,\n    pub nickname: String,\n    pub avatar_url: String,\n}\n\n#[derive(Clone, Debug, Default)]\npub struct GuildImage {\n    pub file_id: u64,\n    pub file_name: String,\n    pub size: u32,\n    pub width: u32,\n    pub height: u32,\n    pub image_type: i32,\n    pub download_index: Vec<u8>,\n    pub md5: Vec<u8>,\n    pub server_ip: u32,\n    pub server_port: u16,\n}\n\nimpl ricq_core::msg::PushElem for GuildImage {\n    fn push_to(img: Self, vec: &mut Vec<MessageElem>) {\n        vec.push(MessageElem::CustomFace(ricq_core::pb::msg::CustomFace {\n            file_path: Some(img.file_name),\n            file_id: Some(img.file_id as _),\n            server_ip: Some(img.server_ip),\n            server_port: Some(img.server_port as _),\n            file_type: Some(66),\n            signature: None,\n            useful: Some(1),\n            md5: Some(img.md5),\n            biz_type: Some(0),\n            image_type: Some(img.image_type),\n            width: Some(img.width),\n            height: Some(img.height),\n            source: Some(200),\n            size: Some(img.size),\n            origin: Some(1),\n            thumb_width: Some((img.width * 10 / 3) as _),\n            thumb_height: Some((img.height * 10 / 3) as _),\n            show_len: Some(0),\n            download_len: Some(0),\n            pb_reserve: {\n                let m = dynamic_message! {\n                    1 => 0u32,\n                    2 => 0u32,\n                    6 => DynamicMessage::new(),\n                    10 => 0u32,\n                    15 => 1u32, // or 8?\n                    20 => Bytes::from(img.download_index)\n                }\n                .encode_to_vec();\n\n                Some(m)\n            },\n            ..Default::default()\n        }));\n    }\n}\n\nimpl ricq_core::msg::PushBuilder for GuildImage {\n    fn push_builder(elem: Self, builder: &mut MessageChainBuilder) {\n        ricq_core::msg::PushElem::push_to(elem, &mut builder.elems);\n    }\n}\n\n#[derive(Debug, Clone)]\npub enum GuildImageStoreResp {\n    Exist {\n        file_id: u64,\n        addrs: Vec<RQAddr>,\n        download_index: Vec<u8>,\n    },\n    NotExist {\n        file_id: u64,\n        upload_key: Vec<u8>,\n        upload_addrs: Vec<RQAddr>,\n        download_index: Vec<u8>,\n    },\n}\n\n#[allow(clippy::all)]\npub mod protobuf {\n    include!(concat!(env!(\"OUT_DIR\"), \"/\", \"guild.rs\"));\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/GuildChannelBase.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nimport \"MsgResponsesSvr.proto\";\n\nmessage ChannelUserInfo {\n  optional ClientIdentity clientIdentity = 1;\n  optional uint32 memberType = 2;\n  optional ChannelUserPermission permission = 3;\n  repeated BaseRoleGroupInfo roleGroups = 4;\n}\n\nmessage ChannelUserPermission {\n  optional bool allowReadFeed = 1;\n  optional bool allowWriteFeed = 2;\n}\n\nmessage ClientIdentity {\n  optional uint32 clientId = 1;\n  optional string desc = 2;\n}\n\nmessage BaseGuildInfo {\n  optional uint64 guildId = 1;\n  optional string name = 2;\n  optional uint64 joinTime = 3;\n}\n\nmessage BaseRoleGroupInfo {\n  optional uint64 roleId = 1;\n  optional string name = 2;\n  optional uint32 color = 3;\n}\n\nmessage StChannelInfo {\n  optional StChannelSign sign = 1;\n  optional string name = 2;\n  optional string iconUrl = 3;\n}\n\nmessage StChannelSign {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n}\n/*\nmessage StEmojiReaction {\n  optional string emojiId = 1;\n  optional uint64 emojiType = 2;\n  optional uint64 cnt = 3;\n  optional bool isClicked = 4;\n  optional bool isDefaultEmoji = 10001;\n}\n */\n\nmessage StEmotionReactionInfo {\n  optional string id = 1;\n  repeated EmojiReaction emojiReactionList = 2;\n}\n\n\nmessage StCommonExt {\n  repeated CommonEntry mapInfo = 1;\n  optional string attachInfo = 2;\n  repeated BytesEntry mapBytesInfo = 3;\n}\n\nmessage BytesEntry {\n  optional string key = 1;\n  optional bytes value = 2;\n}\n\nmessage CommonEntry {\n  optional string key = 1;\n  optional string value = 2;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/GuildFeedCloudMeta.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nimport \"GuildChannelBase.proto\";\n\nmessage ContentMetaData {\n  optional RichTextContentCount count = 1;\n  optional int64 ContentID = 2;\n}\n\nmessage FeedMetaData {\n  optional ContentMetaData content = 1;\n  optional uint64 lastModifiedTime = 2;\n}\n\nmessage FeedRedTouchTransInfo {\n  optional string feedId = 1;\n  optional string author = 2;\n  optional int64 createTs = 3;\n  optional int32 msgType = 4;\n  optional int32 pageType = 5;\n  optional int32 redType = 6;\n  optional int32 insertPageType = 7;\n}\n\nmessage NoticeOperation {\n  optional uint32 type = 1;\n  optional string schema = 2;\n}\n\nmessage RichTextContentCount {\n  optional uint64 textWord = 1;\n  optional uint64 at = 2;\n  optional uint64 url = 3;\n  optional uint64 emoji = 4;\n  optional uint64 image = 5;\n  optional uint64 video = 6;\n}\n\nmessage StAnimation {\n  optional uint32 width = 1;\n  optional uint32 height = 2;\n  optional string animationUrl = 3;\n  optional bytes busiData = 4;\n}\n\nmessage StBusiReportInfo {\n  optional StRecomReportInfo recomReport = 1;\n  optional string traceID = 2;\n}\n\nmessage StChannelShareInfo {\n  optional string feedID = 1;\n  optional string posterID = 2;\n  optional uint64 feedPublishAt = 3;\n  optional StChannelSign channelSign = 4;\n  optional uint64 updateDurationMs = 5;\n  optional StChannelShareSign sign = 6;\n}\n\nmessage StChannelShareSign {\n  optional uint64 createAt = 1;\n  optional string token = 2;\n}\n\nmessage StCircleRankItem {\n  optional int32 rankNo = 1;\n  optional string circleName = 2;\n  optional int64 fuelValue = 3;\n  optional int64 feedNum = 4;\n  optional string circleID = 5;\n}\n\nmessage StClientInfo {\n  optional string feedclientkey = 1;\n  repeated CommonEntry clientMap = 2;\n}\n\n\nmessage StComment {\n  optional string id = 1;\n  optional StUser postUser = 2;\n  optional uint64 createTime = 3;\n  optional string content = 4;\n  optional uint32 replyCount = 5;\n  repeated StReply vecReply = 6;\n  optional bytes busiData = 7;\n  optional StLike likeInfo = 8;\n  optional uint32 typeFlag = 9;\n  repeated string atUinList = 10;\n  optional uint32 typeFlag2 = 11;\n  optional uint64 createTimeNs = 12;\n  repeated CommonEntry storeExtInfo = 13;\n  optional string thirdId = 14;\n  optional uint32 sourceType = 15;\n  optional StRichText richContents = 16;\n}\n\nmessage StDebugInfo {\n  repeated CommonEntry debugMap = 1;\n}\n\nmessage StDittoFeed {\n  optional uint32 dittoId = 1;\n  optional uint32 dittoPatternId = 2;\n  optional bytes dittoData = 3;\n  optional bytes dittoDataNew = 4;\n}\n\nmessage StExifInfo {\n  repeated CommonEntry kvs = 1;\n}\n\nmessage StExternalMedalWallInfo {\n  optional bool needRedPoint = 1;\n  repeated StMedalInfo medalInfos = 2;\n  optional string medalWallJumpUrl = 3;\n  optional bool needShowEntrance = 4;\n}\n\nmessage StFeed {\n  optional string id = 1;\n  optional StRichText title = 2;\n  optional StRichText subtitle = 3;\n  optional StUser poster = 4;\n  repeated StVideo videos = 5;\n  optional StRichText contents = 6;\n  optional uint64 createTime = 7;\n  optional StEmotionReactionInfo emotionReaction = 8;\n  optional uint32 commentCount = 9;\n  repeated StComment vecComment = 10;\n  optional StShare share = 11;\n  optional StVisitor visitorInfo = 12;\n  repeated StImage images = 13;\n  optional StPoiInfoV2 poiInfo = 14;\n  repeated StTagInfo tagInfos = 15;\n  optional bytes busiReport = 16;\n  repeated uint32 opMask = 17;\n  optional StOpinfo opinfo = 18;\n  repeated CommonEntry extInfo = 19;\n  optional string patternInfo = 20;\n  optional StChannelInfo channelInfo = 21;\n  optional uint64 createTimeNs = 22;\n  optional StFeedSummary summary = 23;\n  optional StRecomInfo recomInfo = 24;\n  optional FeedMetaData meta = 25;\n}\n\nmessage StFeedAbstract {\n  optional string id = 1;\n  optional string title = 2;\n  optional StUser poster = 3;\n  optional StImage pic = 4;\n  optional uint32 type = 5;\n  optional uint64 createTime = 6;\n  optional StVideo video = 7;\n  optional uint32 fuelNum = 8;\n  optional string content = 9;\n  repeated StImage images = 10;\n  optional StFeedCount countInfo = 11;\n}\n\nmessage StFeedCount {\n  optional int64 liked = 1;\n  optional int64 push = 2;\n  optional int64 comment = 3;\n  optional int64 visitor = 4;\n}\n\nmessage StFeedSummary {\n  optional uint32 layoutType = 1;\n}\n\nmessage StFollowRecomInfo {\n  optional string followText = 1;\n  repeated StFollowUser followUsers = 4;\n  optional string commFriendText = 6;\n  optional string commGroupText = 7;\n}\n\nmessage StFollowUser {\n  optional uint64 uid = 1;\n  optional string nick = 2;\n}\n\nmessage StGPSV2 {\n  optional int64 lat = 1;\n  optional int64 lon = 2;\n  optional int64 eType = 3;\n  optional int64 alt = 4;\n}\n\nmessage StGuidePublishBubble {\n  optional string id = 1;\n  optional StImage backgroundImage = 2;\n  optional string jumpUrl = 3;\n}\n\nmessage StIconInfo {\n  optional string iconUrl40 = 1;\n  optional string iconUrl100 = 2;\n  optional string iconUrl140 = 3;\n  optional string iconUrl640 = 4;\n  optional string iconUrl = 5;\n}\n\nmessage StImage {\n  optional uint32 width = 1;\n  optional uint32 height = 2;\n  optional string picUrl = 3;\n  repeated StImageUrl vecImageUrl = 4;\n  optional string picId = 5;\n  optional bytes busiData = 6;\n  optional string imageMD5 = 7;\n  optional string layerPicUrl = 8;\n  optional string patternId = 9;\n  optional uint32 displayIndex = 10;\n}\n\nmessage StImageUrl {\n  optional uint32 levelType = 1;\n  optional string url = 2;\n  optional uint32 width = 3;\n  optional uint32 height = 4;\n  optional bytes busiData = 5;\n}\n\nmessage StLightInteractInfo {\n  optional StUser user = 1;\n  optional StRelationInfo relation = 2;\n  optional uint32 count = 3;\n  optional bytes busiData = 4;\n}\n\nmessage StLike {\n  optional string id = 1;\n  optional uint32 count = 2;\n  optional uint32 status = 3;\n  repeated StUser vecUser = 4;\n  optional bytes busiData = 5;\n  optional StUser postUser = 6;\n  optional uint32 hasLikedCount = 7;\n  optional uint32 ownerStatus = 8;\n  optional string jumpUrl = 9;\n}\n\nmessage StLiteBanner {\n  optional StImage icon = 1;\n  optional string title = 2;\n  optional string jumpUrl = 3;\n  optional string activityID = 4;\n  optional string jsonStyle = 5;\n  repeated CommonEntry extInfo = 6;\n}\n\nmessage StMaterialDataNew {\n  optional string materialType = 1;\n  repeated StSingleMaterial materialList = 2;\n}\n\nmessage StMedalInfo {\n  optional int32 type = 1;\n  optional string medalName = 2;\n  optional string medalID = 3;\n  optional int32 rank = 4;\n  optional bool isHighLight = 5;\n  optional bool isNew = 6;\n  optional string jumpUrl = 7;\n  optional string iconUrl = 8;\n  optional string backgroundUrl = 9;\n  optional string describe = 10;\n  optional int32 reportValue = 11;\n}\n\nmessage StNotice {\n  optional StFeed psvFeed = 1;\n  optional StFeed origineFeed = 2;\n  optional StNoticePattonInfo pattonInfo = 3;\n}\n\nmessage StNoticePattonInfo {\n  optional uint32 pattonType = 1;\n  optional StPlainTxtInfo plainTxt = 2;\n}\n\nmessage StNoticeTxtInfo {\n  optional StRichText content = 1;\n  optional StRichText contentOfReference = 2;\n}\n\nmessage StOpinfo {\n  repeated uint64 createTime = 1;\n}\n\nmessage StPlainTxtInfo {\n  optional StNoticeTxtInfo txtInfo = 1;\n  optional NoticeOperation operation = 2;\n}\n\nmessage StPoiInfoV2 {\n  optional string poiId = 1;\n  optional string name = 2;\n  optional int32 poiType = 3;\n  optional string typeName = 4;\n  optional string address = 5;\n  optional int32 districtCode = 6;\n  optional StGPSV2 gps = 7;\n  optional int32 distance = 8;\n  optional int32 hotValue = 9;\n  optional string phone = 10;\n  optional string country = 11;\n  optional string province = 12;\n  optional string city = 13;\n  optional int32 poiNum = 14;\n  optional int32 poiOrderType = 15;\n  optional string defaultName = 16;\n  optional string district = 17;\n  optional string dianPingId = 18;\n  optional string distanceText = 19;\n  optional string displayName = 20;\n}\n\nmessage StPrePullCacheFeed {\n  optional string id = 1;\n  optional StUser poster = 2;\n  optional uint64 createTime = 3;\n  repeated BytesEntry busiTranparent = 4;\n}\n\nmessage StProxyInfo {\n  optional int32 cmdId = 1;\n  optional int32 subCmdId = 2;\n  optional string appProtocol = 3;\n  optional bytes reqBody = 4;\n}\n\nmessage StRankingItem {\n  optional StUser user = 1;\n  optional StRelationInfo relation = 2;\n  optional int64 score = 3;\n  optional int32 grade = 4;\n  optional bytes busiData = 5;\n  optional int32 rankNo = 6;\n  optional int32 inTopicList = 7;\n}\n\nmessage StRecomForward {\n  optional string id = 1;\n  optional string title = 2;\n  optional string subtitle = 3;\n  optional StUser poster = 4;\n  optional uint64 createTime = 5;\n  optional uint32 type = 6;\n  optional bytes busiData = 7;\n}\n\nmessage StRecomInfo {\n  optional string recomReason = 1;\n  optional bytes recomAttachInfo = 2;\n  optional string recomTrace = 3;\n  optional bytes clientSealData = 4;\n  optional string iconUrl = 5;\n  optional int32 recomReasonType = 6;\n}\n\nmessage StRecomReportInfo {\n  repeated StSingleRecomReportInfo recomInfos = 1;\n}\n\nmessage StRelationInfo {\n  optional string id = 1;\n  optional uint32 relation = 2;\n  optional bytes busiData = 3;\n  optional uint32 relationState = 4;\n  optional uint32 score = 5;\n  optional bool isBlock = 6;\n  optional bool isBlocked = 7;\n  optional bool isFriend = 8;\n  optional bool isUncare = 9;\n  optional uint64 imBitMap = 10;\n}\n\nmessage StReply {\n  optional string id = 1;\n  optional StUser postUser = 2;\n  optional uint64 createTime = 3;\n  optional string content = 4;\n  optional StUser targetUser = 5;\n  optional bytes busiData = 6;\n  optional StLike likeInfo = 7;\n  optional uint32 typeFlag = 8;\n  optional uint32 modifyflag = 9;\n  repeated string atUinList = 10;\n  optional uint32 typeFlag2 = 11;\n  optional uint64 createTimeNs = 12;\n  repeated CommonEntry storeExtInfo = 13;\n  optional string thirdId = 14;\n  optional string targetReplyID = 15;\n  optional uint32 sourceType = 16;\n  optional StRichText richContents = 17;\n}\n\nmessage StReportInfo {\n  optional string id = 1;\n  optional bytes busiReport = 2;\n}\n\nmessage StRichText {\n  repeated StRichTextContent contents = 1;\n}\n\nmessage StRichTextAtContent {\n  optional uint32 type = 1;\n  optional GuildChannelBaseGuildInfo guildInfo = 2;\n  optional GuildChannelBaseRoleGroupInfo roleGroupId = 3;\n  optional StUser user = 4;\n}\n\nmessage GuildChannelBaseGuildInfo {\n  optional uint64 guildId = 1;\n  optional string name = 2;\n  optional uint64 joinTime = 3;\n}\n\nmessage GuildChannelBaseRoleGroupInfo {\n  optional uint64 roleId = 1;\n  optional string name = 2;\n  optional uint32 color = 3;\n}\n\nmessage StRichTextChannelContent {\n  optional StChannelInfo channelInfo = 1;\n}\n\nmessage StRichTextContent {\n  optional uint32 type = 1;\n  optional string patternId = 2;\n  optional StRichTextTextContent textContent = 3;\n  optional StRichTextAtContent atContent = 4;\n  optional StRichTextURLContent urlContent = 5;\n  optional StRichTextEmojiContent emojiContent = 6;\n  optional StRichTextChannelContent channelContent = 7;\n}\n\nmessage StRichTextEmojiContent {\n  optional string id = 1;\n  optional string type = 2;\n  optional string name = 3;\n  optional string url = 4;\n}\n\nmessage StRichTextTextContent {\n  optional string text = 1;\n}\n\nmessage StRichTextURLContent {\n  optional string url = 1;\n  optional string displayText = 2;\n}\n\nmessage StSameTopicGuideInfo {\n  optional uint32 isSameTopicGuide = 1;\n  optional int64 stayShowTime = 2;\n  optional string hashTag = 3;\n  optional string words = 4;\n  optional string jumpUrl = 5;\n  optional string reportExt = 6;\n}\n\nmessage StShare {\n  optional string title = 1;\n  optional string desc = 2;\n  optional uint32 type = 3;\n  optional string url = 4;\n  optional StUser author = 5;\n  optional StUser poster = 6;\n  repeated StVideo videos = 7;\n  optional string shorturl = 8;\n  optional string shareCardInfo = 9;\n  optional StShareQzoneInfo shareQzoneInfo = 10;\n  repeated StImage images = 11;\n  optional uint32 publishTotalUser = 12;\n  optional uint32 sharedCount = 13;\n  optional StChannelShareInfo channelShareInfo = 14;\n}\n\nmessage StShareQzoneInfo {\n  repeated CommonEntry entrys = 1;\n}\n\nmessage StSingleMaterial {\n  optional string materialId = 1;\n}\n\nmessage StSingleRecomReportInfo {\n  optional string reportID = 1;\n  optional bytes reportData = 2;\n}\n\nmessage StTagInfo {\n  optional string tagId = 1;\n  optional string tagName = 2;\n  optional string tagDec = 3;\n  repeated StUser userList = 4;\n  repeated StFeedAbstract feedList = 5;\n  optional uint32 tagTotalUser = 6;\n  optional uint32 tagTotalFeed = 7;\n  optional string tagWording = 8;\n  optional uint32 tagType = 9;\n  optional uint32 followState = 10;\n  optional StShare shareInfo = 11;\n  optional uint32 isTop = 12;\n  optional uint32 isSelected = 13;\n  optional int64 userViewHistory = 14;\n  optional StTagMedalInfo medal = 15;\n  optional uint32 status = 16;\n  optional StTagOperateInfo optInfo = 17;\n  optional uint32 tagBaseStatus = 18;\n  optional int32 isRecommend = 19;\n  optional int64 tagViewHistory = 20;\n  optional string operateIconUrl = 21;\n  optional string tagReport = 99;\n  optional string tagIconUrl = 100;\n}\n\nmessage StTagMedalInfo {\n  optional string tagID = 1;\n  optional string tagName = 2;\n  optional uint64 rank = 3;\n}\n\nmessage StTagOperateInfo {\n  optional string createUser = 1;\n  optional string coverURL = 2;\n  optional string desc = 3;\n  optional string backgroundURL = 4;\n  optional string bannerURL = 5;\n  optional string bannerSkipLink = 6;\n  optional int64 activityStartTime = 7;\n  optional int64 activityEndTime = 8;\n  optional string recommendReason = 9;\n  optional int32 isWhite = 10;\n  optional int64 beWhiteStartTime = 11;\n  optional int64 beWhiteEndTime = 12;\n  optional string publishSchema = 13;\n}\n\nmessage StUnifiedTag {\n  optional string unifiedType = 1;\n  optional string unifiedId = 2;\n}\n\nmessage StUser {\n  optional string id = 1;\n  optional string nick = 2;\n  optional StIconInfo icon = 3;\n  optional string desc = 4;\n  optional uint32 followState = 5;\n  optional uint32 type = 6;\n  optional uint32 sex = 7;\n  optional uint64 birthday = 8;\n  optional string school = 9;\n  optional string location = 11;\n  optional bytes busiData = 12;\n  optional uint32 frdState = 13;\n  optional uint32 relationState = 14;\n  optional uint32 blackState = 15;\n  optional StTagMedalInfo medal = 16;\n  optional int32 constellation = 17;\n  optional string jumpUrl = 18;\n  optional string locationCode = 19;\n  optional string thirdId = 20;\n  optional string company = 21;\n  optional string certificationDesc = 22;\n  optional uint32 descType = 23;\n  optional GuildChannelBaseChannelUserInfo channelUserInfo = 24;\n  optional string loginId = 25;\n}\n\nmessage GuildChannelBaseChannelUserInfo {\n  optional ClientIdentity clientIdentity = 1;\n  optional uint32 memberType = 2;\n  // optional ChannelUserPermission permission = 3;\n  repeated GuildChannelBaseRoleGroupInfo roleGroups = 4;\n}\n\nmessage StUserGroupInfo {\n  optional string id = 1;\n  optional string name = 2;\n  repeated StUser userList = 3;\n}\n\nmessage StUserRecomInfo {\n  optional StUser user = 1;\n  repeated StFeedAbstract feedList = 2;\n  optional bytes busiData = 3;\n}\n\nmessage StVideo {\n  optional string fileId = 1;\n  optional uint32 fileSize = 2;\n  optional uint32 duration = 3;\n  optional uint32 width = 4;\n  optional uint32 height = 5;\n  optional string playUrl = 6;\n  optional uint32 transStatus = 7;\n  optional uint32 videoPrior = 8;\n  optional uint32 videoRate = 9;\n  repeated StVideoUrl vecVideoUrl = 10;\n  optional bytes busiData = 11;\n  optional uint32 approvalStatus = 12;\n  optional uint32 videoSource = 13;\n  optional uint32 mediaQualityRank = 14;\n  optional float mediaQualityScore = 15;\n  optional string videoMD5 = 16;\n  optional uint32 isQuic = 17;\n  optional uint32 orientation = 18;\n  optional StImage cover = 19;\n  optional string patternId = 20;\n  optional uint32 displayIndex = 21;\n}\n\nmessage StVideoUrl {\n  optional uint32 levelType = 1;\n  optional string playUrl = 2;\n  optional uint32 videoPrior = 3;\n  optional uint32 videoRate = 4;\n  optional uint32 transStatus = 5;\n  optional bytes busiData = 6;\n  optional bool hasWatermark = 7;\n}\n\nmessage StVisitor {\n  optional uint32 viewCount = 1;\n  optional bytes busiData = 2;\n  optional uint32 recomCount = 3;\n  optional string viewDesc = 4;\n}\n\nmessage StWearingMedal {\n  repeated StWearingMedalInfo medalInfos = 1;\n}\n\nmessage StWearingMedalInfo {\n  optional int32 type = 1;\n  optional string medalName = 2;\n  optional string medalID = 3;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/GuildFeedCloudRead.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nimport \"GuildFeedCloudMeta.proto\";\nimport \"GuildChannelBase.proto\";\n\n\nmessage GetNoticesReq {\n  optional StCommonExt extInfo = 1;\n  optional uint32 pageNum = 2;\n  optional string attachInfo = 3;\n}\n\nmessage GetNoticesRsp {\n  optional StCommonExt extInfo = 1;\n  repeated StNotice notices = 2;\n  optional uint32 totalNum = 3;\n  optional bool isFinish = 4;\n  optional string attachInfo = 5;\n}\n\nmessage NeedInsertCommentInfo {\n  optional string commentID = 1;\n}\n\nmessage RefreshToast {\n  optional string text = 1;\n}\n\nmessage StGetChannelFeedsReq {\n  optional StCommonExt extInfo = 1;\n  optional uint32 count = 2;\n  optional uint32 from = 3;\n  optional StChannelSign channelSign = 4;\n  optional string feedAttchInfo = 5;\n}\n\nmessage StGetChannelFeedsRsp {\n  optional StCommonExt extInfo = 1;\n  repeated StFeed vecFeed = 2;\n  optional uint32 isFinish = 3;\n  optional StUser user = 4;\n  optional string feedAttchInfo = 5;\n  optional RefreshToast refreshToast = 6;\n}\n\nmessage StGetChannelShareFeedReq {\n  optional StCommonExt extInfo = 1;\n  optional uint32 from = 2;\n  optional StChannelShareInfo channelShareInfo = 3;\n}\n\nmessage StGetChannelShareFeedRsp {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n}\n\nmessage StGetFeedCommentsReq {\n  optional StCommonExt extInfo = 1;\n  optional string userId = 2;\n  optional string feedId = 3;\n  optional uint32 listNum = 4;\n  optional uint32 from = 5;\n  optional string attchInfo = 6;\n  optional string entrySchema = 7;\n}\n\nmessage StGetFeedCommentsRsp {\n  optional StCommonExt extInfo = 1;\n  repeated StComment vecComment = 2;\n  optional uint32 totalNum = 3;\n  optional uint32 isFinish = 4;\n  optional string attchInfo = 5;\n}\n\nmessage StGetFeedDetailReq {\n  optional StCommonExt extInfo = 1;\n  optional uint32 from = 2;\n  optional string userId = 3;\n  optional string feedId = 4;\n  optional uint64 createTime = 5;\n  optional uint32 detailType = 6;\n  optional StChannelSign channelSign = 7;\n}\n\nmessage StGetFeedDetailRsp {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional StUser loginUser = 3;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/GuildWriter.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nimport \"GuildFeedCloudMeta.proto\";\nimport \"GuildChannelBase.proto\";\n\nmessage StAlterFeedReq {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional bytes busiReqData = 3;\n  optional uint64 mBitmap = 4;\n  optional int32 from = 5;\n  optional int32 src = 6;\n  repeated CommonEntry alterFeedExtInfo = 7;\n  optional string jsonFeed = 8;\n  optional StClientContent clientContent = 9;\n}\n\nmessage StAlterFeedRsp {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional bytes busiRspData = 3;\n}\n\nmessage StClientContent {\n  repeated StClientImageContent clientImageContents = 1;\n  repeated StClientVideoContent clientVideoContents = 2;\n}\n\nmessage StClientImageContent {\n  optional string taskId = 1;\n  optional string picId = 2;\n  optional string url = 3;\n}\n\nmessage StClientVideoContent {\n  optional string taskId = 1;\n  optional string videoId = 2;\n  optional string videoUrl = 3;\n  optional string coverUrl = 4;\n}\n\nmessage StDelFeedReq {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional int32 from = 3;\n  optional int32 src = 4;\n}\n\nmessage StDelFeedRsp {\n  optional StCommonExt extInfo = 1;\n}\n\nmessage StDoCommentReq {\n  optional StCommonExt extInfo = 1;\n  optional uint32 commentType = 2;\n  optional StComment comment = 3;\n  optional StFeed feed = 4;\n  optional int32 from = 5;\n  optional bytes busiReqData = 6;\n  optional int32 src = 7;\n}\n\nmessage StDoCommentRsp {\n  optional StCommonExt extInfo = 1;\n  optional StComment comment = 2;\n  optional bytes busiRspData = 3;\n}\n\nmessage StDoLikeReq {\n  optional StCommonExt extInfo = 1;\n  optional uint32 likeType = 2;\n  optional StLike like = 3;\n  optional StFeed feed = 4;\n  optional bytes busiReqData = 5;\n  optional StComment comment = 6;\n  optional StReply reply = 7;\n  optional int32 from = 8;\n  optional int32 src = 9;\n  optional StEmotionReactionInfo emotionReaction = 10;\n}\n\nmessage StDoLikeRsp {\n  optional StCommonExt extInfo = 1;\n  optional StLike like = 2;\n  optional bytes busiRspData = 3;\n  optional StEmotionReactionInfo emotionReaction = 4;\n}\n\nmessage StDoReplyReq {\n  optional StCommonExt extInfo = 1;\n  optional uint32 replyType = 2;\n  optional StReply reply = 3;\n  optional StComment comment = 4;\n  optional StFeed feed = 5;\n  optional int32 from = 6;\n  optional bytes busiReqData = 7;\n  optional int32 src = 8;\n}\n\nmessage StDoReplyRsp {\n  optional StCommonExt extInfo = 1;\n  optional StReply reply = 2;\n  optional bytes busiRspData = 3;\n}\n\nmessage StDoSecurityReq {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional StComment comment = 3;\n  optional StReply reply = 4;\n  optional StUser poster = 5;\n  optional int32 secType = 6;\n}\n\nmessage StDoSecurityRsp {\n  optional StCommonExt extInfo = 1;\n}\n\nmessage StModifyFeedReq {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional uint64 mBitmap = 3;\n  optional int32 from = 4;\n  optional int32 src = 5;\n  repeated CommonEntry modifyFeedExtInfo = 6;\n}\n\nmessage StModifyFeedRsp {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional bytes busiRspData = 3;\n}\n\nmessage StPublishFeedReq {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional bytes busiReqData = 3;\n  optional int32 from = 4;\n  optional int32 src = 5;\n  repeated CommonEntry storeFeedExtInfo = 6;\n  optional string jsonFeed = 7;\n  optional StClientContent clientContent = 8;\n}\n\nmessage StPublishFeedRsp {\n  optional StCommonExt extInfo = 1;\n  optional StFeed feed = 2;\n  optional bytes busiRspData = 3;\n}\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/MsgResponsesSvr.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nmessage BatchGetMsgRspCountReq {\n  repeated GuildMsg guildMsgList = 1;\n}\n\nmessage BatchGetMsgRspCountRsp {\n  repeated GuildMsgInfo guildMsgInfoList = 1;\n}\n\nmessage SvrChannelMsg {\n  optional uint64 channelId = 1;\n  repeated MsgId id = 2;\n}\n\nmessage ChannelMsgInfo {\n  optional uint64 channelId = 1;\n  repeated MsgRespData respData = 2;\n}\n\nmessage EmojiReaction {\n  optional string emojiId = 1;\n  optional uint64 emojiType = 2;\n  optional uint64 cnt = 3;\n  optional bool isClicked = 4;\n  optional bool isDefaultEmoji = 10001;\n}\n\nmessage GuildMsg {\n  optional uint64 guildId = 1;\n  repeated SvrChannelMsg channelMsgList = 2;\n}\n\nmessage GuildMsgInfo {\n  optional uint64 guildId = 1;\n  repeated ChannelMsgInfo channelMsgInfoList = 2;\n}\n\nmessage MsgCnt {\n  optional MsgId id = 1;\n  repeated EmojiReaction emojiReaction = 2;\n}\n\nmessage MsgId {\n  optional uint64 version = 1;\n  optional uint64 seq = 2;\n}\n\nmessage MsgRespData {\n  optional MsgId id = 1;\n  optional bytes cnt = 2;\n}\n\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/common.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nimport \"msg/msg.proto\";\n\nmessage ChannelContentHead {\n  optional uint64 type = 1;\n  optional uint64 subType = 2;\n  optional uint64 random = 3;\n  optional uint64 seq = 4;\n  optional uint64 cntSeq = 5;\n  optional uint64 time = 6;\n  optional bytes meta = 7;\n}\n\nmessage DirectMessageMember {\n  optional uint64 uin = 1;\n  optional uint64 tinyid = 2;\n  optional uint64 sourceGuildId = 3;\n  optional bytes sourceGuildName = 4;\n  optional bytes nickName = 5;\n  optional bytes memberName = 6;\n  optional uint32 notifyType = 7;\n}\n\nmessage ChannelEvent {\n  optional uint64 type = 1;\n  optional uint64 version = 2;\n  optional ChannelMsgOpInfo opInfo = 3;\n}\n\nmessage ChannelExtInfo {\n  optional bytes fromNick = 1;\n  optional bytes guildName = 2;\n  optional bytes channelName = 3;\n  optional uint32 visibility = 4;\n  optional uint32 notifyType = 5;\n  optional uint32 offlineFlag = 6;\n  optional uint32 nameType = 7;\n  optional bytes memberName = 8;\n  optional uint32 timestamp = 9;\n  optional uint64 eventVersion = 10;\n  repeated ChannelEvent events = 11;\n  optional ChannelRole fromRoleInfo = 12;\n  optional ChannelFreqLimitInfo freqLimitInfo = 13;\n  repeated DirectMessageMember directMessageMember = 14;\n}\n\nmessage ChannelFreqLimitInfo {\n  optional uint32 isLimited = 1;\n  optional uint32 leftCount = 2;\n  optional uint64 limitTimestamp = 3;\n}\n\nmessage ChannelInfo {\n  optional uint64 id = 1;\n  optional bytes name = 2;\n  optional uint32 color = 3;\n  optional uint32 hoist = 4;\n}\n\nmessage ChannelLoginSig {\n  optional uint32 type = 1;\n  optional bytes sig = 2;\n  optional uint32 appid = 3;\n}\n\nmessage ChannelMeta {\n  optional uint64 fromUin = 1;\n  optional ChannelLoginSig loginSig = 2;\n}\n\nmessage ChannelMsgContent {\n  optional ChannelMsgHead head = 1;\n  optional ChannelMsgCtrlHead ctrlHead = 2;\n  optional msg.MessageBody body = 3;\n  optional ChannelExtInfo extInfo = 4;\n}\n\nmessage ChannelMsgCtrlHead {\n  repeated bytes includeUin = 1;\n  // repeated uint64 excludeUin = 2; // bytes?\n  // repeated uint64 featureid = 3;\n  optional uint32 offlineFlag = 4;\n  optional uint32 visibility = 5;\n  optional uint64 ctrlFlag = 6;\n  repeated ChannelEvent events = 7;\n  optional uint64 level = 8;\n  repeated PersonalLevel personalLevels = 9;\n  optional uint64 guildSyncSeq = 10;\n  optional uint32 memberNum = 11;\n  optional uint32 channelType = 12;\n  optional uint32 privateType = 13;\n}\n\nmessage ChannelMsgHead {\n  optional ChannelRoutingHead routingHead = 1;\n  optional ChannelContentHead contentHead = 2;\n}\n\nmessage ChannelMsgMeta {\n  optional uint64 atAllSeq = 1;\n}\n\nmessage ChannelMsgOpInfo {\n  optional uint64 operatorTinyid = 1;\n  optional uint64 operatorRole = 2;\n  optional uint64 reason = 3;\n  optional uint64 timestamp = 4;\n  optional uint64 atType = 5;\n}\n\nmessage PersonalLevel {\n  optional uint64 toUin = 1;\n  optional uint64 level = 2;\n}\n\nmessage ChannelRole {\n  optional uint64 id = 1;\n  optional bytes info = 2;\n  optional uint32 flag = 3;\n}\n\nmessage ChannelRoutingHead {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n  optional uint64 fromUin = 3;\n  optional uint64 fromTinyid = 4;\n  optional uint64 guildCode = 5;\n  optional uint64 fromAppid = 6;\n  optional uint32 directMessageFlag = 7;\n}"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/msgpush.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nimport \"common.proto\";\n\nmessage FocusInfo {\n  repeated uint64 channelIdList = 1;\n}\n\nmessage MsgOnlinePush {\n  repeated ChannelMsgContent msgs = 1;\n  optional uint32 generalFlag = 2;\n  optional uint32 needResp = 3;\n  optional bytes serverBuf = 4;\n  optional uint32 compressFlag = 5;\n  optional bytes compressMsg = 6;\n  optional FocusInfo focusInfo = 7;\n  optional uint32 hugeFlag = 8;\n}\n\nmessage MsgPushResp {\n  optional bytes serverBuf = 1;\n}\n\nmessage PressMsg {\n  repeated ChannelMsgContent msgs = 1;\n}\n\nmessage ServerBuf {\n  optional uint32 svrIp = 1;\n  optional uint32 svrPort = 2;\n  optional bytes echoKey = 3;\n}\n\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/oidb0xf62.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nimport \"common.proto\";\nimport \"msg/msg.proto\";\n\nmessage DF62ReqBody {\n  optional ChannelMsgContent msg = 1;\n}\n\nmessage DF62RspBody {\n  optional uint32 result = 1;\n  optional bytes errmsg = 2;\n  optional uint32 sendTime = 3;\n  optional ChannelMsgHead head = 4;\n  optional uint32 errType = 5;\n  optional TransSvrInfo transSvrInfo = 6;\n  optional ChannelFreqLimitInfo freqLimitInfo = 7;\n  optional msg.MessageBody body = 8;\n}\n\nmessage TransSvrInfo {\n  optional uint32 subType = 1;\n  optional int32 retCode = 2;\n  optional bytes errMsg = 3;\n  optional bytes transInfo = 4;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/servtype.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nmessage AppChannelMsg {\n  optional string summary = 1;\n  optional string msg = 2;\n  optional uint64 expireTimeMs = 3;\n  optional uint32 schemaType = 4;\n  optional string schema = 5;\n}\n\nmessage CategoryChannelInfo {\n  optional uint32 channelIndex = 1;\n  optional uint64 channelId = 2;\n}\n\nmessage CategoryInfo {\n  optional uint32 categoryIndex = 1;\n  repeated CategoryChannelInfo channelInfo = 2;\n  optional bytes categoryName = 3;\n  optional uint64 categoryId = 4;\n}\n\nmessage ChanInfoFilter {\n  optional uint32 channelName = 2;\n  optional uint32 creatorId = 3;\n  optional uint32 createTime = 4;\n  optional uint32 guildId = 5;\n  optional uint32 msgNotifyType = 6;\n  optional uint32 channelType = 7;\n  optional uint32 speakPermission = 8;\n  optional uint32 lastMsgSeq = 11;\n  optional uint32 lastCntMsgSeq = 12;\n  optional VoiceChannelInfoFilter voiceChannelInfoFilter = 14;\n  optional LiveChannelInfoFilter liveChannelInfoFilter = 15;\n  optional uint32 bannedSpeak = 16;\n}\n\nmessage ChangeChanInfo {\n  optional uint64 guildId = 1;\n  optional uint64 chanId = 2;\n  optional uint64 operatorId = 3;\n  optional MsgSeq infoSeq = 4;\n  optional uint32 updateType = 5;\n  optional ChanInfoFilter chanInfoFilter = 6;\n  optional ServChannelInfo chanInfo = 7;\n}\n\nmessage ChangeGuildInfo {\n  optional uint64 guildId = 1;\n  optional uint64 operatorId = 2;\n  optional MsgSeq infoSeq = 3;\n  optional MsgSeq faceSeq = 4;\n  optional uint32 updateType = 5;\n  optional GuildInfoFilter guildInfoFilter = 6;\n  optional GuildInfo guildInfo = 7;\n}\n\nmessage ChannelID {\n  optional uint64 chanId = 1;\n}\n\nmessage ServChannelInfo {\n  optional uint64 channelId = 1;\n  optional bytes channelName = 2;\n  optional uint64 creatorId = 3;\n  optional uint64 createTime = 4;\n  optional uint64 guildId = 5;\n  optional uint32 msgNotifyType = 6;\n  optional uint32 channelType = 7;\n  optional uint32 speakPermission = 8;\n  optional MsgSeq lastMsgSeq = 11;\n  optional MsgSeq lastCntMsgSeq = 12;\n  optional VoiceChannelInfo voiceChannelInfo = 14;\n  optional LiveChannelInfo liveChannelInfo = 15;\n  optional uint32 bannedSpeak = 16;\n}\n\nmessage CommGrayTips {\n  optional uint64 busiType = 1;\n  optional uint64 busiId = 2;\n  optional uint32 ctrlFlag = 3;\n  optional uint64 templId = 4;\n  repeated TemplParam templParam = 5;\n  optional bytes content = 6;\n  optional uint64 tipsSeqId = 10;\n  optional bytes pbReserv = 100;\n\n  message TemplParam {\n    optional bytes name = 1;\n    optional bytes value = 2;\n  }\n}\n\nmessage CreateChan {\n  optional uint64 guildId = 1;\n  optional uint64 operatorId = 3;\n  repeated ChannelID createId = 4;\n}\n\nmessage CreateGuild {\n  optional uint64 operatorId = 1;\n  optional uint64 guildId = 2;\n}\n\nmessage DestroyChan {\n  optional uint64 guildId = 1;\n  optional uint64 operatorId = 3;\n  repeated ChannelID deleteId = 4;\n}\n\nmessage DestroyGuild {\n  optional uint64 operatorId = 1;\n  optional uint64 guildId = 2;\n}\n\nmessage EventBody {\n  optional ReadNotify readNotify = 1;\n  optional CommGrayTips commGrayTips = 2;\n  optional CreateGuild createGuild = 3;\n  optional DestroyGuild destroyGuild = 4;\n  optional JoinGuild joinGuild = 5;\n  optional KickOffGuild kickOffGuild = 6;\n  optional QuitGuild quitGuild = 7;\n  optional ChangeGuildInfo changeGuildInfo = 8;\n  optional CreateChan createChan = 9;\n  optional DestroyChan destroyChan = 10;\n  optional ChangeChanInfo changeChanInfo = 11;\n  optional SetAdmin setAdmin = 12;\n  optional SetMsgRecvType setMsgRecvType = 13;\n  optional UpdateMsg updateMsg = 14;\n  optional SetTop setTop = 17;\n  optional SwitchVoiceChannel switchChannel = 18;\n  optional UpdateCategory updateCategory = 21;\n  optional UpdateVoiceBlockList updateVoiceBlockList = 22;\n  optional SetMute setMute = 23;\n  optional LiveRoomStatusChangeMsg liveStatusChangeRoom = 24;\n  optional SwitchLiveRoom switchLiveRoom = 25;\n  repeated MsgEvent events = 39;\n  optional SchedulerMsg scheduler = 40;\n  optional AppChannelMsg appChannel = 41;\n  optional FeedEvent feedEvent = 44;\n  optional AppChannelMsg weakMsgAppChannel = 46;\n  optional ReadFeedNotify readFeedNotify = 48;\n}\n\nmessage FeedEvent {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n  optional string feedId = 3;\n  optional string msgSummary = 4;\n  optional uint64 eventTime = 5;\n}\n\nmessage ReadFeedNotify {\n  optional uint64 reportTime = 2;\n}\n\nmessage GroupProStatus {\n  optional uint32 isEnable = 1;\n  optional uint32 isBanned = 2;\n  optional uint32 isFrozen = 3;\n}\n\nmessage GuildInfo {\n  optional uint64 guildCode = 2;\n  optional uint64 ownerId = 3;\n  optional uint64 createTime = 4;\n  optional uint32 memberMaxNum = 5;\n  optional uint32 memberNum = 6;\n  optional uint32 guildType = 7;\n  optional bytes guildName = 8;\n  repeated uint64 robotList = 9;\n  repeated uint64 adminList = 10;\n  optional uint32 robotMaxNum = 11;\n  optional uint32 adminMaxNum = 12;\n  optional bytes profile = 13;\n  optional uint64 faceSeq = 14;\n  optional GroupProStatus guildStatus = 15;\n  optional uint32 channelNum = 16;\n  optional MsgSeq memberChangeSeq = 5002;\n  optional MsgSeq guildInfoChangeSeq = 5003;\n  optional MsgSeq channelChangeSeq = 5004;\n}\n\nmessage GuildInfoFilter {\n  optional uint32 guildCode = 2;\n  optional uint32 ownerId = 3;\n  optional uint32 createTime = 4;\n  optional uint32 memberMaxNum = 5;\n  optional uint32 memberNum = 6;\n  optional uint32 guildType = 7;\n  optional uint32 guildName = 8;\n  optional uint32 robotList = 9;\n  optional uint32 adminList = 10;\n  optional uint32 robotMaxNum = 11;\n  optional uint32 adminMaxNum = 12;\n  optional uint32 profile = 13;\n  optional uint32 faceSeq = 14;\n  optional uint32 guildStatus = 15;\n  optional uint32 channelNum = 16;\n  optional uint32 memberChangeSeq = 5002;\n  optional uint32 guildInfoChangeSeq = 5003;\n  optional uint32 channelChangeSeq = 5004;\n}\n\nmessage JoinGuild {\n  optional uint64 memberId = 3;\n  optional uint32 memberType = 4;\n  optional uint64 memberTinyid = 5;\n}\n\nmessage KickOffGuild {\n  optional uint64 memberId = 3;\n  optional uint32 setBlack = 4;\n  optional uint64 memberTinyid = 5;\n}\n\nmessage LiveChannelInfo {\n  optional uint64 roomId = 1;\n  optional uint64 anchorUin = 2;\n  optional bytes name = 3;\n}\n\nmessage LiveChannelInfoFilter {\n  optional uint32 isNeedRoomId = 1;\n  optional uint32 isNeedAnchorUin = 2;\n  optional uint32 isNeedName = 3;\n}\n\nmessage LiveRoomStatusChangeMsg {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n  optional uint64 roomId = 3;\n  optional uint64 anchorTinyid = 4;\n  optional uint32 action = 5;\n}\n\nmessage MsgEvent {\n  optional uint64 seq = 1;\n  optional uint64 eventType = 2;\n  optional uint64 eventVersion = 3;\n}\n\nmessage MsgSeq {\n  optional uint64 seq = 1;\n  optional uint64 time = 2;\n}\n\nmessage QuitGuild {}\n\nmessage ReadNotify {\n  optional uint64 channelId = 1;\n  optional uint64 guildId = 2;\n  optional MsgSeq readMsgSeq = 3;\n  optional MsgSeq readCntMsgSeq = 4;\n  optional bytes readMsgMeta = 5;\n}\n\nmessage SchedulerMsg {\n  optional bytes creatorHeadUrl = 1;\n  optional string wording = 2;\n  optional uint64 expireTimeMs = 3;\n}\n\nmessage SetAdmin {\n  optional uint64 guildId = 1;\n  optional uint64 chanId = 2;\n  optional uint64 operatorId = 3;\n  optional uint64 adminId = 4;\n  optional uint64 adminTinyid = 5;\n  optional uint32 operateType = 6;\n}\n\nmessage SetMsgRecvType {\n  optional uint64 guildId = 1;\n  optional uint64 chanId = 2;\n  optional uint64 operatorId = 3;\n  optional uint32 msgNotifyType = 4;\n}\n\nmessage SetMute {\n  optional uint32 action = 1;\n  optional uint64 tinyID = 2;\n}\n\nmessage SetTop {\n  optional uint32 action = 1;\n}\n\nmessage SwitchDetail {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n  optional uint32 platform = 3;\n}\n\nmessage SwitchLiveRoom {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n  // optional uint64 roomId = 3;\n  // optional uint64 tinyid = 4;\n  optional SwitchLiveRoomUserInfo userInfo = 3;\n  optional uint32 action = 4; // JOIN = 1 QUIT = 2\n}\n\nmessage SwitchLiveRoomUserInfo {\n  optional uint64 tinyId = 1;\n  optional string nickname = 2;\n}\n\nmessage SwitchVoiceChannel {\n  optional uint64 memberId = 1;\n  optional SwitchDetail enterDetail = 2;\n  optional SwitchDetail leaveDetail = 3;\n}\n\nmessage UpdateCategory {\n  repeated CategoryInfo categoryInfo = 1;\n  optional CategoryInfo noClassifyCategoryInfo = 2;\n}\n\nmessage UpdateMsg {\n  optional uint64 msgSeq = 1;\n  optional bool origMsgUncountable = 2;\n  optional uint64 eventType = 3;\n  optional uint64 eventVersion = 4;\n  optional uint64 operatorTinyid = 5;\n  optional uint64 operatorRole = 6;\n  optional uint64 reason = 7;\n  optional uint64 timestamp = 8;\n}\n\nmessage UpdateVoiceBlockList {\n  optional uint32 action = 1;\n  optional uint64 objectTinyid = 2;\n}\n\nmessage VoiceChannelInfo {\n  optional uint32 memberMaxNum = 1;\n}\n\nmessage VoiceChannelInfoFilter {\n  optional uint32 memberMaxNum = 1;\n}\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/synclogic.proto",
    "content": "syntax = \"proto2\";\n\npackage guild;\n\nimport \"common.proto\";\n\nmessage ChannelMsg {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n  optional uint32 result = 3;\n  optional uint64 rspBeginSeq = 4;\n  optional uint64 rspEndSeq = 5;\n  repeated ChannelMsgContent msgs = 6;\n}\n\nmessage ChannelMsgReq {\n  optional ChannelParam channelParam = 1;\n  optional uint32 withVersionFlag = 2;\n  optional uint32 directMessageFlag = 3;\n}\n\nmessage ChannelMsgRsp {\n  optional uint32 result = 1;\n  optional bytes errMsg = 2;\n  optional ChannelMsg channelMsg = 3;\n  optional uint32 withVersionFlag = 4;\n  optional uint64 getMsgTime = 5;\n}\n\nmessage ChannelNode {\n  optional uint64 channelId = 1;\n  optional uint64 seq = 2;\n  optional uint64 cntSeq = 3;\n  optional uint64 time = 4;\n  optional uint64 memberReadMsgSeq = 5;\n  optional uint64 memberReadCntSeq = 6;\n  optional uint32 notifyType = 7;\n  optional bytes channelName = 8;\n  optional uint32 channelType = 9;\n  optional bytes meta = 10;\n  optional bytes readMsgMeta = 11;\n  optional uint32 eventTime = 12;\n}\n\nmessage ChannelParam {\n  optional uint64 guildId = 1;\n  optional uint64 channelId = 2;\n  optional uint64 beginSeq = 3;\n  optional uint64 endSeq = 4;\n  optional uint64 time = 5;\n  repeated uint64 version = 6;\n  repeated MsgCond seqs = 7;\n}\n\nmessage DirectMessageSource {\n  optional uint64 tinyId = 1;\n  optional uint64 guildId = 2;\n  optional bytes guildName = 3;\n  optional bytes memberName = 4;\n  optional bytes nickName = 5;\n}\n\nmessage FirstViewMsg {\n  optional uint32 pushFlag = 1;\n  optional uint32 seq = 2;\n  repeated GuildNode guildNodes = 3;\n  repeated ChannelMsg channelMsgs = 4;\n  optional uint64 getMsgTime = 5;\n  repeated GuildNode directMessageGuildNodes = 6;\n}\n\nmessage FirstViewReq {\n  optional uint64 lastMsgTime = 1;\n  optional uint32 udcFlag = 2;\n  optional uint32 seq = 3;\n  optional uint32 directMessageFlag = 4;\n}\n\nmessage FirstViewRsp {\n  optional uint32 result = 1;\n  optional bytes errMsg = 2;\n  optional uint32 seq = 3;\n  optional uint32 udcFlag = 4;\n  optional uint32 guildCount = 5;\n  optional uint64 selfTinyid = 6;\n  optional uint32 directMessageSwitch = 7;\n  optional uint32 directMessageGuildCount = 8;\n}\n\nmessage GuildNode {\n  optional uint64 guildId = 1;\n  optional uint64 guildCode = 2;\n  repeated ChannelNode channelNodes = 3;\n  optional bytes guildName = 4;\n  optional DirectMessageSource peerSource = 5;\n}\n\nmessage MsgCond {\n  optional uint64 seq = 1;\n  optional uint64 eventVersion = 2;\n}\n\nmessage MultiChannelMsg {\n  optional uint32 pushFlag = 1;\n  optional uint32 seq = 2;\n  repeated ChannelMsg channelMsgs = 3;\n  optional uint64 getMsgTime = 4;\n}\n\nmessage MultiChannelMsgReq {\n  repeated ChannelParam channelParams = 1;\n  optional uint32 seq = 2;\n  optional uint32 directMessageFlag = 3;\n}\n\nmessage MultiChannelMsgRsp {\n  optional uint32 result = 1;\n  optional bytes errMsg = 2;\n  optional uint32 seq = 3;\n}\n\nmessage ReqBody {\n  optional ChannelParam channelParam = 1;\n  optional uint32 directMessageFlag = 2;\n}\n\nmessage RspBody {\n  optional uint32 result = 1;\n  optional bytes errMsg = 2;\n  optional ChannelMsg channelMsg = 3;\n}\n\n"
  },
  {
    "path": "ricq-guild/src/protocol/protobuf/unknown.proto",
    "content": "// 存放所有未知的结构体, 均为手动分析复原\nsyntax = \"proto2\";\n\npackage guild;\n\n// see sub_37628C\nmessage ChannelOidb0xf5bRsp {\n  optional uint64 guildId = 1;\n  repeated GuildMemberInfo bots = 4;\n  repeated GuildMemberInfo members = 5;\n  optional uint32 nextIndex = 10;\n  optional uint32 finished = 9;\n  optional string nextQueryParam = 24;\n  repeated GuildGroupMembersInfo memberWithRoles = 25;\n  optional uint64 nextRoleIdIndex = 26;\n}\n\nmessage ChannelOidb0xf88Rsp {\n  optional GuildUserProfile profile = 1;\n}\n\nmessage ChannelOidb0xfc9Rsp {\n  optional GuildUserProfile profile = 1;\n}\n\nmessage ChannelOidb0xf57Rsp {\n  optional GuildMetaRsp rsp = 1;\n}\n\nmessage ChannelOidb0xf55Rsp {\n  optional GuildChannelInfo info = 1;\n}\n\nmessage ChannelOidb0xf5dRsp {\n  optional ChannelListRsp rsp = 1;\n}\n\nmessage ChannelOidb0x1017Rsp {\n  optional P10x1017 p1 = 1;\n}\n\nmessage P10x1017 {\n  optional uint64 tinyId = 1;\n  repeated GuildUserRole roles = 3;\n}\n\nmessage ChannelOidb0x1019Rsp {\n  optional uint64 guildId = 1;\n  repeated GuildRole roles = 2;\n  // 3: ?\n  // 4: \n}\n\n/*\nmessage ChannelOidb0x100dReq { // 修改身份组\n  optional uint64 guildId = 1;\n  repeated uint64 roleId = 2; \n  repeated int32 unkonwn = 3; // 3: ? 三个1\n  repeated ModifyGuildRole role = 4;\n}*/\n\n/*\nmessage ChannelOidb0x1016Req { // 新建身份组\n  optional uint64 guildId = 1;\n  repeated int32 unknown = 2; // 2: ? 三个1\n  optional ModifyGuildRole role = 3;\n  repeated uint64 initialUsers = 4;\n}*/\n\nmessage ChannelOidb0x1016Rsp {\n  optional uint64 roleId = 2;\n}\n\n/*\nmessage ChannelOidb0x101aReq { // 修改身份组\n  optional uint64 guildId = 1;\n  repeated SetGuildRole setRoles = 2;\n  repeated SetGuildRole removeRoles = 3;\n}*/\n\nmessage GuildMetaRsp {\n  optional uint64 guildId = 3;\n  optional GuildMeta meta = 4;\n}\n\nmessage ChannelListRsp {\n  optional uint64 guildId = 1;\n  repeated GuildChannelInfo channels = 2;\n  // 5: Category infos\n}\n\nmessage GuildGroupMembersInfo {\n  optional uint64 roleId = 1;\n  repeated GuildMemberInfo members = 2;\n  optional string roleName = 3;\n  optional uint32 color = 4;\n}\n\n// see sub_374334\nmessage GuildMemberInfo {\n  optional string title = 2;\n  optional string nickname = 3;\n  optional int64 lastSpeakTime = 4; // uncertainty\n  optional int32 role = 5; // uncertainty\n  optional uint64 tinyId = 8;\n}\n\n// 频道系统用户资料\nmessage GuildUserProfile {\n  optional uint64 tinyId = 2;\n  optional string nickname = 3;\n  optional string avatarUrl = 6;\n  // 15: avatar url info\n  optional int64 joinTime = 16;  // uncertainty\n  // 22 cards\n  // 23 display cards\n  // 25 current cards *uncertainty\n}\n\nmessage GuildRole {\n  optional uint64 roleId = 1;\n  optional string name = 2;\n  optional uint32 argbColor = 3;\n  optional int32 independent = 4;\n  optional int32 num = 5;\n  optional int32 owned = 6; // 是否拥有 存疑\n  optional int32 disabled = 7; // 权限不足或不显示\n  optional int32 maxNum = 8;\n  // 9: ?\n}\n\nmessage GuildUserRole {\n  optional uint64 roleId = 1;\n  optional string name = 2;\n  optional uint32 argbColor = 3;\n  optional int32 independent = 4;\n}\n\n/*\nmessage SetGuildRole {\n  optional uint64 roleId = 1;\n  optional uint64 targetId = 2;\n}*/\n\n/*\nmessage ModifyGuildRole {\n  optional string roleName = 1;\n  optional uint32 color = 2;\n  optional int32 independent = 3; // 身份组单独显示\n}*/\n\nmessage GuildMeta {\n  optional uint64 guildCode = 2;\n  optional int64 createTime = 4;\n  optional int64 maxMemberCount = 5;\n  optional int64 memberCount = 6;\n  optional string name = 8;\n  optional int32 robotMaxNum = 11;\n  optional int32 adminMaxNum = 12;\n  optional string profile = 13;\n  optional int64 avatarSeq = 14;\n  optional uint64 ownerId = 18;\n  optional int64 coverSeq = 19;\n  optional int32 clientId = 20;\n}\n\nmessage GuildChannelInfo {\n  optional uint64 channelId = 1;\n  optional string channelName = 2;\n  optional int64 creatorUin = 3;\n  optional int64 createTime = 4;\n  optional uint64 guildId = 5;\n  optional int32 finalNotifyType = 6;\n  optional int32 channelType = 7;\n  optional int32 talkPermission = 8;\n  // 11 - 14 : MsgInfo\n  optional uint64 creatorTinyId = 15;\n  // 16: Member info ?\n  optional int32 visibleType = 22;\n  optional GuildChannelTopMsgInfo topMsg = 28;\n  optional int32 currentSlowModeKey = 31;\n  repeated GuildChannelSlowModeInfo slowModeInfos = 32;\n}\n\nmessage GuildChannelSlowModeInfo {\n  optional int32 slowModeKey = 1;\n  optional int32 speakFrequency = 2;\n  optional int32 slowModeCircle = 3;\n  optional string slowModeText = 4;\n}\n\nmessage GuildChannelTopMsgInfo {\n  optional uint64 topMsgSeq = 1;\n  optional int64 topMsgTime = 2;\n  optional uint64 topMsgOperatorTinyId = 3;\n}\n/*\n// 个性档案卡片\nmessage GuildMemberProfileCard {\n  optional int32 appid = 1;\n  optional string name = 2;\n\n}\n */\n\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"nightly\""
  }
]