[
  {
    "path": ".clang-format",
    "content": "BasedOnStyle: Google\nIndentWidth: 4\nColumnLimit: 120\n---\nLanguage: C\nAccessModifierOffset: -4\nIndentPPDirectives: AfterHash\nIncludeBlocks: Preserve\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n*.py text eol=lf\n*.pyi text eol=lf\n*.pyx text eol=lf\n*.pxd text eol=lf\n*.h text eol=lf\n*.c text eol=lf\n*.cpp text eol=lf\n*.hpp text eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Bug报告\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**简要描述这个bug**\n\n...\n\n**如何复现**\n\n在何种场景下用何种操作复现\n\n**你希望程序作出何种行为**\n\n...\n\n**截图（可选）**\n\n...\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/discussion.md",
    "content": "---\nname: Discussion\nabout: 技术交流\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: 功能需求\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**我需要什么功能**\n\n...\n\n**我想将这个功能应用于何种场景**\n\n...\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\n\nupdates:\n  - package-ecosystem: \"pip\"\n    directory: \"/\"\n    target-branch: \"develop\"\n    schedule:\n      interval: \"daily\"\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    target-branch: \"develop\"\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/CI-beta.yml",
    "content": "name: CI-beta\n\non:\n  workflow_dispatch:\n\njobs:\n  test-beta:\n    name: Test-beta\n    runs-on: ubuntu-latest\n\n    environment: develop\n\n    env:\n      PYTHON_VERSION: \"3.15\"\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          ref: develop\n\n      - name: Setup UV\n        uses: astral-sh/setup-uv@v7\n\n      - name: Install dependencies\n        run: |\n          uv python pin ${{ env.PYTHON_VERSION }}\n          uv sync\n\n      - name: Run tests\n        env:\n          TB_BDUSS: ${{ secrets.BDUSS }}\n          TB_STOKEN: ${{ secrets.STOKEN }}\n        run: uv run pytest\n"
  },
  {
    "path": ".github/workflows/CI.yml",
    "content": "name: CI\n\non:\n  schedule:\n    - cron: \"42 6 * * *\"\n  push:\n    branches: [develop]\n    paths:\n      - \"src/**\"\n      - \"tests/**\"\n      - \".github/workflows/CI.yml\"\n  pull_request:\n    branches: [develop]\n    paths:\n      - \"src/**\"\n      - \"tests/**\"\n      - \".github/workflows/CI.yml\"\n  workflow_dispatch:\n\njobs:\n  test:\n    name: Test\n    runs-on: ubuntu-latest\n\n    environment: develop\n\n    strategy:\n      matrix:\n        python-version: [\"3.10\", \"3.14\"]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          ref: develop\n\n      - name: Setup UV\n        uses: astral-sh/setup-uv@v7\n\n      - name: Install dependencies\n        run: |\n          uv python pin ${{ matrix.python-version }}\n          uv sync\n\n      - name: Run tests\n        env:\n          TB_BDUSS: ${{ secrets.BDUSS }}\n          TB_STOKEN: ${{ secrets.STOKEN }}\n        run: uv run pytest\n"
  },
  {
    "path": ".github/workflows/Pages.yml",
    "content": "name: Pages\n\non:\n  push:\n    branches: [develop]\n    paths:\n      - \"mkdocs.yml\"\n      - \"docs/**\"\n      - \"pyproject.toml\"\n      - \".github/workflows/Pages*\"\n  pull_request:\n    branches: [develop]\n    paths:\n      - \"mkdocs.yml\"\n      - \"docs/**\"\n      - \"pyproject.toml\"\n      - \".github/workflows/Pages*\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          ref: develop\n\n      - name: Setup UV\n        uses: astral-sh/setup-uv@v7\n\n      - name: Install dependencies\n        run: uv sync --only-group docs\n\n      - name: Build\n        run: uv run mkdocs build -d site\n\n      - name: Upload Artifact\n        uses: actions/upload-pages-artifact@v5\n        with:\n          path: site\n\n  deploy:\n    needs: build\n\n    runs-on: ubuntu-latest\n\n    permissions:\n      pages: write\n      id-token: write\n\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v5\n"
  },
  {
    "path": ".github/workflows/Publish.yml",
    "content": "name: Publish\n\non:\n  push:\n    tags:\n      - \"*\"\n  workflow_dispatch:\n\njobs:\n  build_wheels:\n    name: Build wheels on ${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [\"ubuntu-latest\", \"windows-latest\", \"macos-latest\", \"macos-15-intel\"]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        \n      - name: Setup UV\n        uses: astral-sh/setup-uv@v7\n\n      - name: Build wheels\n        uses: pypa/cibuildwheel@v3.4.1\n\n      - uses: actions/upload-artifact@v7\n        with:\n          name: artifact-wheels-${{ matrix.os }}\n          path: ./wheelhouse/*.whl\n\n  build_sdist:\n    name: Build source distribution\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Build sdist\n        run: pipx run build --sdist\n\n      - uses: actions/upload-artifact@v7\n        with:\n          name: artifact-source\n          path: ./dist/*.tar.gz\n\n  publish:\n    name: Publish\n    needs: [build_wheels, build_sdist]\n    runs-on: ubuntu-latest\n\n    environment:\n      name: pypi\n\n    permissions:\n      id-token: write\n\n    steps:\n      - name: Merge artifacts\n        uses: actions/upload-artifact/merge@v7\n        with:\n          name: artifact\n          pattern: artifact-*\n          delete-merged: true\n\n      - name: Download artifacts\n        uses: actions/download-artifact@v8\n        with:\n          name: artifact\n          path: dist\n\n      - name: Publish to PyPI\n        if: ${{ github.event_name == 'push' }}\n        uses: pypa/gh-action-pypi-publish@release/v1\n\n      - name: Publish to TestPyPI\n        if: ${{ github.event_name == 'workflow_dispatch' }}\n        uses: pypa/gh-action-pypi-publish@release/v1\n        with:\n          repository-url: https://test.pypi.org/legacy/\n"
  },
  {
    "path": ".gitignore",
    "content": ".*/\n!.github/\n*_cache/\n*.py[cd]\n__pycache__\n\nlog/\ndist/\n*build*/\n\n.python-version\n*.lock\n\n/*.py\naccount.toml\ndatabase.toml\nupdate.md\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15 FATAL_ERROR)\n\nif (NOT SKBUILD_PROJECT_NAME)\n    set(SKBUILD_PROJECT_NAME \"aiotieba\")\nendif ()\n\nif (NOT SKBUILD_PROJECT_VERSION)\n    set(SKBUILD_PROJECT_VERSION 0)\nendif ()\n\nproject(${SKBUILD_PROJECT_NAME} VERSION ${SKBUILD_PROJECT_VERSION})\n\nfind_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module)\n\nadd_subdirectory(src/${SKBUILD_PROJECT_NAME}/helper/crypto)\n"
  },
  {
    "path": "LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n<a href=\"https://socialify.git.ci\">\n    <img src=\"https://user-images.githubusercontent.com/48282276/217530379-1348f7c5-7056-48f4-8c64-1c74caf5497c.svg\">\n</a>\n</p>\n\n<div align=\"center\">\n<p>\n<a href=\"https://github.com/lumina37/aiotieba/actions\">\n    <img src=\"https://img.shields.io/github/actions/workflow/status/lumina37/aiotieba/CI.yml?branch=develop&label=CI&logo=github&style=flat-square\" alt=\"GitHub Workflow Status\">\n</a>\n<a href=\"https://pypi.org/project/aiotieba\">\n    <img src=\"https://img.shields.io/pypi/v/aiotieba?color=g&style=flat-square\" alt=\"PyPI - Version\">\n</a>\n<a href=\"https://pypi.org/project/aiotieba\">\n    <img src=\"https://img.shields.io/pypi/pyversions/aiotieba?style=flat-square\" alt=\"PyPI - Python Version\">\n</a>\n</p>\n</div>\n\n---\n\n## 安装\n\n```shell\npip install aiotieba\n```\n\n## 尝试一下\n\n```python\nimport asyncio\n\nimport aiotieba\n\n\nasync def main():\n    async with aiotieba.Client() as client:\n        threads = await client.get_threads(\"天堂鸡汤\")\n        for thread in threads[3:6]:\n            print(f\"tid={thread.tid}\\ntext={thread.text}\")\n\n\nasyncio.run(main())\n```\n\n*输出样例*\n\n```log\ntid=8537603600\ntext=一人发一句最喜欢的游戏台词\n楼主先来\n很喜欢lol布隆说的“夜晚越黑暗，星星就越明亮”，尤其在当下这个有着诸多缺点的世界里，这句话让我感觉舒服了很多。\n在人们已不再相信理想主义的至暗时刻，高擎炬火之人便显得更加重要，至少我会坚持我的理想\n\ntid=8093410706\ntext=大概是剪切板里的一些有意思的话\n今天看自己的剪切板快满了，稍微翻翻突然发现以前存的一些话还挺有意思，就放在这里啦\n（咦，疑似水帖啊我）\n\ntid=8537699088\ntext=记录一下自己人生第一次当“老师”的经历^_^\n明天我带的孩子们就“毕业”了，第一次当老师我改变了很多也收获了很多，就想着给自己记录一下这段宝贵的经历:-)\n```\n\n继续阅读[**入门教程**](https://aiotieba.cc/tutorial/start)\n\n## 项目特色\n\n+ 收录[**数十个常用API**](https://github.com/lumina37/aiotieba/tree/develop/aiotieba/api)\n+ 类型注解全覆盖，方法注释全覆盖，内部命名统一\n+ 支持protobuf序列化请求参数\n+ 支持websocket接口\n+ 与官方版本高度一致的密码学实现\n\n## 友情链接\n\n+ [带UI的吧务管理器 (dog194/TiebaManager)](https://github.com/dog194/TiebaManager)\n+ [VSCode贴吧摸鱼插件 (akacaijizhou/tieba-fish)](https://github.com/akacaijizhou/tieba-fish)\n+ [eztb贴吧工具箱 (Dilettante258/eazy-tieba)](https://www.eztb.org)\n+ [功能全面的贴吧管理QQ bot (TiebaMeow/TiebaManageBot)](https://github.com/TiebaMeow/TiebaManageBot)\n+ [易于部署和使用的 Web 贴吧管理和自动化平台 (TiebaMeow/WebTiebaManager)](https://github.com/TiebaMeow/WebTiebaManager)\n+ [灵活且高可靠的贴吧爬虫 (TiebaMeow/TiebaScraper)](https://github.com/TiebaMeow/TiebaScraper)\n+ [TiebaLite 第三方安卓客户端 (zzc10086/TiebaLite)](https://github.com/zzc10086/TiebaLite)\n+ [C#版本的贴吧接口库 (BaWuZhuShou/AioTieba4DotNet)](https://github.com/BaWuZhuShou/AioTieba4DotNet)\n+ [基于aiotieba的tieba bot (adk23333/BungleCat)](https://github.com/adk23333/BungleCat)\n+ [基于aiotieba的贴吧管理器 (adk23333/tieba-admin)](https://github.com/adk23333/tieba-admin)\n+ [贴吧protobuf定义文件合集 更新至12.51 (n0099/tbclient.protobuf)](https://github.com/n0099/tbclient.protobuf)\n\n## 特别鸣谢\n\n<p align=\"center\">\n<a href=\"https://jb.gg/OpenSourceSupport\">\n    <img src=\"https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg\">\n</a>\n</p>\n\n为本开源项目提供的免费产品授权\n"
  },
  {
    "path": "docs/CNAME",
    "content": "aiotieba.cc"
  },
  {
    "path": "docs/css/custom.css",
    "content": "[data-md-color-scheme=slate] {\n  --md-hue: 210;\n  --md-default-bg-color: hsla(var(--md-hue), 15%, 10%, 1);\n  --md-typeset-a-color: hsla(var(--md-hue), 75%, 50%, 1);\n}\n"
  },
  {
    "path": "docs/index.md",
    "content": "# \n\n<p align=\"center\">\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://user-images.githubusercontent.com/48282276/217530379-1348f7c5-7056-48f4-8c64-1c74caf5497c.svg\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"https://user-images.githubusercontent.com/48282276/217530385-98a2fb24-ff6e-4b27-990f-998b66c2ab5e.svg\">\n  <img src=\"https://user-images.githubusercontent.com/48282276/217530385-98a2fb24-ff6e-4b27-990f-998b66c2ab5e.svg\">\n</picture>\n</p>\n\n<p align=\"center\">\n<a href=\"https://github.com/lumina37/aiotieba/actions\">\n    <img src=\"https://img.shields.io/github/actions/workflow/status/lumina37/aiotieba/CI.yml?branch=develop&label=CI&logo=github&style=flat-square\" alt=\"GitHub Workflow Status\">\n</a>\n<a href=\"https://pypi.org/project/aiotieba\">\n    <img src=\"https://img.shields.io/pypi/v/aiotieba?color=g&style=flat-square\" alt=\"PyPI - Version\">\n</a>\n<a href=\"https://pypi.org/project/aiotieba\">\n    <img src=\"https://img.shields.io/pypi/pyversions/aiotieba?style=flat-square\" alt=\"PyPI - Python Version\">\n</a>\n</p>\n\n---\n\n## 安装\n\n```shell\npip install aiotieba\n```\n\n## 尝试一下\n\n```python\nimport asyncio\n\nimport aiotieba\n\n\nasync def main():\n    async with aiotieba.Client() as client:\n        threads = await client.get_threads(\"天堂鸡汤\")\n        for thread in threads[3:6]:\n            print(f\"tid={thread.tid} text={thread.text}\")\n\n\nasyncio.run(main())\n```\n\n*输出样例*\n\n```log\ntid=8537603600 text=一人发一句最喜欢的游戏台词\n楼主先来\n很喜欢lol布隆说的“夜晚越黑暗，星星就越明亮”，尤其在当下这个有着诸多缺点的世界里，这句话让我感觉舒服了很多在人们已不再相信理想主义的至暗时刻，高擎炬火之人便显得更加重要，至少我会坚持我的理想\n---\ntid=8093410706 text=大概是剪切板里的一些有意思的话\n今天看自己的剪切板快满了，稍微翻翻突然发现以前存的一些话还挺有意思，就放在这里啦\n（咦，疑似水帖啊我）\n---\ntid=8537699088 text=记录一下自己人生第一次当“老师”的经历^_^\n明天我带的孩子们就“毕业”了，第一次当老师我改变了很多也收获了很多，就想着给自己记录一下这段宝贵的经历:-)\n```\n\n继续阅读[**入门教程**](https://aiotieba.cc/tutorial/start)\n\n## 项目特色\n\n+ 收录[**数十个常用API**](https://github.com/lumina37/aiotieba/tree/develop/aiotieba/api)\n+ 类型注解全覆盖，方法注释全覆盖，类属性注释全覆盖，内部命名统一\n+ 请求参数支持protobuf序列化\n+ 支持websocket接口\n+ 高一致性的密码学实现\n"
  },
  {
    "path": "docs/ref/classdef/ats.md",
    "content": "::: aiotieba.api.get_ats._classdef\n"
  },
  {
    "path": "docs/ref/classdef/bawu_blacklist.md",
    "content": "::: aiotieba.api.get_bawu_blacklist._classdef\n"
  },
  {
    "path": "docs/ref/classdef/bawu_info.md",
    "content": "::: aiotieba.api.get_bawu_info._classdef\n"
  },
  {
    "path": "docs/ref/classdef/bawu_perm.md",
    "content": "::: aiotieba.api.get_bawu_perm._classdef\n"
  },
  {
    "path": "docs/ref/classdef/bawu_postlogs.md",
    "content": "::: aiotieba.api.get_bawu_postlogs._classdef\n"
  },
  {
    "path": "docs/ref/classdef/bawu_userlogs.md",
    "content": "::: aiotieba.api.get_bawu_userlogs._classdef\n"
  },
  {
    "path": "docs/ref/classdef/blacklist.md",
    "content": "::: aiotieba.api.get_blacklist._classdef\n"
  },
  {
    "path": "docs/ref/classdef/blacklist_old.md",
    "content": "::: aiotieba.api.get_blacklist_old._classdef\n"
  },
  {
    "path": "docs/ref/classdef/blocks.md",
    "content": "::: aiotieba.api.get_blocks._classdef\n"
  },
  {
    "path": "docs/ref/classdef/comments.md",
    "content": "::: aiotieba.api.get_comments._classdef\n"
  },
  {
    "path": "docs/ref/classdef/dislike_forums.md",
    "content": "::: aiotieba.api.get_dislike_forums._classdef\n"
  },
  {
    "path": "docs/ref/classdef/fans.md",
    "content": "::: aiotieba.api.get_fans._classdef\n"
  },
  {
    "path": "docs/ref/classdef/follow_forums.md",
    "content": "::: aiotieba.api.get_follow_forums._classdef\n"
  },
  {
    "path": "docs/ref/classdef/follows.md",
    "content": "::: aiotieba.api.get_follows._classdef\n"
  },
  {
    "path": "docs/ref/classdef/forum_detail.md",
    "content": "::: aiotieba.api.get_forum_detail._classdef\n"
  },
  {
    "path": "docs/ref/classdef/group_msg.md",
    "content": "::: aiotieba.api.get_group_msg._classdef\n"
  },
  {
    "path": "docs/ref/classdef/images.md",
    "content": "::: aiotieba.api.get_images._classdef\n"
  },
  {
    "path": "docs/ref/classdef/last_replyers.md",
    "content": "::: aiotieba.api.get_last_replyers._classdef\n"
  },
  {
    "path": "docs/ref/classdef/member_users.md",
    "content": "::: aiotieba.api.get_member_users._classdef\n"
  },
  {
    "path": "docs/ref/classdef/posts.md",
    "content": "::: aiotieba.api.get_posts._classdef\n"
  },
  {
    "path": "docs/ref/classdef/profile.md",
    "content": "::: aiotieba.api.profile._classdef\n"
  },
  {
    "path": "docs/ref/classdef/rank_users.md",
    "content": "::: aiotieba.api.get_rank_users._classdef\n"
  },
  {
    "path": "docs/ref/classdef/recom_status.md",
    "content": "::: aiotieba.api.get_recom_status._classdef\n"
  },
  {
    "path": "docs/ref/classdef/recover_thread.md",
    "content": "::: aiotieba.api.get_recover_info._classdef\n"
  },
  {
    "path": "docs/ref/classdef/recovers.md",
    "content": "::: aiotieba.api.get_recovers._classdef\n"
  },
  {
    "path": "docs/ref/classdef/replys.md",
    "content": "::: aiotieba.api.get_replys._classdef\n"
  },
  {
    "path": "docs/ref/classdef/searches.md",
    "content": "::: aiotieba.api.search_exact._classdef\n"
  },
  {
    "path": "docs/ref/classdef/self_follow_forums.md",
    "content": "::: aiotieba.api.get_self_follow_forums._classdef\n"
  },
  {
    "path": "docs/ref/classdef/square_forums.md",
    "content": "::: aiotieba.api.get_square_forums._classdef\n"
  },
  {
    "path": "docs/ref/classdef/statistics.md",
    "content": "::: aiotieba.api.get_statistics._classdef\n"
  },
  {
    "path": "docs/ref/classdef/threads.md",
    "content": "::: aiotieba.api.get_threads._classdef\n"
  },
  {
    "path": "docs/ref/classdef/unblock_appeals.md",
    "content": "::: aiotieba.api.get_unblock_appeals._classdef\n"
  },
  {
    "path": "docs/ref/classdef/user_contents.md",
    "content": "::: aiotieba.api.get_user_contents._classdef\n"
  },
  {
    "path": "docs/ref/classdef/user_info.md",
    "content": "::: aiotieba.api.tieba_uid2user_info._classdef\n::: aiotieba.api.get_uinfo_getuserinfo_app._classdef\n::: aiotieba.api.get_uinfo_getUserInfo_web._classdef\n::: aiotieba.api.get_uinfo_user_json._classdef\n::: aiotieba.api.get_uinfo_panel._classdef\n"
  },
  {
    "path": "docs/ref/client.md",
    "content": "# 客户端\n\n## 如何使用\n\n`aiotieba.Client`是aiotieba的核心入口点 (Entry Point)，其中封装了大量操作百度贴吧核心API的简便方法，你可以把它理解成一个“客户端”\n\n我们推荐通过异步上下文管理器来使用`Client`，例如：\n\n```python\nasync with aiotieba.Client() as client:\n    ...\n```\n\n## Client\n\n::: aiotieba.Client"
  },
  {
    "path": "docs/ref/config.md",
    "content": "# 超时配置\n\n::: aiotieba.config\n"
  },
  {
    "path": "docs/ref/enums.md",
    "content": "# 枚举\n\n::: aiotieba.enums\n"
  },
  {
    "path": "docs/ref/exception.md",
    "content": "# 异常处理\n\n::: aiotieba.exception\n"
  },
  {
    "path": "docs/tutorial/async_start.md",
    "content": "# 异步编程入门教程\n\n## 样例代码\n\n本样例将获取并打印吧主题帖列表\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\n# [2] 异步函数——`async`关键字\nasync def main():\n    # [6] `async with`是什么？\n    async with tb.Client() as client:\n        # [1] CPU在何时离开？——`await`关键字\n        threads = await client.get_threads(\"天堂鸡汤\")\n\n    print(threads)\n\n\n# [4] CPU在何时返回？——事件循环\n# 官方文档：运行asyncio程序\n# https://docs.python.org/zh-cn/3/library/asyncio-task.html#running-an-asyncio-program\nasyncio.run(main())\n```\n\n## 样例解析\n\n### 什么是异步\n\n在计算机中，CPU的速度普遍要比网络IO的速度快几个数量级。为了不让网络IO成为系统性能的瓶颈，我们会希望CPU等高速设备不要原地等待网卡慢悠悠地传输数据，而是可以把IO缓冲区交给网卡芯片就立刻离开，暂时转去执行一些其他任务，这其中就体现了**异步**（Asynchronous）的思想。\n\n*Asynchronous*的前缀*a-*意为*not*，*syn*意为*together*，*chrono*源自古希腊语*khronos*，意为*time*，*-ous*为形容词后缀，合起来就是*not-together-time*，不同时发生的。在计算机领域，*Asynchronous*常常用于形容“多个事件不在同一时间发生”。这里的“同一时间”所强调的并不是时间长短，而是**逻辑上的连贯性**。\n\n应用了异步技术后，在网络请求发出后到网络响应到来前的这段时间，CPU可以暂时离开，切换到其他地方去处理另外的工作。这种逻辑切换使得网络IO的请求事件与响应事件，相对于CPU及其他相关的高速设备而言，不在同一个**时间**发生。*Asynchronous*的词源与其术语含义高度匹配，值得反复品读。\n\n### CPU在何时离开？——`await`关键字\n\n那么Python是如何实现异步的？在抛出一大堆错综复杂的概念来回答这个问题之前，你可以先带着一个更小的子问题来阅读下面的文章——CPU在何时离开？让我们先来看样例。\n\n在样例`threads = await client.get_threads()`中，`client.get_threads`是一个**异步函数**。它和同步函数类似，都是可调对象（Callable）。`client.get_threads()`调用（call）了这个异步函数，调用异步函数不会像调用同步函数那样返回结果，而是会返回一个“施工方案”，也就是**可等待对象**（`Awaitable`）。此时，在可等待对象`client.get_threads()`的左边，那个至关重要的关键字`await`出现了。\n\n*await*意为“等待”，往往用于表达“以被动的姿态等待某事的发生”，且暗含期待之意。在`threads = await client.get_threads()`中，`await`执行`client.get_threads()`返回的施工方案，并要求上一级执行过程`await main()`必须等待`client.get_threads()`给出执行结果`threads`后，再继续施工。\n\n可能有初学者会迷惑于一个点，`client.get_threads()`会在何时执行？在创建时，还是在受到`await`调度时？下面这个简单的例子可以解答你的疑惑。\n\n```python\nimport asyncio\n\n\nasync def foo():\n    print(\"1 - foo_coro is executing\")\n\n\nasync def main():\n    foo_coro = foo()\n    print(\"0 - foo_coro has not been executed!!!\")\n    await foo_coro\n\n\nasyncio.run(main())\n```\n\n输出结果\n\n```log\n0 - foo_coro has not been executed!!!\n1 - foo_coro is executing\n```\n\n这说明`foo_coro`并不会在创建时立即执行，施工流程的创建（`foo_coro = foo()`）和执行（`await foo_coro`）是可以分开的。\n\n`main()`的等待行为对应于一个计算机术语**“挂起”**（suspend），后面我们都会使用这个术语来替代“暂停”等口语化的词汇。*suspend*与*pause*意思相近，但他们之间有着微妙的区别——*suspend*往往表示较长时间的暂停，譬如在快节奏游戏中的暂停我们通常会说*pause*而不是*suspend*。\n\n思考一个问题，在`await`关键字的指挥下做出等待行为的是谁？正确答案是`main()`的执行过程，而不是`main()`或者`main`，更不应该是`client.get_threads()`或者CPU。这一套概念辨析可不是什么无用的八股，它特别有助于加深我们对Python异步的理解。施工方案`main()`只是一个可等待对象（`Awaitable`），同样的，`main`也只是一个可调对象（`Callable`）。对象可没有“等待”、“暂停”的说法——“暂停一个对象”？你应该会觉得这句话十分诡异。只有施工方案的执行过程，也就是`main()`的执行过程可以有“暂停施工”、“等待某个任务完成再继续施工”的说法。\n\n在这一小节的最后，我相信各位读者已经能够从具体到抽象，自行总结出`await`的含义——Python中`await`关键字的作用就是执行右侧的可等待对象，并让其当前所处的执行流程挂起以等待右侧的可等待对象给出结果。在本节开头提出的问题也可以解答了，在利用`await`等待一个可等待对象时，如果不能立即获得结果，CPU就会离开。\n\n### 异步函数——`async`关键字\n\n在Python中，`async`关键字被用于将函数标记为异步的。不论函数体内是否需要等待（`await`），添加了`async`标记的函数都会返回一个可等待对象。\n\n### 什么是协程\n\n**协程**（`Coroutine`）就是可以在中途挂起和恢复执行的函数流程。调用异步函数`main`所得到的可等待对象`main()`就是一个协程。\n\n### CPU在何时返回？——事件循环\n\n回调函数（callback function）是这样一种函数：客户需要到柜台取货，但他又不想一直在柜台干等，就把自己的电话号码（回调函数）交给柜台，让柜台在有货之后打电话（执行回调函数）通知他来取。\n\n**事件循环**（`EventLoop`），其中*event*意为事件，*loop*意为循环，就是用来获取事件通知，调度协程执行的循环体。事件循环在每次循环中都会做以下工作：将新增的定时任务添加到优先级队列；将已到执行时间的定时任务的回调函数添加到待执行回调函数列表；从操作系统获取事件通知（比如网卡通过硬件中断通知系统：某个socket有数据到来）并将读/写缓冲区的回调函数添加到待执行列表；最后，执行所有待执行的回调函数。CPU会在“执行回调函数”这一步骤返回先前被挂起的正在等待IO事件的协程，恢复他们的执行。\n\n`asyncio.run`将会使用一个全局事件循环（不存在则新建）来执行作为参数的协程。`asyncio.run(main())`也就是在全局事件循环中执行协程`main()`。从`main()`到`client.get_threads()`一路往下执行，最终抵达一个底层协程，它将一个用于网络通信的socket注册到操作系统内核，然后立即返回一个`Future`对象，表示一个将要到来的结果，由于我们`await`了这个`Future`，调用链上所有的协程都被挂起，直到事件循环从操作系统获取到一个匹配的可读事件后，事件循环再执行对应读缓冲区的回调函数将协程唤醒并继续执行。\n\n### 为什么`await`只能在异步函数中使用？\n\n协程有多种实现方式，而Python实现的是一种沿用了生成器机制的无栈协程。沿用生成器机制，意味着协程的上下文和生成器一样，被保存在一个对象中；无栈，意味着Python协程的调用链并不是一个栈结构，而是一个酷似链表的结构，最开始被调用的协程为链表头，通过其保存的上下文一路指向最底层的协程。而与无栈协程相对的有栈协程则与线程十分类似，不论同步函数的调用还是异步函数的调用都共享一套调用栈，因此有栈协程可以像线程一样在任何位置挂起，而无栈协程只能在特定位置（如`await`关键字标记的地方）挂起。无栈协程的一大“丑陋”之处就是`await`只能在异步函数中使用，JavaScript/Rust/C++的无栈协程都是如此，这导致如果你要使用异步特性，就必须将`async def`铺满整个调用链。\n\n现在我们来回答标题提出的问题，如果在同步函数的调用过程中使用了`await`来等待异步结果，由于调用同步函数的返回值不是协程，不会在其中保存上下文信息，这导致我们无法在同步函数中找到那个应该恢复执行的正确位置。但如果我就是要在同步函数中记录应当恢复执行的正确位置呢？很好，那么你将实现一个有栈协程，go语言正是如此。\n\n### `async with`是什么？\n\n也就是一个异步版本的上下文生成器，在使用`__init__`初始化对象之后使用对象的异步方法`__aenter__`进行异步初始化工作（比如创建连接池），使用异步方法`__aexit__`进行异步清理工作（比如关闭所有连接）。\n"
  },
  {
    "path": "docs/tutorial/many_utils.md",
    "content": "# 实用工具\n\n## 签到\n\n```python\nfrom __future__ import annotations\n\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def sign(BDUSS_key: str, *, retry_times: int = 0):\n    \"\"\"\n    各种签到\n\n    Args:\n        BDUSS (str): 用于创建客户端\n        retry_times (int, optional): 重试次数. Defaults to 0.\n    \"\"\"\n\n    async with tb.Client(BDUSS_key) as client:\n        # 成长等级签到\n        for _ in range(retry_times):\n            await asyncio.sleep(1.0)\n            if await client.sign_growth():\n                break\n        # 签到\n        await client.sign_forums()  # 先一键签到\n        retry_list: list[str] = []\n        for pn in range(1, 9999):\n            forums = await client.get_self_follow_forums(pn)\n            retry_list += [forum.fname for forum in forums if not forum.is_signed]\n            if not forums.has_more:\n                break\n        for _ in range(retry_times + 1):\n            new_retry_list: list[str] = []\n            for fname in retry_list:\n                ret = await client.sign_forum(fname)\n                if ret.err is not None and ret.err.code not in [160002, 340006]:\n                    new_retry_list.append(fname)\n                await asyncio.sleep(1.0)\n            if not new_retry_list:\n                break\n            retry_list = new_retry_list\n\n\nasync def main():\n    await sign(\"在此处输入待签到账号的BDUSS\", retry_times=3)\n    await sign(\"在此处输入另一个待签到账号的BDUSS\", retry_times=3)\n\n\nasyncio.run(main())\n```\n\n## 批量封禁\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client(\"在此处输入你的BDUSS\") as client:\n        for uid in [\n            1111,\n            2222,\n            3333,\n        ]:\n            await client.block(\"xxx\", uid, day=10)\n\n\nasyncio.run(main())\n```\n\n## 将个人主页的帖子全部设为隐藏\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client(\"在此处输入你的BDUSS\") as client:\n        # 海象运算符(:=)会在创建threads变量并赋值的同时返回该值，方便while语句检查其是否为空\n        # 更多信息请搜索“Python海象运算符”\n        while threads := await client.get_user_threads():\n            await asyncio.gather(*[client.set_thread_private(thread.fid, thread.tid, thread.pid) for thread in threads])\n\n\nasyncio.run(main())\n```\n\n## 屏蔽贴吧，使它们不再出现在你的首页推荐里\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client(\"在此处输入你的BDUSS\") as client:\n        await asyncio.gather(*[\n            client.dislike_forum(fname)\n            for fname in [\n                \"贴吧名A\",\n                \"贴吧名B\",\n                \"贴吧名C\",\n            ]  # 把你要屏蔽的贴吧名填在这个列表里\n        ])\n\n\nasyncio.run(main())\n```\n\n## 解除多个贴吧的屏蔽状态\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client(\"在此处输入你的BDUSS\") as client:\n        # 此列表用于设置例外\n        # 将你希望依然保持屏蔽的贴吧名填在这个列表里\n        preserve_fnames = [\n            \"保持屏蔽的贴吧名A\",\n            \"保持屏蔽的贴吧名B\",\n            \"保持屏蔽的贴吧名C\",\n        ]\n        while 1:\n            forums = await client.get_dislike_forums()\n            await asyncio.gather(*[\n                client.undislike_forum(forum.fid) for forum in forums if forum.fname not in preserve_fnames\n            ])\n            if not forums.has_more:\n                break\n\n\nasyncio.run(main())\n```\n\n## 清除旧版乱码昵称\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client(\"在此处输入你的BDUSS\") as client:\n        user = await client.get_self_info(tb.ReqUInfo.USER_NAME)\n        await client.set_nickname_old(user.user_name)\n\n\nasyncio.run(main())\n```\n\n## 清空粉丝列表（无法复原的危险操作，请谨慎使用！）\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client(\"在此处输入你的BDUSS\") as client:\n        while fans := await client.get_fans():\n            await asyncio.gather(*[client.remove_fan(fan.user_id) for fan in fans])\n\n\nasyncio.run(main())\n```\n\n## 清除所有历史回复（无法复原的危险操作，请谨慎使用！）\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client(\"在此处输入你的BDUSS\") as client:\n        while posts_list := await client.get_user_posts():\n            await asyncio.gather(*[\n                client.del_post(post.fid, post.tid, post.pid) for posts in posts_list for post in posts\n            ])\n\n\nasyncio.run(main())\n```\n"
  },
  {
    "path": "docs/tutorial/start.md",
    "content": "# 入门教程\n\n阅读本教程，你至少需要对Python的 **上下文管理器** `with...as...` 和 **迭代器** `for...in...` 有基本的印象，因为本文不会对这些重要的基本概念做过于深入的解释\n\n推荐在本地新建一个`.py`文件来运行样例\n\n## 命名约定\n\n贴吧服务端使用以下名称表示特定的数据\n\n### BDUSS\n\n贴吧服务端使用BDUSS来确认用户身份\n\nBDUSS是一串由纯ascii字符组成的，长度为192的字符串\n\n!!! warning\n\n    使用BDUSS可以完成**一切**不需要手机/邮箱验证码的操作，包括**发帖**/**发私信**/**获取账号上的所有历史发言**\n\n    BDUSS的过期时间长达数年，一般只能通过退出登录或修改密码使其失效\n\n    因此将BDUSS泄露给不受信任的人可能导致长期的账号安全风险和隐私泄露风险\n\n在浏览器的Cookie和各种表单参数中你都能看到它的身影\n\n搜索 你的浏览器型号+如何查看网站的Cookie 就能知道如何获取你的贴吧账号的BDUSS了\n\n以Chrome为例，在任何一个贴吧网页下按<kbd>F12</kbd>调出开发者选项，然后你就能在下图的位置找到它\n\n![Chrome Cookie](https://user-images.githubusercontent.com/48282276/179938990-77139ea2-2d94-4d38-8d7d-9c6a3d99b69e.png)\n\n### user_name\n\n用户名\n\nuser_name唯一，但可变，且可以是空值\n\n请注意与nick_name相区分\n\n### portrait\n\n头像ID\n\n每个贴吧用户都有且仅有一个portrait\n\nportrait是一串由纯ascii字符组成的，以tb.1.作为开头的，长度为33~36的字符串（仅有一些远古时期的ip账号不符合这个规则）\n\n譬如我的portrait就是tb.1.8277e641.gUE2cTq4A4z5fi2EHn5k3Q\n\n你可以通过portrait获取用户头像，例如[我的头像](http://tb.himg.baidu.com/sys/portraith/item/tb.1.8277e641.gUE2cTq4A4z5fi2EHn5k3Q)\n\n### user_id\n\n用户ID\n\nuser_id唯一，不可变，不能为空\n\n请注意将其与用户个人主页的tieba_uid相区分\n\nuser_id是一个uint64值（仅有一些远古时期的ip账号不符合这个规则）\n\nuser_name portrait user_id 都是满足唯一性的用户标识符，并可以通过其中任意一个的值反查其余两个\n\n### tieba_uid\n\n用户个人主页ID\n\ntieba_uid唯一，不可变，但可以为空\n\n请注意将其与用户的user_id相区分\n\ntieba_uid是一个uint64值\n\n可以通过tieba_uid的值反查user_name portrait user_id\n\n### forum_id\n\n吧ID，简称fid\n\n每个贴吧都有且仅有一个fid\n\n### thread_id\n\n主题帖ID，简称tid\n\n每个主题帖都有且仅有一个tid\n\n### post_id\n\n回复ID，简称pid\n\n每个楼层、楼中楼都有且仅有一个pid\n\n## 关于异步编程\n\n如果你不了解Python异步编程，请先阅读[异步编程入门教程](async_start.md)\n\n## 迈出第一步\n\n一个非常简单的入门案例\n\n### 样例代码\n\n本样例将获取并打印当前账号的用户信息\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\nBDUSS = \"在这里输入你账号的BDUSS\"\n\n\nasync def main():\n    async with tb.Client(BDUSS) as client:\n        user = await client.get_self_info()\n\n    print(user)\n\n\nasyncio.run(main())\n```\n\n### 期望结果\n\n如果你的[`BDUSS`](#bduss)填写无误，你会获得类似下面这样的结果\n\n```log\nAAAA（你的用户名）\n```\n\n## 内容的层次结构\n\n本样例将协助你理解“主题帖-回复-楼中楼”的三级层次结构，以及如何解析富媒体内容\n\n### 样例代码\n\n本样例将逐级获取并打印“主题帖-回复-楼中楼”中各层级的部分内容\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client() as client:\n        threads = await client.get_threads(\"天堂鸡汤\")\n        for thread in threads[3:6]:\n            print(thread)  # 打印整个主题帖\n            print(thread.contents)  # 打印主题帖中的内容碎片（含富媒体信息）\n            print(thread.contents.emojis)  # 仅打印表情相关的内容碎片\n\n        selected_thread = threads[4]\n        posts = await client.get_posts(selected_thread.tid)\n        for post in posts[3:6]:\n            print(post)  # 打印整个回复\n            print(post.contents)  # 打印回复中的内容碎片\n            print(post.contents.imgs)  # 仅打印图片相关的内容碎片\n\n        for post in posts:\n            if post.reply_num == 0:\n                continue\n            comments = await client.get_comments(post.tid, post.pid)\n            for comment in comments:\n                print(comment)  # 打印整个楼中楼\n                print(comment.contents.ats)  # 仅打印@相关的内容碎片\n                break\n\n\nasyncio.run(main())\n```\n\n## 运行时更改BDUSS\n\n该案例演示了如何在运行时更改BDUSS\n\n建议为每个账号新建`Client`，以避免误用遗留的websocket连接\n\n同样地，你也可以直接向`Client.account`赋值以动态变更用户参数\n\n### 样例代码\n\n本样例将获取并打印当前账号的用户信息\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\n\nasync def main():\n    async with tb.Client() as client:\n        client.account.BDUSS = \"在这里输入你账号的BDUSS\"\n        user = await client.get_self_info()\n\n    print(user)\n\n\nasyncio.run(main())\n```\n\n### 期望结果\n\n如果你的[`BDUSS`](#bduss)填写无误，你会获得类似下面这样的结果\n\n```log\nAAAA（你的用户名）\n```\n\n## 多账号\n\n如何同时使用多个账号？\n\n### 样例代码\n\n本样例将获取并打印多个账号的用户信息\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\nBDUSS1 = \"在这里输入第一个账号的BDUSS\"\nBDUSS2 = \"在这里输入第二个账号的BDUSS\"\n\n\nasync def main():\n    async with tb.Client(BDUSS1) as client1, tb.Client(BDUSS2) as client2:\n        user1 = await client1.get_self_info()\n        user2 = await client2.get_self_info()\n        print(f\"账号1: {user1}, 账号2: {user2}\")\n\n\nasyncio.run(main())\n```\n\n### 期望结果\n\n如果你的[`BDUSS`](#bduss)填写无误，你会获得类似下面这样的结果\n\n```log\n账号1: AAAA, 账号2: BBBB\n```\n\n## Account的序列化与反序列化\n\n该功能可以用于导出账号参数\n\n### 样例代码\n\n本样例将演示账号参数的导出与导入，并使用导入生成的Account获取用户信息\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\nBDUSS = \"在这里输入你账号的BDUSS\"\n\n\nasync def main():\n    account1 = tb.Account(BDUSS)\n    dic = account1.to_dict()\n    print(dic)\n    account2 = tb.Account.from_dict(dic)\n    assert account1.BDUSS == account2.BDUSS\n\n    async with tb.Client(account=account2) as client:\n        user = await client.get_self_info()\n\n    print(user)\n\n\nasyncio.run(main())\n```\n\n### 期望结果\n\n如果你的[`BDUSS`](#bduss)填写无误，你会获得类似下面这样的结果\n\n```log\n{'BDUSS': '...'}\nAAAA（你的用户名）\n```\n\n## 简单并发爬虫\n\n### 样例代码\n\n本样例将同时请求用户个人信息和天堂鸡汤吧首页前30帖，并将他们打印出来\n\n```python\nimport asyncio\n\nimport aiotieba as tb\n\nBDUSS = \"在这里输入你账号的BDUSS\"\n\n\nasync def main():\n    async with tb.Client(BDUSS) as client:\n        # [1] 什么是`asyncio.gather`？\n        # 参考官方文档：并发运行任务\n        # https://docs.python.org/zh-cn/3/library/asyncio-task.html#running-tasks-concurrently\n        user, threads = await asyncio.gather(client.get_self_info(), client.get_threads(\"天堂鸡汤\"))\n\n    # 将获取的信息打印到日志\n    print(f\"当前用户: {user}\")\n    for thread in threads:\n        # Threads支持迭代，因此可以使用for循环逐条打印主题帖信息\n        # 当然了，Threads也支持使用下标的随机访问\n        print(f\"tid: {thread.tid} 最后回复时间戳: {thread.last_time} 标题: {thread.title}\")\n\n\n# 使用asyncio.run执行协程main\nasyncio.run(main())\n```\n\n### 样例解析\n\n#### 什么是`asyncio.gather`？\n\n你可以将若干协程作为参数传入`asyncio.gather`，样例中传入了两个协程。如果你忘记了协程和异步函数之间的区别，请及时复习。\n\n`asyncio.gather`会为每个传入的协程创建对应的任务来同时执行它们（并发），同时`asyncio.gather(...)`自身也是一个协程，在前面添加`await`以要求主协程`main()`等待其执行完毕。执行完毕后，返回数据的顺序与传入协程的顺序一致，即`user`对应`client.get_self_info()`，`threads`对应`client.get_threads(...)`\n\n### 期望结果\n\n运行效果如下所示\n\n```log\n当前用户: Starry_OvO\ntid: 7595618217 最后回复时间戳: 1672461980 标题: 关于负能量帖子的最新规定\ntid: 8204562074 最后回复时间戳: 1672502281 标题: 外卖超时退单，心理煎熬\ntid: 8165883863 最后回复时间戳: 1672502270 标题: 【记录】我这半醉半醒的人生啊\ntid: 8204618726 最后回复时间戳: 1672502254 标题: 记录一下编导生的日常\ntid: 8202743003 最后回复时间戳: 1672502252 标题: 2023会更好吗？或者，又是一年的碌碌无为\ntid: 8204456677 最后回复时间戳: 1672502301 标题: 2023新年倒计时开始，有人的话请回复\ntid: 8203409990 最后回复时间戳: 1672502197 标题: 年尾了，谢谢你们\ntid: 8203959170 最后回复时间戳: 1672502156 标题: 求祝福\ntid: 8188549079 最后回复时间戳: 1672502122 标题: pollen's club\ntid: 8204240728 最后回复时间戳: 1672502091 标题: 这是孩子最贵重的东西\ntid: 8200916354 最后回复时间戳: 1672502023 标题: 这个是真的吗\ntid: 8204206290 最后回复时间戳: 1672501931 标题: 家里突然多了只狗，请大家取个名字\ntid: 8204353842 最后回复时间戳: 1672501936 标题: 一个很好的外卖小哥\ntid: 8204583367 最后回复时间戳: 1672501911 标题: 何等奇迹！坚韧灵魂！\ntid: 8204431580 最后回复时间戳: 1672501835 标题: 大家今年想怎么跨年呢？\ntid: 8204442527 最后回复时间戳: 1672501832 标题: 吧友们，快过年了能不能发一些温馨可爱的图\ntid: 8202573308 最后回复时间戳: 1672501923 标题:\ntid: 8202504004 最后回复时间戳: 1672501740 标题: 吧友们，想听到那4个字\ntid: 8203284120 最后回复时间戳: 1672501971 标题: 看到评论区 觉得很暖心 想给吧友分享分享\ntid: 8203290932 最后回复时间戳: 1672502300 标题:\ntid: 8202592714 最后回复时间戳: 1672501686 标题: 不要走啊狗狗\ntid: 8165292224 最后回复时间戳: 1672501498 标题: 你想要只肥啾吗？\ntid: 8202351346 最后回复时间戳: 1672501588 标题: 这就是缘分吗？\ntid: 8204609134 最后回复时间戳: 1672501304 标题:\ntid: 8204575619 最后回复时间戳: 1672501526 标题: 标题五个字\ntid: 8199583210 最后回复时间戳: 1672501343 标题: 一些有趣的图图\ntid: 8204401395 最后回复时间戳: 1672494092 标题: 兄弟们  初来乍到\ntid: 8200191186 最后回复时间戳: 1672500928 标题: 我妈做了一件好事\ntid: 8204273523 最后回复时间戳: 1672500829 标题: 你如初待我模样\n```\n\n## 任务队列实现多协程爬虫\n\n### 样例代码\n\n本样例将通过任务队列实现一个多协程爬虫，快速爬取天堂鸡汤吧的前32页共960条主题帖，并打印其中浏览量最高的10条\n\n```python\nfrom __future__ import annotations\n\nimport asyncio\nimport time\n\nimport aiotieba as tb\nfrom aiotieba.logging import get_logger as LOG\n\nBDUSS = \"在这里输入你账号的BDUSS\"\n\n\nasync def crawler(fname: str):\n    \"\"\"\n    获取贴吧名为fname的贴吧的前32页中浏览量最高的10个主题帖\n\n    Args:\n        fname (str): 贴吧名\n    \"\"\"\n\n    start_time = time.perf_counter()\n    LOG().info(\"Spider start\")\n\n    # thread_list用来保存主题帖列表\n    thread_list: list[tb.typing.Thread] = []\n\n    # 使用键名\"default\"对应的BDUSS创建客户端\n    async with tb.Client(BDUSS) as client:\n        # asyncio.Queue是一个任务队列\n        # maxsize=8意味着缓冲区长度为8\n        # 当缓冲区被填满时，调用Queue.put的协程会被阻塞\n        task_queue = asyncio.Queue(maxsize=8)\n        # 当is_running被设为False后，消费者会在超时后退出\n        is_running = True\n\n        async def producer():\n            \"\"\"\n            生产者协程\n            \"\"\"\n\n            for pn in range(32, 0, -1):\n                # 生产者使用Queue.put不断地将页码pn填入任务队列task_queue\n                await task_queue.put(pn)\n            # 这里需要nonlocal来允许对闭包外的变量的修改操作（类似于引用传递和值传递的区别）\n            nonlocal is_running\n            # 将is_running设置为False以允许各消费协程超时退出\n            is_running = False\n\n        async def worker(i: int):\n            \"\"\"\n            消费者协程\n\n            Args:\n                i (int): 协程编号\n            \"\"\"\n\n            while 1:\n                try:\n                    # 消费者协程不断地使用Queue.get从task_queue中拉取由生产者协程提供的页码pn作为任务\n                    # asyncio.wait_for会等待作为参数的协程执行完毕直到超时\n                    # timeout=1即把超时时间设为1秒\n                    # 如果超过1秒未获取到新的页码pn，asyncio.wait_for(...)将抛出asyncio.TimeoutError\n                    pn = await asyncio.wait_for(task_queue.get(), timeout=1)\n                    LOG().debug(f\"Worker#{i} handling pn:{pn}\")\n                except asyncio.TimeoutError:\n                    # 捕获asyncio.TimeoutError以退出协程\n                    if is_running is False:\n                        # 如果is_running为False，意味着不需要再轮询task_queue获取新任务\n                        LOG().debug(f\"Worker#{i} quit\")\n                        # 消费者协程通过return退出\n                        return\n                else:\n                    # 执行被分派的任务，即爬取pn页的帖子列表\n                    threads = await client.get_threads(fname, pn)\n                    # 这里的nonlocal同样是为了修改闭包外的变量thread_list\n                    nonlocal thread_list\n                    thread_list += threads\n\n        # 创建8个消费者协程\n        workers = [worker(i) for i in range(8)]\n        # 使用asyncio.gather并发执行\n        # 需要注意这里*workers中的*意为将列表展开成多个参数\n        # 因为asyncio.gather只接受协程作为参数，不接受协程列表\n        await asyncio.gather(*workers, producer())\n\n    LOG().info(f\"Spider complete. Time cost: {time.perf_counter() - start_time:.4f} secs\")\n\n    # 按主题帖浏览量降序排序\n    thread_list.sort(key=lambda thread: thread.view_num, reverse=True)\n    # 将浏览量最高的10个主题帖的信息打印到日志\n    for i, thread in enumerate(thread_list[0:10], 1):\n        LOG().info(f\"Rank#{i} view_num:{thread.view_num} title:{thread.title}\")\n\n\n# 执行协程crawler\nasyncio.run(crawler(\"天堂鸡汤\"))\n```\n\n### 期望结果\n\n运行效果如下图所示\n\n```log\n<2023-01-01 00:03:01.195> [INFO] [crawler] Spider start\n<2023-01-01 00:03:01.198> [DEBUG] [worker] Worker#0 handling pn:32\n<2023-01-01 00:03:01.242> [DEBUG] [worker] Worker#1 handling pn:31\n<2023-01-01 00:03:01.245> [DEBUG] [worker] Worker#2 handling pn:30\n<2023-01-01 00:03:01.245> [DEBUG] [worker] Worker#3 handling pn:29\n<2023-01-01 00:03:01.246> [DEBUG] [worker] Worker#4 handling pn:28\n<2023-01-01 00:03:01.247> [DEBUG] [worker] Worker#5 handling pn:27\n<2023-01-01 00:03:01.248> [DEBUG] [worker] Worker#6 handling pn:26\n<2023-01-01 00:03:01.248> [DEBUG] [worker] Worker#7 handling pn:25\n<2023-01-01 00:03:01.599> [DEBUG] [worker] Worker#7 handling pn:24\n<2023-01-01 00:03:01.626> [DEBUG] [worker] Worker#4 handling pn:23\n<2023-01-01 00:03:01.685> [DEBUG] [worker] Worker#2 handling pn:22\n<2023-01-01 00:03:01.711> [DEBUG] [worker] Worker#5 handling pn:21\n<2023-01-01 00:03:01.744> [DEBUG] [worker] Worker#3 handling pn:20\n<2023-01-01 00:03:01.768> [DEBUG] [worker] Worker#0 handling pn:19\n<2023-01-01 00:03:01.776> [DEBUG] [worker] Worker#1 handling pn:18\n<2023-01-01 00:03:01.777> [DEBUG] [worker] Worker#6 handling pn:17\n<2023-01-01 00:03:01.974> [DEBUG] [worker] Worker#5 handling pn:16\n<2023-01-01 00:03:02.041> [DEBUG] [worker] Worker#7 handling pn:15\n<2023-01-01 00:03:02.043> [DEBUG] [worker] Worker#4 handling pn:14\n<2023-01-01 00:03:02.072> [DEBUG] [worker] Worker#6 handling pn:13\n<2023-01-01 00:03:02.083> [DEBUG] [worker] Worker#2 handling pn:12\n<2023-01-01 00:03:02.145> [DEBUG] [worker] Worker#3 handling pn:11\n<2023-01-01 00:03:02.190> [DEBUG] [worker] Worker#0 handling pn:10\n<2023-01-01 00:03:02.197> [DEBUG] [worker] Worker#1 handling pn:9\n<2023-01-01 00:03:02.365> [DEBUG] [worker] Worker#7 handling pn:8\n<2023-01-01 00:03:02.379> [DEBUG] [worker] Worker#2 handling pn:7\n<2023-01-01 00:03:02.425> [DEBUG] [worker] Worker#5 handling pn:6\n<2023-01-01 00:03:02.547> [DEBUG] [worker] Worker#6 handling pn:5\n<2023-01-01 00:03:02.579> [DEBUG] [worker] Worker#4 handling pn:4\n<2023-01-01 00:03:02.606> [DEBUG] [worker] Worker#3 handling pn:3\n<2023-01-01 00:03:02.635> [DEBUG] [worker] Worker#0 handling pn:2\n<2023-01-01 00:03:02.640> [DEBUG] [worker] Worker#1 handling pn:1\n<2023-01-01 00:03:03.789> [DEBUG] [worker] Worker#5 quit\n<2023-01-01 00:03:03.820> [DEBUG] [worker] Worker#7 quit\n<2023-01-01 00:03:03.821> [DEBUG] [worker] Worker#2 quit\n<2023-01-01 00:03:03.821> [DEBUG] [worker] Worker#6 quit\n<2023-01-01 00:03:03.882> [DEBUG] [worker] Worker#4 quit\n<2023-01-01 00:03:03.975> [DEBUG] [worker] Worker#0 quit\n<2023-01-01 00:03:03.975> [DEBUG] [worker] Worker#1 quit\n<2023-01-01 00:03:03.976> [INFO] [crawler] Spider complete. Time cost: 2.7822 secs\n<2023-01-01 00:03:03.977> [INFO] [crawler] Rank#1 view_num:295571 title:各位发点暖心小故事吧我先来\n<2023-01-01 00:03:03.978> [INFO] [crawler] Rank#2 view_num:285897 title:解决压力大\n<2023-01-01 00:03:03.978> [INFO] [crawler] Rank#3 view_num:255771 title:人活着是为了什么\n<2023-01-01 00:03:03.978> [INFO] [crawler] Rank#4 view_num:243325 title:面藕，我的面藕😭\n<2023-01-01 00:03:03.979> [INFO] [crawler] Rank#5 view_num:222611 title:什么事情是你长大很久之后才明白的？\n<2023-01-01 00:03:03.979> [INFO] [crawler] Rank#6 view_num:216527 title:教你谈恋爱\n<2023-01-01 00:03:03.979> [INFO] [crawler] Rank#7 view_num:214848 title:你已经是只狗了！\n<2023-01-01 00:03:03.980> [INFO] [crawler] Rank#8 view_num:208130 title:好温暖呀~\n<2023-01-01 00:03:03.980> [INFO] [crawler] Rank#9 view_num:206946 title:好温柔的叔叔啊😭\n<2023-01-01 00:03:03.980> [INFO] [crawler] Rank#10 view_num:203606 title:你会不会删掉已故亲人的联系方式？\n```\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: aiotieba\nsite_description: aiotieba开发文档\nsite_url: https://aiotieba.cc/\n\ntheme:\n  name: material\n  palette:\n    - media: \"(prefers-color-scheme: light)\"\n      scheme: default\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\n    - media: \"(prefers-color-scheme: dark)\"\n      scheme: slate\n      toggle:\n        icon: material/brightness-4\n        name: Switch to light mode\n  features:\n    - navigation.sections\n    - search.suggest\n    - search.highlight\n  language: zh\n\nrepo_name: lumina37/aiotieba\nrepo_url: https://github.com/lumina37/aiotieba/\n\nnav:\n  - 介绍: index.md\n  - 教程:\n      - 入门教程: tutorial/start.md\n      - 异步编程入门教程: tutorial/async_start.md\n      - 实用微脚本合集: tutorial/many_utils.md\n  - 参考文档:\n      - 客户端 (Client): ref/client.md\n      - 枚举: ref/enums.md\n      - 配置: ref/config.md\n      - 异常处理: ref/exception.md\n      - 类型定义:\n          - forum_detail: ref/classdef/forum_detail.md\n          - threads: ref/classdef/threads.md\n          - posts: ref/classdef/posts.md\n          - comments: ref/classdef/comments.md\n          - last_replyers: ref/classdef/last_replyers.md\n          - searches: ref/classdef/searches.md\n          - user_info: ref/classdef/user_info.md\n          - profile: ref/classdef/profile.md\n          - follow_forums: ref/classdef/follow_forums.md\n          - self_follow_forums: ref/classdef/self_follow_forums.md\n          - user_contents: ref/classdef/user_contents.md\n          - images: ref/classdef/images.md\n          - replys: ref/classdef/replys.md\n          - ats: ref/classdef/ats.md\n          - follows: ref/classdef/follows.md\n          - fans: ref/classdef/fans.md\n          - blacklist: ref/classdef/blacklist.md\n          - blacklist_old: ref/classdef/blacklist_old.md\n          - dislike_forums: ref/classdef/dislike_forums.md\n          - square_forums: ref/classdef/square_forums.md\n          - bawu_info: ref/classdef/bawu_info.md\n          - bawu_perm: ref/classdef/bawu_perm.md\n          - rank_users: ref/classdef/rank_users.md\n          - member_users: ref/classdef/member_users.md\n          - blocks: ref/classdef/blocks.md\n          - recovers: ref/classdef/recovers.md\n          - recover_thread: ref/classdef/recover_thread.md\n          - bawu_userlogs: ref/classdef/bawu_userlogs.md\n          - bawu_postlogs: ref/classdef/bawu_postlogs.md\n          - unblock_appeals: ref/classdef/unblock_appeals.md\n          - bawu_blacklist: ref/classdef/bawu_blacklist.md\n          - statistics: ref/classdef/statistics.md\n          - recom_status: ref/classdef/recom_status.md\n          - group_msg: ref/classdef/group_msg.md\n\nmarkdown_extensions:\n  - md_in_html\n  - admonition\n  - codehilite:\n      css_class: highlight\n\nplugins:\n  - search\n  - mkdocstrings:\n      handlers:\n        python:\n          options:\n            show_bases: false\n            show_source: false\n            members_order: source\n\nextra_css:\n  - css/custom.css\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[project]\nname = \"aiotieba\"\nversion = \"4.7.0\"\ndescription = \"Asynchronous I/O Client for Baidu Tieba\"\nauthors = [{ name = \"lumina37\", email = \"starry.qvq@gmail.com\" }]\nurls = { Repository = \"https://github.com/lumina37/aiotieba/\", Documentation = \"https://aiotieba.cc/\" }\nreadme = \"README.md\"\nkeywords = [\"baidu\", \"tieba\"]\nclassifiers = [\n  \"Development Status :: 4 - Beta\",\n  \"Framework :: AsyncIO\",\n  \"Intended Audience :: Developers\",\n  \"License :: OSI Approved :: The Unlicense (Unlicense)\",\n  \"Operating System :: OS Independent\",\n  \"Programming Language :: Python :: 3.10\",\n  \"Programming Language :: Python :: 3.11\",\n  \"Programming Language :: Python :: 3.12\",\n  \"Programming Language :: Python :: 3.13\",\n  \"Programming Language :: Python :: 3.14\",\n  \"Topic :: Internet :: WWW/HTTP :: Session\",\n]\nrequires-python = \">=3.10,<3.15\"\ndependencies = [\n  \"aiohttp>=3.11.0,<4;python_version>='3.10' and python_version<'3.14'\",\n  \"aiohttp>=3.13.0,<4;python_version>='3.14'\",\n  \"beautifulsoup4>=4.7.1,<5\",\n  \"lxml>=4.6.4,<7;python_version=='3.10'\",\n  \"lxml>=4.9.2,<7;python_version=='3.11'\",\n  \"lxml>=4.9.3,<7;python_version=='3.12'\",\n  \"lxml>=5.3.0,<7;python_version=='3.13'\",\n  \"lxml>=6.0.1,<7;python_version>='3.14'\",\n  \"protobuf>=4.21.1,<8\",\n  \"cryptography>=35.0.0,<47\",\n  \"async-timeout>=4.0,<6;python_version=='3.10'\",\n  \"StrEnum>=0.4.0,<0.5;python_version=='3.10'\",\n]\n\n[project.optional-dependencies]\nimg = [\n  \"opencv-contrib-python-headless>=4.6.0.66,<5;sys_platform=='linux'\",\n  \"opencv-contrib-python>=4.6.0.66,<5;sys_platform!='linux'\",\n]\nspeedup = [\n  \"orjson>=3.4.7,<4;python_version=='3.10'\",\n  \"orjson>=3.7.10,<4;python_version=='3.11'\",\n  \"orjson>=3.9.10,<4;python_version=='3.12'\",\n  \"orjson>=3.10.7,<4;python_version=='3.13'\",\n  \"orjson>=3.11.1,<4;python_version>='3.14'\",\n]\n\n[dependency-groups]\ndev = [\"pytest==9.0.3\", \"pytest-asyncio==1.3.0\", \"pytest-rerunfailures==16.1\"]\ndocs = [\"mkdocs-material\", \"mkdocstrings[python]\"]\n\n[build-system]\nrequires = [\"scikit-build-core>=0.12.2,<0.13\"]\nbuild-backend = \"scikit_build_core.build\"\n\n[tool.uv]\nmanaged = true\n\n[tool.scikit-build]\nsdist.exclude = [\"*.proto\", \".*\", \"docs\", \"scripts\", \"tests\", \"mkdocs.yml\"]\nwheel.exclude = [\"*.c\", \"*.h\", \"*.txt\"]\n\n[[tool.scikit-build.generate]]\npath = \"aiotieba/__version__.py\"\ntemplate = '''__version__ = \"${version}\"'''\n\n[tool.cibuildwheel]\nbuild = \"cp310* cp311* cp312* cp313* cp314* pp310* pp311*\"\nskip = \"*-win32 *_i686 *_s390x *_ppc64le\"\nenable = [\"pypy\", \"pypy-eol\"]\nbuild-frontend = \"uv\"\n\n[tool.ruff]\nline-length = 120\ntarget-version = \"py310\"\npreview = true\n\n[tool.ruff.lint]\nselect = [\n  \"F\",\n  \"E\",\n  \"W\",\n  \"I\",\n  \"UP\",\n  \"YTT\",\n  \"ASYNC\",\n  \"B\",\n  \"A\",\n  \"C4\",\n  \"FA\",\n  \"ICN\",\n  \"LOG\",\n  \"G\",\n  \"PIE\",\n  \"T20\",\n  \"PT\",\n  \"Q\",\n  \"RSE\",\n  \"SLOT\",\n  \"TC\",\n  \"PTH\",\n  \"NPY\",\n  \"PERF\",\n  \"FURB\",\n]\nignore = [\"A005\", \"E402\", \"E501\", \"E266\"]\n\n[tool.ruff.lint.per-file-ignores]\n\"__init__.py\" = [\"F401\"]\n\"typing.py\" = [\"F401\"]\n\"*_pb2.py\" = [\"F401\"]\n\n[tool.pytest.ini_options]\naddopts = \"-q\"\ntestpaths = [\"tests\"]\nrequired_plugins = \"pytest-asyncio pytest-rerunfailures\"\nasyncio_mode = \"strict\"\nasyncio_default_fixture_loop_scope = \"function\"\n"
  },
  {
    "path": "scripts/proto_compile.py",
    "content": "from __future__ import annotations\n\nimport subprocess\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\ncommom_proto_pth = Path(\"src/aiotieba/api/_protobuf\")\n\nfor fpth in commom_proto_pth.glob(\"*_pb2.py\"):\n    fpth.unlink()\n\nsubprocess.run(\"protoc --python_out=. *.proto\", cwd=str(commom_proto_pth), check=True, timeout=60.0)\n\n\ndef row_filter(rows: list[str], import_perfix: str) -> Iterator[str]:\n    is_runtime_checker = False\n    for row in rows:\n        if row.startswith(\"#\"):\n            continue\n\n        if not is_runtime_checker and row.startswith(\"_runtime\"):\n            is_runtime_checker = True\n        if is_runtime_checker and row.startswith(\")\"):\n            is_runtime_checker = False\n            continue\n        if is_runtime_checker:\n            continue\n\n        if \"import runtime_version\" in row:\n            continue\n\n        if row.startswith(\"import\"):\n            row = import_perfix + row\n\n        yield row\n\n\nfor fpth in commom_proto_pth.glob(\"*_pb2.py\"):\n    bak_fpth = fpth.with_suffix(\".bak\")\n    with (\n        fpth.open(\"r\") as f,\n        bak_fpth.open(\"w\") as bak_f,\n    ):\n        bak_f.writelines(row_filter(f, \"from . \"))\n    fpth.unlink()\n    bak_fpth.rename(fpth)\n\nfor mod_pth in Path(\"src/aiotieba/api\").glob(\"*/protobuf\"):\n    for fpth in mod_pth.glob(\"*_pb2.py\"):\n        fpth.unlink()\n\n    subprocess.run(\"protoc -I../../_protobuf -I. --python_out=. *.proto\", cwd=str(mod_pth), check=True, timeout=10.0)\n\n    for fpth in mod_pth.glob(\"*_pb2.py\"):\n        bak_fpth = fpth.with_suffix(\".bak\")\n        with (\n            fpth.open(\"r\") as f,\n            bak_fpth.open(\"w\") as bak_f,\n        ):\n            bak_f.writelines(row_filter(f, \"from ..._protobuf \"))\n        fpth.unlink()\n        bak_fpth.rename(fpth)\n\nsubprocess.run(\"uvx ruff check src/**/*_pb2.py --fix --unsafe-fixes -s\", cwd=\".\", check=False, timeout=10.0)\nsubprocess.run(\"uvx ruff format src/**/*_pb2.py -s\", cwd=\".\", check=False, timeout=30.0)\n"
  },
  {
    "path": "src/aiotieba/__init__.py",
    "content": "\"\"\"\nAsynchronous I/O Client/Reviewer for Baidu Tieba\n\n@Author: starry.qvq@gmail.com\n@License: Unlicense\n@Documentation: https://aiotieba.cc/\n\"\"\"\n\nfrom . import const, core, enums, exception, logging, typing\nfrom .__version__ import __version__\nfrom .client import Client\nfrom .config import ProxyConfig, TimeoutConfig\nfrom .core import Account\nfrom .enums import *  # noqa: F403\nfrom .logging import enable_filelog, get_logger\n"
  },
  {
    "path": "src/aiotieba/__version__.py",
    "content": "__version__ = \"TBD\"\n"
  },
  {
    "path": "src/aiotieba/api/__init__.py",
    "content": ""
  },
  {
    "path": "src/aiotieba/api/_classdef/__init__.py",
    "content": "from ...core import Account\nfrom .common import TypeMessage\nfrom .container import Containers\nfrom .contents import (\n    FragAt,\n    FragEmoji,\n    FragImage,\n    FragItem,\n    FragLink,\n    FragText,\n    FragTiebaPlus,\n    FragUnknown,\n    TypeFragAt,\n    TypeFragEmoji,\n    TypeFragImage,\n    TypeFragItem,\n    TypeFragLink,\n    TypeFragment,\n    TypeFragText,\n    TypeFragTiebaPlus,\n)\nfrom .user import UserInfo\nfrom .vote import VoteInfo\n"
  },
  {
    "path": "src/aiotieba/api/_classdef/common.py",
    "content": "from typing import TypeVar\n\nfrom google.protobuf.message import Message\n\nTypeMessage = TypeVar(\"TypeMessage\", bound=Message)\n"
  },
  {
    "path": "src/aiotieba/api/_classdef/container.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING, Generic, SupportsIndex, TypeVar, overload\n\nif TYPE_CHECKING:\n    from collections.abc import Iterator\n\nTypeContainer = TypeVar(\"TypeContainer\")\n\n\n@dcs.dataclass\nclass Containers(Generic[TypeContainer]):\n    \"\"\"\n    内容列表的泛型基类\n    约定取内容的通用接口\n\n    Attributes:\n        objs (list[TypeContainer]): 内容列表\n    \"\"\"\n\n    objs: list[TypeContainer] = dcs.field(default_factory=list)\n\n    def __iter__(self) -> Iterator[TypeContainer]:\n        return self.objs.__iter__()\n\n    @overload\n    def __getitem__(self, idx: SupportsIndex) -> TypeContainer: ...\n\n    @overload\n    def __getitem__(self, idx: slice) -> list[TypeContainer]: ...\n\n    def __getitem__(self, idx):\n        return self.objs.__getitem__(idx)\n\n    def __setitem__(self, idx, val):\n        raise NotImplementedError\n\n    def __delitem__(self, idx):\n        raise NotImplementedError\n\n    def __len__(self) -> int:\n        return self.objs.__len__()\n\n    def __bool__(self) -> bool:\n        return bool(self.objs)\n"
  },
  {
    "path": "src/aiotieba/api/_classdef/contents.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nimport re\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING, Any, Protocol, TypeVar\n\nimport yarl\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n    from .common import TypeMessage\n\nTypeFragment = TypeVar(\"TypeFragment\")\n\n\n@dcs.dataclass\nclass FragText:\n    \"\"\"\n    纯文本碎片\n\n    Attributes:\n        text (str): 文本内容\n    \"\"\"\n\n    text: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragText:\n        text = data_proto.text\n        return FragText(text)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> FragText:\n        text = data_map[\"text\"]\n        return FragText(text)\n\n\nclass TypeFragText(Protocol):\n    text: str\n\n\n@dcs.dataclass\nclass FragEmoji:\n    \"\"\"\n    表情碎片\n\n    Attributes:\n        id (str): 表情图片id\n        desc (str): 表情描述\n    \"\"\"\n\n    id: str = \"\"\n    desc: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragEmoji:\n        id_ = data_proto.text\n        desc = data_proto.c\n        return FragEmoji(id_, desc)\n\n\nclass TypeFragEmoji(Protocol):\n    id: str\n    desc: str\n\n\n_IMAGEHASH_EXP = re.compile(r\"/([a-z0-9]{32,})\\.\")\n\n\n@dcs.dataclass\nclass FragImage:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 小图链接 宽720px\n        big_src (str): 大图链接 宽960px\n        origin_src (str): 原图链接\n        origin_size (int): 原图大小\n        show_width (int): 图像在客户端预览显示的宽度\n        show_height (int): 图像在客户端预览显示的高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    big_src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    origin_size: int = 0\n    show_width: int = 0\n    show_height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragImage:\n        src = data_proto.cdn_src\n        big_src = data_proto.big_cdn_src\n        origin_src = data_proto.origin_src\n        origin_size = data_proto.origin_size\n\n        show_width, _, show_height = data_proto.bsize.partition(\",\")\n        show_width = int(show_width)\n        show_height = int(show_height)\n\n        if hash_obj := _IMAGEHASH_EXP.search(src):\n            hash_ = hash_obj.group(1)\n        else:\n            hash_ = \"\"\n\n        return FragImage(src, big_src, origin_src, origin_size, show_width, show_height, hash_)\n\n\n@dcs.dataclass\nclass TypeFragImage(Protocol):\n    src: str\n    origin_src: str\n    hash: str\n\n\n@dcs.dataclass\nclass FragAt:\n    \"\"\"\n    @碎片\n\n    Attributes:\n        text (str): 被@用户的昵称 含@\n        user_id (int): 被@用户的user_id\n    \"\"\"\n\n    text: str = \"\"\n    user_id: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragAt:\n        text = data_proto.text\n        user_id = data_proto.uid\n        return FragAt(text, user_id)\n\n\nclass TypeFragAt(Protocol):\n    text: str\n    user_id: int\n\n\n@dcs.dataclass\nclass FragVoice:\n    \"\"\"\n    音频碎片\n\n    Attributes:\n        md5 (str): 音频md5\n        duration (int): 音频长度 以秒为单位\n    \"\"\"\n\n    md5: str = \"\"\n    duration: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragVoice:\n        md5 = data_proto.voice_md5\n        duration = data_proto.during_time / 1000\n        return FragVoice(md5, duration)\n\n    def __bool__(self) -> bool:\n        return bool(self.md5)\n\n\nclass TypeFragVoice(Protocol):\n    md5: str\n    duration: int\n\n\n@dcs.dataclass\nclass FragVideo:\n    \"\"\"\n    视频碎片\n\n    Attributes:\n        src (str): 视频链接\n        cover_src (str): 封面链接\n        duration (int): 视频长度\n        width (int): 视频宽度\n        height (int): 视频高度\n        view_num (int): 浏览次数\n    \"\"\"\n\n    src: str = \"\"\n    cover_src: str = \"\"\n    duration: int = 0\n    width: int = 0\n    height: int = 0\n    view_num: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragVideo:\n        src = data_proto.video_url\n        cover_src = data_proto.thumbnail_url\n        duration = data_proto.video_duration\n        width = data_proto.video_width\n        height = data_proto.video_height\n        view_num = data_proto.play_count\n        return FragVideo(src, cover_src, duration, width, height, view_num)\n\n    def __bool__(self) -> bool:\n        return bool(self.width)\n\n\nclass TypeFragVideo(Protocol):\n    src: str\n    cover_src: str\n    duration: int\n    width: int\n    height: int\n    view_num: int\n\n\n@dcs.dataclass\nclass FragLink:\n    \"\"\"\n    链接碎片\n\n    Attributes:\n        text (str): 原链接\n        title (str): 链接标题\n        raw_url (yarl.URL): 解析后的原链接\n        url (yarl.URL): 解析后的去前缀链接\n        is_external (bool): 是否外部链接\n    \"\"\"\n\n    text: str = \"\"\n    title: str = \"\"\n    raw_url: yarl.URL = dcs.field(default_factory=yarl.URL)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragLink:\n        text = data_proto.link\n        title = data_proto.text\n        raw_url = yarl.URL(text)\n        return FragLink(text, title, raw_url)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> FragLink:\n        text = data_map[\"link\"]\n        title = data_map[\"text\"]\n        raw_url = yarl.URL(text)\n        return FragLink(text, title, raw_url)\n\n    @cached_property\n    def url(self) -> yarl.URL:\n        if self.is_external:\n            url = yarl.URL(self.raw_url.query[\"url\"])\n        else:\n            url = self.raw_url\n        return url\n\n    @cached_property\n    def is_external(self) -> bool:\n        return self.raw_url.path == \"/mo/q/checkurl\"\n\n\nclass TypeFragLink(Protocol):\n    text: str\n    title: str\n    raw_url: yarl.URL\n\n    @property\n    def url(self) -> yarl.URL: ...\n\n    @property\n    def is_external(self) -> bool: ...\n\n\n@dcs.dataclass\nclass FragTiebaPlus:\n    \"\"\"\n    贴吧plus广告碎片\n\n    Attributes:\n        text (str): 贴吧plus广告描述\n        url (yarl.URL): 解析后的贴吧plus广告跳转链接\n    \"\"\"\n\n    text: str = \"\"\n    url: yarl.URL = dcs.field(default_factory=yarl.URL)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragTiebaPlus:\n        text = data_proto.tiebaplus_info.desc\n        url = yarl.URL(data_proto.tiebaplus_info.jump_url)\n        return FragTiebaPlus(text, url)\n\n\nclass TypeFragTiebaPlus(Protocol):\n    text: str\n    url: yarl.URL\n\n\n@dcs.dataclass\nclass FragItem:\n    \"\"\"\n    item碎片\n\n    Attributes:\n        text (str): item名称\n    \"\"\"\n\n    text: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragItem:\n        text = data_proto.item.item_name\n        return FragItem(text)\n\n\nclass TypeFragItem(Protocol):\n    text: str\n\n\n@dcs.dataclass\nclass FragUnknown:\n    \"\"\"\n    未知碎片\n\n    Attributes:\n        data (Any): 原始数据\n    \"\"\"\n\n    proto: Any\n\n    @staticmethod\n    def from_proto(data: Any) -> FragUnknown:\n        return FragUnknown(data)\n\n    @staticmethod\n    def from_json(data: Mapping) -> FragUnknown:\n        return FragUnknown(data)\n"
  },
  {
    "path": "src/aiotieba/api/_classdef/user.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\n\nfrom ...enums import Gender, PrivLike, PrivReply\n\n\n@dcs.dataclass\nclass UserInfo:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_old (str): 旧版昵称\n        nick_name_new (str): 新版昵称\n        tieba_uid (int): 用户个人主页uid\n\n        glevel (int): 贴吧成长等级\n        gender (Gender): 性别\n        age (float): 吧龄 以年为单位\n        post_num (int): 发帖数\n        agree_num (int): 获赞数\n        fan_num (int): 粉丝数\n        follow_num (int): 关注数\n        forum_num (int): 关注贴吧数\n        sign (str): 个性签名\n        ip (str): ip归属地\n        icons (list[str]): 印记信息\n\n        is_vip (bool): 是否超级会员\n        is_god (bool): 是否大神\n        is_blocked (bool): 是否被永久封禁屏蔽\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_old: str = \"\"\n    nick_name_new: str = \"\"\n    tieba_uid: int = 0\n\n    glevel: int = 0\n    gender: Gender = Gender.UNKNOWN\n    age: float = 0.0\n    post_num: int = 0\n    agree_num: int = 0\n    fan_num: int = 0\n    follow_num: int = 0\n    forum_num: int = 0\n    sign: str = \"\"\n    ip: str = \"\"\n    icons: list[str] = dcs.field(default_factory=list)\n\n    is_vip: bool = False\n    is_god: bool = False\n    is_blocked: bool = False\n    uk: int = 0\n    bduk: str = \"\"\n    trigger_id: int = 0\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    def __ior__(self, obj) -> UserInfo:\n        for field in dcs.fields(obj):\n            if hasattr(self, field.name):\n                val = getattr(obj, field.name)\n                setattr(self, field.name, val)\n        return self\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new or self.nick_name_old\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.nick_name_old or self.user_name\n\n    @property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n"
  },
  {
    "path": "src/aiotieba/api/_classdef/vote.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from .common import TypeMessage\n\n\n@dcs.dataclass\nclass VoteOption:\n    \"\"\"\n    投票选项信息\n\n    Attributes:\n        vote_num (int): 得票数\n        text (str): 选项描述文字\n    \"\"\"\n\n    vote_num: int = 0\n    text: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> VoteOption:\n        vote_num = data_proto.num\n        text = data_proto.text\n        return VoteOption(vote_num, text)\n\n\n@dcs.dataclass\nclass VoteInfo:\n    \"\"\"\n    投票信息\n\n    Attributes:\n        title (str): 投票标题\n        is_multi (bool): 是否多选\n        options (list[VoteOption]): 选项列表\n        total_vote (int): 总投票数\n        total_user (int): 总投票人数\n    \"\"\"\n\n    title: str = \"\"\n    is_multi: bool = False\n    options: list[VoteOption] = dcs.field(default_factory=list)\n    total_vote: int = 0\n    total_user: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> VoteInfo:\n        title = data_proto.title\n        is_multi = bool(data_proto.is_multi)\n        options = [VoteOption.from_proto(p) for p in data_proto.options]\n        total_vote = data_proto.total_poll\n        total_user = data_proto.total_num\n        return VoteInfo(title, is_multi, options, total_vote, total_user)\n\n    def __len__(self) -> int:\n        return len(self.options)\n\n    def __bool__(self) -> bool:\n        return bool(self.options)\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Agree.proto",
    "content": "// tbclient.Agree\nsyntax = \"proto3\";\n\nmessage Agree {\n    int64 agree_num = 1;\n    int64 disagree_num = 4;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Agree_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x0b\\x41gree.proto\"0\\n\\x05\\x41gree\\x12\\x11\\n\\tagree_num\\x18\\x01 \\x01(\\x03\\x12\\x14\\n\\x0c\\x64isagree_num\\x18\\x04 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"Agree_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_AGREE\"]._serialized_start = 15\n    _globals[\"_AGREE\"]._serialized_end = 63\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/CommonReq.proto",
    "content": "// tbclient.CommonReq\nsyntax = \"proto3\";\n\nmessage CommonReq {\n    int32 _client_type = 1;\n    string _client_version = 2;\n    string _client_id = 3;\n    string _phone_imei = 5;\n    string _from = 6;\n    string cuid = 7;\n    int64 _timestamp = 8;\n    string model = 9;\n    string BDUSS = 10;\n    string tbs = 11;\n    int32 net_type = 12;\n    string pversion = 24;\n    string _os_version = 25;\n    string brand = 26;\n    string lego_lib_version = 28;\n    string applist = 29;\n    string stoken = 30;\n    string z_id = 31;\n    string cuid_galaxy2 = 32;\n    string cuid_gid = 33;\n    string c3_aid = 35;\n    string sample_id = 36;\n    int32 scr_w = 37;\n    int32 scr_h = 38;\n    double scr_dip = 39;\n    int32 q_type = 40;\n    int32 is_teenager = 41;\n    string sdk_ver = 42;\n    string framework_ver = 43;\n    string naws_game_ver = 44;\n    int64 active_timestamp = 49;\n    int64 first_install_time = 50;\n    int64 last_update_time = 51;\n    string event_day = 53;\n    string android_id = 54;\n    int32 cmode = 55;\n    string start_scheme = 56;\n    int32 start_type = 57;\n    string idfv = 60;\n    string extra = 61;\n    string user_agent = 62;\n    int32 personalized_rec_switch = 63;\n    string device_score = 70;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/CommonReq_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b\"\\n\\x0f\\x43ommonReq.proto\\\"\\xc6\\x06\\n\\tCommonReq\\x12\\x14\\n\\x0c_client_type\\x18\\x01 \\x01(\\x05\\x12\\x17\\n\\x0f_client_version\\x18\\x02 \\x01(\\t\\x12\\x12\\n\\n_client_id\\x18\\x03 \\x01(\\t\\x12\\x13\\n\\x0b_phone_imei\\x18\\x05 \\x01(\\t\\x12\\r\\n\\x05_from\\x18\\x06 \\x01(\\t\\x12\\x0c\\n\\x04\\x63uid\\x18\\x07 \\x01(\\t\\x12\\x12\\n\\n_timestamp\\x18\\x08 \\x01(\\x03\\x12\\r\\n\\x05model\\x18\\t \\x01(\\t\\x12\\r\\n\\x05\\x42\\x44USS\\x18\\n \\x01(\\t\\x12\\x0b\\n\\x03tbs\\x18\\x0b \\x01(\\t\\x12\\x10\\n\\x08net_type\\x18\\x0c \\x01(\\x05\\x12\\x10\\n\\x08pversion\\x18\\x18 \\x01(\\t\\x12\\x13\\n\\x0b_os_version\\x18\\x19 \\x01(\\t\\x12\\r\\n\\x05\\x62rand\\x18\\x1a \\x01(\\t\\x12\\x18\\n\\x10lego_lib_version\\x18\\x1c \\x01(\\t\\x12\\x0f\\n\\x07\\x61pplist\\x18\\x1d \\x01(\\t\\x12\\x0e\\n\\x06stoken\\x18\\x1e \\x01(\\t\\x12\\x0c\\n\\x04z_id\\x18\\x1f \\x01(\\t\\x12\\x14\\n\\x0c\\x63uid_galaxy2\\x18  \\x01(\\t\\x12\\x10\\n\\x08\\x63uid_gid\\x18! \\x01(\\t\\x12\\x0e\\n\\x06\\x63\\x33_aid\\x18# \\x01(\\t\\x12\\x11\\n\\tsample_id\\x18$ \\x01(\\t\\x12\\r\\n\\x05scr_w\\x18% \\x01(\\x05\\x12\\r\\n\\x05scr_h\\x18& \\x01(\\x05\\x12\\x0f\\n\\x07scr_dip\\x18' \\x01(\\x01\\x12\\x0e\\n\\x06q_type\\x18( \\x01(\\x05\\x12\\x13\\n\\x0bis_teenager\\x18) \\x01(\\x05\\x12\\x0f\\n\\x07sdk_ver\\x18* \\x01(\\t\\x12\\x15\\n\\rframework_ver\\x18+ \\x01(\\t\\x12\\x15\\n\\rnaws_game_ver\\x18, \\x01(\\t\\x12\\x18\\n\\x10\\x61\\x63tive_timestamp\\x18\\x31 \\x01(\\x03\\x12\\x1a\\n\\x12\\x66irst_install_time\\x18\\x32 \\x01(\\x03\\x12\\x18\\n\\x10last_update_time\\x18\\x33 \\x01(\\x03\\x12\\x11\\n\\tevent_day\\x18\\x35 \\x01(\\t\\x12\\x12\\n\\nandroid_id\\x18\\x36 \\x01(\\t\\x12\\r\\n\\x05\\x63mode\\x18\\x37 \\x01(\\x05\\x12\\x14\\n\\x0cstart_scheme\\x18\\x38 \\x01(\\t\\x12\\x12\\n\\nstart_type\\x18\\x39 \\x01(\\x05\\x12\\x0c\\n\\x04idfv\\x18< \\x01(\\t\\x12\\r\\n\\x05\\x65xtra\\x18= \\x01(\\t\\x12\\x12\\n\\nuser_agent\\x18> \\x01(\\t\\x12\\x1f\\n\\x17personalized_rec_switch\\x18? \\x01(\\x05\\x12\\x14\\n\\x0c\\x64\\x65vice_score\\x18\\x46 \\x01(\\tb\\x06proto3\"\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"CommonReq_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_COMMONREQ\"]._serialized_start = 20\n    _globals[\"_COMMONREQ\"]._serialized_end = 858\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Error.proto",
    "content": "// tbclient.Error\nsyntax = \"proto3\";\n\nmessage Error {\n    int32 errorno = 1;\n    string errmsg = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Error_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x0b\\x45rror.proto\"(\\n\\x05\\x45rror\\x12\\x0f\\n\\x07\\x65rrorno\\x18\\x01 \\x01(\\x05\\x12\\x0e\\n\\x06\\x65rrmsg\\x18\\x02 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"Error_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_ERROR\"]._serialized_start = 15\n    _globals[\"_ERROR\"]._serialized_end = 55\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/ForumList.proto",
    "content": "// Not actually exist\nsyntax = \"proto3\";\n\nmessage ForumList {\n    int64 forum_id = 1;\n    string forum_name = 2;\n    int32 member_count = 4;\n    int64 post_num = 7;\n    int64 thread_num = 8;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/ForumList_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x0f\\x46orumList.proto\"m\\n\\tForumList\\x12\\x10\\n\\x08\\x66orum_id\\x18\\x01 \\x01(\\x03\\x12\\x12\\n\\nforum_name\\x18\\x02 \\x01(\\t\\x12\\x14\\n\\x0cmember_count\\x18\\x04 \\x01(\\x05\\x12\\x10\\n\\x08post_num\\x18\\x07 \\x01(\\x03\\x12\\x12\\n\\nthread_num\\x18\\x08 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"ForumList_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_FORUMLIST\"]._serialized_start = 19\n    _globals[\"_FORUMLIST\"]._serialized_end = 128\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/FrsTabInfo.proto",
    "content": "// tbclient.FrsTabInfo\nsyntax = \"proto3\";\n\nmessage FrsTabInfo {\n    int32 tab_id = 1;\n    string tab_name = 3;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/FrsTabInfo_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x10\\x46rsTabInfo.proto\".\\n\\nFrsTabInfo\\x12\\x0e\\n\\x06tab_id\\x18\\x01 \\x01(\\x05\\x12\\x10\\n\\x08tab_name\\x18\\x03 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"FrsTabInfo_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_FRSTABINFO\"]._serialized_start = 20\n    _globals[\"_FRSTABINFO\"]._serialized_end = 66\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Lcm.proto",
    "content": "syntax = \"proto3\";\n\nmessage Common {\n    string cuid = 1;\n    string device = 2;\n    string os_version = 3;\n    string manufacture = 4;\n    string model_type = 5;\n    string app_id = 6;\n    string app_version = 7;\n    string sdk_version = 8;\n    string network = 9;\n    string rom_version = 10;\n    string user_key = 11;\n}\n\nmessage LcmNotify {\n    int64 log_id = 1;\n    int32 action = 2;\n}\n\nmessage LcmRequest {\n    int64 log_id = 1;\n    string token = 2;\n    Common common = 3;\n    int64 timestamp = 4;\n    int32 action = 5;\n    int32 start_type = 6;\n    int32 conn_type = 7;\n}\n\nmessage LcmResponse {\n    int64 log_id = 1;\n    int32 error_code = 2;\n    string error_msg = 3;\n    int64 next_interval_ms = 4;\n    string server_info = 5;\n}\n\nmessage RpcData {\n    LcmRequest lcm_request = 1;\n    LcmResponse lcm_response = 2;\n    LcmNotify lcm_notify = 3;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Lcm_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\tLcm.proto\"\\xd5\\x01\\n\\x06\\x43ommon\\x12\\x0c\\n\\x04\\x63uid\\x18\\x01 \\x01(\\t\\x12\\x0e\\n\\x06\\x64\\x65vice\\x18\\x02 \\x01(\\t\\x12\\x12\\n\\nos_version\\x18\\x03 \\x01(\\t\\x12\\x13\\n\\x0bmanufacture\\x18\\x04 \\x01(\\t\\x12\\x12\\n\\nmodel_type\\x18\\x05 \\x01(\\t\\x12\\x0e\\n\\x06\\x61pp_id\\x18\\x06 \\x01(\\t\\x12\\x13\\n\\x0b\\x61pp_version\\x18\\x07 \\x01(\\t\\x12\\x13\\n\\x0bsdk_version\\x18\\x08 \\x01(\\t\\x12\\x0f\\n\\x07network\\x18\\t \\x01(\\t\\x12\\x13\\n\\x0brom_version\\x18\\n \\x01(\\t\\x12\\x10\\n\\x08user_key\\x18\\x0b \\x01(\\t\"+\\n\\tLcmNotify\\x12\\x0e\\n\\x06log_id\\x18\\x01 \\x01(\\x03\\x12\\x0e\\n\\x06\\x61\\x63tion\\x18\\x02 \\x01(\\x05\"\\x8e\\x01\\n\\nLcmRequest\\x12\\x0e\\n\\x06log_id\\x18\\x01 \\x01(\\x03\\x12\\r\\n\\x05token\\x18\\x02 \\x01(\\t\\x12\\x17\\n\\x06\\x63ommon\\x18\\x03 \\x01(\\x0b\\x32\\x07.Common\\x12\\x11\\n\\ttimestamp\\x18\\x04 \\x01(\\x03\\x12\\x0e\\n\\x06\\x61\\x63tion\\x18\\x05 \\x01(\\x05\\x12\\x12\\n\\nstart_type\\x18\\x06 \\x01(\\x05\\x12\\x11\\n\\tconn_type\\x18\\x07 \\x01(\\x05\"s\\n\\x0bLcmResponse\\x12\\x0e\\n\\x06log_id\\x18\\x01 \\x01(\\x03\\x12\\x12\\n\\nerror_code\\x18\\x02 \\x01(\\x05\\x12\\x11\\n\\terror_msg\\x18\\x03 \\x01(\\t\\x12\\x18\\n\\x10next_interval_ms\\x18\\x04 \\x01(\\x03\\x12\\x13\\n\\x0bserver_info\\x18\\x05 \\x01(\\t\"o\\n\\x07RpcData\\x12 \\n\\x0blcm_request\\x18\\x01 \\x01(\\x0b\\x32\\x0b.LcmRequest\\x12\"\\n\\x0clcm_response\\x18\\x02 \\x01(\\x0b\\x32\\x0c.LcmResponse\\x12\\x1e\\n\\nlcm_notify\\x18\\x03 \\x01(\\x0b\\x32\\n.LcmNotifyb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"Lcm_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_COMMON\"]._serialized_start = 14\n    _globals[\"_COMMON\"]._serialized_end = 227\n    _globals[\"_LCMNOTIFY\"]._serialized_start = 229\n    _globals[\"_LCMNOTIFY\"]._serialized_end = 272\n    _globals[\"_LCMREQUEST\"]._serialized_start = 275\n    _globals[\"_LCMREQUEST\"]._serialized_end = 417\n    _globals[\"_LCMRESPONSE\"]._serialized_start = 419\n    _globals[\"_LCMRESPONSE\"]._serialized_end = 534\n    _globals[\"_RPCDATA\"]._serialized_start = 536\n    _globals[\"_RPCDATA\"]._serialized_end = 647\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Media.proto",
    "content": "// tbclient.Media\nsyntax = \"proto3\";\n\nmessage Media {\n    int32 type = 1;\n    string small_pic = 2;\n    string big_pic = 3;\n    string water_pic = 4;\n    uint32 width = 10;\n    uint32 height = 11;\n    string origin_pic = 15;\n    uint32 origin_size = 16;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Media_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x0bMedia.proto\"\\x94\\x01\\n\\x05Media\\x12\\x0c\\n\\x04type\\x18\\x01 \\x01(\\x05\\x12\\x11\\n\\tsmall_pic\\x18\\x02 \\x01(\\t\\x12\\x0f\\n\\x07\\x62ig_pic\\x18\\x03 \\x01(\\t\\x12\\x11\\n\\twater_pic\\x18\\x04 \\x01(\\t\\x12\\r\\n\\x05width\\x18\\n \\x01(\\r\\x12\\x0e\\n\\x06height\\x18\\x0b \\x01(\\r\\x12\\x12\\n\\norigin_pic\\x18\\x0f \\x01(\\t\\x12\\x13\\n\\x0borigin_size\\x18\\x10 \\x01(\\rb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"Media_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_MEDIA\"]._serialized_start = 16\n    _globals[\"_MEDIA\"]._serialized_end = 164\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Page.proto",
    "content": "// tbclient.Page\nsyntax = \"proto3\";\n\nmessage Page {\n    int32 page_size = 1;\n    int32 current_page = 3;\n    int32 total_count = 4;\n    int32 total_page = 5;\n    int32 has_more = 6;\n    int32 has_prev = 7;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Page_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\nPage.proto\"|\\n\\x04Page\\x12\\x11\\n\\tpage_size\\x18\\x01 \\x01(\\x05\\x12\\x14\\n\\x0c\\x63urrent_page\\x18\\x03 \\x01(\\x05\\x12\\x13\\n\\x0btotal_count\\x18\\x04 \\x01(\\x05\\x12\\x12\\n\\ntotal_page\\x18\\x05 \\x01(\\x05\\x12\\x10\\n\\x08has_more\\x18\\x06 \\x01(\\x05\\x12\\x10\\n\\x08has_prev\\x18\\x07 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"Page_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PAGE\"]._serialized_start = 14\n    _globals[\"_PAGE\"]._serialized_end = 138\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/PbContent.proto",
    "content": "// tbclient.PbContent\nsyntax = \"proto3\";\n\nmessage PbContent {\n    uint32 type = 1;\n    string text = 2;\n    string link = 3;\n    string src = 4;\n    string bsize = 5;\n    string cdn_src = 8;\n    string big_cdn_src = 9;\n    string c = 11;\n    string voice_md5 = 12;\n    uint32 during_time = 13;\n    int64 uid = 15;\n    uint32 width = 18;\n    uint32 height = 19;\n    string origin_src = 25;\n    uint32 origin_size = 27;\n    int32 count = 28;\n\n    message TiebaPlusInfo {\n        string title = 1;\n        string desc = 2;\n        string jump_url = 3;\n        string app_icon = 6;\n        int32 target_type = 12;\n        int32 h5_jump_type = 13;\n        string h5_jump_number = 14;\n        string h5_jump_param = 15;\n        int32 jump_type = 16;\n        string button_desc = 23;\n    }\n    TiebaPlusInfo tiebaplus_info = 40;\n\n    message Item {\n        string item_name = 2;\n    }\n    Item item = 41;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/PbContent_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x0fPbContent.proto\"\\xcf\\x04\\n\\tPbContent\\x12\\x0c\\n\\x04type\\x18\\x01 \\x01(\\r\\x12\\x0c\\n\\x04text\\x18\\x02 \\x01(\\t\\x12\\x0c\\n\\x04link\\x18\\x03 \\x01(\\t\\x12\\x0b\\n\\x03src\\x18\\x04 \\x01(\\t\\x12\\r\\n\\x05\\x62size\\x18\\x05 \\x01(\\t\\x12\\x0f\\n\\x07\\x63\\x64n_src\\x18\\x08 \\x01(\\t\\x12\\x13\\n\\x0b\\x62ig_cdn_src\\x18\\t \\x01(\\t\\x12\\t\\n\\x01\\x63\\x18\\x0b \\x01(\\t\\x12\\x11\\n\\tvoice_md5\\x18\\x0c \\x01(\\t\\x12\\x13\\n\\x0b\\x64uring_time\\x18\\r \\x01(\\r\\x12\\x0b\\n\\x03uid\\x18\\x0f \\x01(\\x03\\x12\\r\\n\\x05width\\x18\\x12 \\x01(\\r\\x12\\x0e\\n\\x06height\\x18\\x13 \\x01(\\r\\x12\\x12\\n\\norigin_src\\x18\\x19 \\x01(\\t\\x12\\x13\\n\\x0borigin_size\\x18\\x1b \\x01(\\r\\x12\\r\\n\\x05\\x63ount\\x18\\x1c \\x01(\\x05\\x12\\x30\\n\\x0etiebaplus_info\\x18( \\x01(\\x0b\\x32\\x18.PbContent.TiebaPlusInfo\\x12\\x1d\\n\\x04item\\x18) \\x01(\\x0b\\x32\\x0f.PbContent.Item\\x1a\\xd2\\x01\\n\\rTiebaPlusInfo\\x12\\r\\n\\x05title\\x18\\x01 \\x01(\\t\\x12\\x0c\\n\\x04\\x64\\x65sc\\x18\\x02 \\x01(\\t\\x12\\x10\\n\\x08jump_url\\x18\\x03 \\x01(\\t\\x12\\x10\\n\\x08\\x61pp_icon\\x18\\x06 \\x01(\\t\\x12\\x13\\n\\x0btarget_type\\x18\\x0c \\x01(\\x05\\x12\\x14\\n\\x0ch5_jump_type\\x18\\r \\x01(\\x05\\x12\\x16\\n\\x0eh5_jump_number\\x18\\x0e \\x01(\\t\\x12\\x15\\n\\rh5_jump_param\\x18\\x0f \\x01(\\t\\x12\\x11\\n\\tjump_type\\x18\\x10 \\x01(\\x05\\x12\\x13\\n\\x0b\\x62utton_desc\\x18\\x17 \\x01(\\t\\x1a\\x19\\n\\x04Item\\x12\\x11\\n\\titem_name\\x18\\x02 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"PbContent_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PBCONTENT\"]._serialized_start = 20\n    _globals[\"_PBCONTENT\"]._serialized_end = 611\n    _globals[\"_PBCONTENT_TIEBAPLUSINFO\"]._serialized_start = 374\n    _globals[\"_PBCONTENT_TIEBAPLUSINFO\"]._serialized_end = 584\n    _globals[\"_PBCONTENT_ITEM\"]._serialized_start = 586\n    _globals[\"_PBCONTENT_ITEM\"]._serialized_end = 611\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/PollInfo.proto",
    "content": "// tbclient.PollInfo\nsyntax = \"proto3\";\n\nmessage PollInfo {\n    int32 is_multi = 2;\n    int64 total_num = 3;\n\n    message PollOption {\n        int64 num = 2;\n        string text = 3;\n    }\n    repeated PollOption options = 9;\n\n    int64 total_poll = 11;\n    string title = 12;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/PollInfo_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b\"\\n\\x0ePollInfo.proto\\\"\\xa2\\x01\\n\\x08PollInfo\\x12\\x10\\n\\x08is_multi\\x18\\x02 \\x01(\\x05\\x12\\x11\\n\\ttotal_num\\x18\\x03 \\x01(\\x03\\x12%\\n\\x07options\\x18\\t \\x03(\\x0b\\x32\\x14.PollInfo.PollOption\\x12\\x12\\n\\ntotal_poll\\x18\\x0b \\x01(\\x03\\x12\\r\\n\\x05title\\x18\\x0c \\x01(\\t\\x1a'\\n\\nPollOption\\x12\\x0b\\n\\x03num\\x18\\x02 \\x01(\\x03\\x12\\x0c\\n\\x04text\\x18\\x03 \\x01(\\tb\\x06proto3\"\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"PollInfo_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_POLLINFO\"]._serialized_start = 19\n    _globals[\"_POLLINFO\"]._serialized_end = 181\n    _globals[\"_POLLINFO_POLLOPTION\"]._serialized_start = 142\n    _globals[\"_POLLINFO_POLLOPTION\"]._serialized_end = 181\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Post.proto",
    "content": "// tbclient.Post\nsyntax = \"proto3\";\n\nimport \"PbContent.proto\";\nimport \"SubPostList.proto\";\nimport \"User.proto\";\nimport \"Agree.proto\";\n\nmessage Post {\n    int64 id = 1;\n    uint32 floor = 3;\n    uint32 time = 4;\n    repeated PbContent content = 5;\n    uint32 sub_post_number = 13;\n    int64 author_id = 19;\n\n    message SubPost {\n        repeated SubPostList sub_post_list = 2;\n    }\n    SubPost sub_post_list = 15;\n\n    message SignatureData {\n        message SignatureContent {\n            int32 type = 1;\n            string text = 2;\n        }\n        repeated SignatureContent content = 4;\n    }\n    SignatureData signature = 21;\n\n    User author = 23;\n    Agree agree = 37;\n    int64 tid = 46;\n\n    message ChatContent {\n        string bot_uk = 1;\n    }\n    ChatContent chat_content = 78;\n\n    message SpriteMemeInfo {\n        int64 meme_id = 1;\n    }\n    SpriteMemeInfo sprite_meme_info = 79;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/PostInfoList.proto",
    "content": "// tbclient.PostInfoList\nsyntax = \"proto3\";\n\nimport \"Media.proto\";\nimport \"Voice.proto\";\nimport \"PollInfo.proto\";\nimport \"VideoInfo.proto\";\nimport \"PbContent.proto\";\nimport \"Agree.proto\";\n\nmessage PostInfoList {\n    uint64 forum_id = 1;\n    uint64 thread_id = 2;\n    uint64 post_id = 3;\n    uint32 create_time = 5;\n    string forum_name = 6;\n    string title = 7;\n\n    message PostInfoContent {\n        message Abstract {\n            int32 type = 1;\n            string text = 2;\n            string link = 3;\n            string during_time = 6;\n            string voice_md5 = 7;\n        }\n        repeated Abstract post_content = 1;\n\n        uint64 create_time = 2;\n        uint64 post_type = 3;\n        uint64 post_id = 4;\n    }\n    repeated PostInfoContent content = 8;\n\n    string user_name = 10;\n    repeated Media media = 16;\n    uint32 reply_num = 17;\n    int64 user_id = 18;\n    string user_portrait = 19;\n    repeated Voice voice_info = 23;\n    uint64 thread_type = 26;\n    PollInfo poll_info = 28;\n    VideoInfo video_info = 29;\n    int32 freq_num = 33;\n    string name_show = 35;\n    int32 share_num = 39;\n    Agree agree = 40;\n    int32 is_share_thread = 44;\n    repeated PbContent first_post_content = 49;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/PostInfoList_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom . import Agree_pb2 as Agree__pb2\nfrom . import Media_pb2 as Media__pb2\nfrom . import PbContent_pb2 as PbContent__pb2\nfrom . import PollInfo_pb2 as PollInfo__pb2\nfrom . import VideoInfo_pb2 as VideoInfo__pb2\nfrom . import Voice_pb2 as Voice__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b\"\\n\\x12PostInfoList.proto\\x1a\\x0bMedia.proto\\x1a\\x0bVoice.proto\\x1a\\x0ePollInfo.proto\\x1a\\x0fVideoInfo.proto\\x1a\\x0fPbContent.proto\\x1a\\x0b\\x41gree.proto\\\"\\xf9\\x05\\n\\x0cPostInfoList\\x12\\x10\\n\\x08\\x66orum_id\\x18\\x01 \\x01(\\x04\\x12\\x11\\n\\tthread_id\\x18\\x02 \\x01(\\x04\\x12\\x0f\\n\\x07post_id\\x18\\x03 \\x01(\\x04\\x12\\x13\\n\\x0b\\x63reate_time\\x18\\x05 \\x01(\\r\\x12\\x12\\n\\nforum_name\\x18\\x06 \\x01(\\t\\x12\\r\\n\\x05title\\x18\\x07 \\x01(\\t\\x12.\\n\\x07\\x63ontent\\x18\\x08 \\x03(\\x0b\\x32\\x1d.PostInfoList.PostInfoContent\\x12\\x11\\n\\tuser_name\\x18\\n \\x01(\\t\\x12\\x15\\n\\x05media\\x18\\x10 \\x03(\\x0b\\x32\\x06.Media\\x12\\x11\\n\\treply_num\\x18\\x11 \\x01(\\r\\x12\\x0f\\n\\x07user_id\\x18\\x12 \\x01(\\x03\\x12\\x15\\n\\ruser_portrait\\x18\\x13 \\x01(\\t\\x12\\x1a\\n\\nvoice_info\\x18\\x17 \\x03(\\x0b\\x32\\x06.Voice\\x12\\x13\\n\\x0bthread_type\\x18\\x1a \\x01(\\x04\\x12\\x1c\\n\\tpoll_info\\x18\\x1c \\x01(\\x0b\\x32\\t.PollInfo\\x12\\x1e\\n\\nvideo_info\\x18\\x1d \\x01(\\x0b\\x32\\n.VideoInfo\\x12\\x10\\n\\x08\\x66req_num\\x18! \\x01(\\x05\\x12\\x11\\n\\tname_show\\x18# \\x01(\\t\\x12\\x11\\n\\tshare_num\\x18' \\x01(\\x05\\x12\\x15\\n\\x05\\x61gree\\x18( \\x01(\\x0b\\x32\\x06.Agree\\x12\\x17\\n\\x0fis_share_thread\\x18, \\x01(\\x05\\x12&\\n\\x12\\x66irst_post_content\\x18\\x31 \\x03(\\x0b\\x32\\n.PbContent\\x1a\\xe6\\x01\\n\\x0fPostInfoContent\\x12<\\n\\x0cpost_content\\x18\\x01 \\x03(\\x0b\\x32&.PostInfoList.PostInfoContent.Abstract\\x12\\x13\\n\\x0b\\x63reate_time\\x18\\x02 \\x01(\\x04\\x12\\x11\\n\\tpost_type\\x18\\x03 \\x01(\\x04\\x12\\x0f\\n\\x07post_id\\x18\\x04 \\x01(\\x04\\x1a\\\\\\n\\x08\\x41\\x62stract\\x12\\x0c\\n\\x04type\\x18\\x01 \\x01(\\x05\\x12\\x0c\\n\\x04text\\x18\\x02 \\x01(\\t\\x12\\x0c\\n\\x04link\\x18\\x03 \\x01(\\t\\x12\\x13\\n\\x0b\\x64uring_time\\x18\\x06 \\x01(\\t\\x12\\x11\\n\\tvoice_md5\\x18\\x07 \\x01(\\tb\\x06proto3\"\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"PostInfoList_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_POSTINFOLIST\"]._serialized_start = 112\n    _globals[\"_POSTINFOLIST\"]._serialized_end = 873\n    _globals[\"_POSTINFOLIST_POSTINFOCONTENT\"]._serialized_start = 643\n    _globals[\"_POSTINFOLIST_POSTINFOCONTENT\"]._serialized_end = 873\n    _globals[\"_POSTINFOLIST_POSTINFOCONTENT_ABSTRACT\"]._serialized_start = 781\n    _globals[\"_POSTINFOLIST_POSTINFOCONTENT_ABSTRACT\"]._serialized_end = 873\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Post_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom . import Agree_pb2 as Agree__pb2\nfrom . import PbContent_pb2 as PbContent__pb2\nfrom . import SubPostList_pb2 as SubPostList__pb2\nfrom . import User_pb2 as User__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b\"\\n\\nPost.proto\\x1a\\x0fPbContent.proto\\x1a\\x11SubPostList.proto\\x1a\\nUser.proto\\x1a\\x0b\\x41gree.proto\\\"\\xc4\\x04\\n\\x04Post\\x12\\n\\n\\x02id\\x18\\x01 \\x01(\\x03\\x12\\r\\n\\x05\\x66loor\\x18\\x03 \\x01(\\r\\x12\\x0c\\n\\x04time\\x18\\x04 \\x01(\\r\\x12\\x1b\\n\\x07\\x63ontent\\x18\\x05 \\x03(\\x0b\\x32\\n.PbContent\\x12\\x17\\n\\x0fsub_post_number\\x18\\r \\x01(\\r\\x12\\x11\\n\\tauthor_id\\x18\\x13 \\x01(\\x03\\x12$\\n\\rsub_post_list\\x18\\x0f \\x01(\\x0b\\x32\\r.Post.SubPost\\x12&\\n\\tsignature\\x18\\x15 \\x01(\\x0b\\x32\\x13.Post.SignatureData\\x12\\x15\\n\\x06\\x61uthor\\x18\\x17 \\x01(\\x0b\\x32\\x05.User\\x12\\x15\\n\\x05\\x61gree\\x18% \\x01(\\x0b\\x32\\x06.Agree\\x12\\x0b\\n\\x03tid\\x18. \\x01(\\x03\\x12'\\n\\x0c\\x63hat_content\\x18N \\x01(\\x0b\\x32\\x11.Post.ChatContent\\x12.\\n\\x10sprite_meme_info\\x18O \\x01(\\x0b\\x32\\x14.Post.SpriteMemeInfo\\x1a.\\n\\x07SubPost\\x12#\\n\\rsub_post_list\\x18\\x02 \\x03(\\x0b\\x32\\x0c.SubPostList\\x1av\\n\\rSignatureData\\x12\\x35\\n\\x07\\x63ontent\\x18\\x04 \\x03(\\x0b\\x32$.Post.SignatureData.SignatureContent\\x1a.\\n\\x10SignatureContent\\x12\\x0c\\n\\x04type\\x18\\x01 \\x01(\\x05\\x12\\x0c\\n\\x04text\\x18\\x02 \\x01(\\t\\x1a\\x1d\\n\\x0b\\x43hatContent\\x12\\x0e\\n\\x06\\x62ot_uk\\x18\\x01 \\x01(\\t\\x1a!\\n\\x0eSpriteMemeInfo\\x12\\x0f\\n\\x07meme_id\\x18\\x01 \\x01(\\x03\\x62\\x06proto3\"\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"Post_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_POST\"]._serialized_start = 76\n    _globals[\"_POST\"]._serialized_end = 656\n    _globals[\"_POST_SUBPOST\"]._serialized_start = 424\n    _globals[\"_POST_SUBPOST\"]._serialized_end = 470\n    _globals[\"_POST_SIGNATUREDATA\"]._serialized_start = 472\n    _globals[\"_POST_SIGNATUREDATA\"]._serialized_end = 590\n    _globals[\"_POST_SIGNATUREDATA_SIGNATURECONTENT\"]._serialized_start = 544\n    _globals[\"_POST_SIGNATUREDATA_SIGNATURECONTENT\"]._serialized_end = 590\n    _globals[\"_POST_CHATCONTENT\"]._serialized_start = 592\n    _globals[\"_POST_CHATCONTENT\"]._serialized_end = 621\n    _globals[\"_POST_SPRITEMEMEINFO\"]._serialized_start = 623\n    _globals[\"_POST_SPRITEMEMEINFO\"]._serialized_end = 656\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Rpc.proto",
    "content": "syntax = \"proto3\";\n\nmessage ChunkInfo {\n    int64 stream_id = 1;\n    int64 chunk_id = 2;\n}\n\nmessage EventTimestamp {\n    string event = 1;\n    int64 timestamp_ms = 2;\n}\n\nmessage RpcNotifyMeta {\n    int64 service_id = 1;\n    int64 method_id = 2;\n    int64 log_id = 3;\n    repeated EventTimestamp event_list = 4;\n}\n\nmessage RpcRequestMeta {\n    int64 service_id = 1;\n    int64 method_id = 2;\n    int64 log_id = 3;\n    int32 need_common = 4;\n    repeated EventTimestamp event_list = 5;\n}\n\nmessage RpcResponseMeta {\n    int64 service_id = 1;\n    int64 method_id = 2;\n    int64 log_id = 3;\n    int32 error_code = 4;\n    string error_text = 5;\n    repeated EventTimestamp event_list = 6;\n}\n\nmessage RpcMeta{\n    RpcRequestMeta request = 1;\n    RpcResponseMeta response = 2;\n    optional int32 compress_type = 3;\n    int64 correlation_id = 4;\n    int32 attachment_size = 5;\n    ChunkInfo chunk_info = 6;\n    bytes authentication_data = 7;\n    RpcNotifyMeta notify = 8;\n    int32 accept_compress_type = 9;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Rpc_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\tRpc.proto\"0\\n\\tChunkInfo\\x12\\x11\\n\\tstream_id\\x18\\x01 \\x01(\\x03\\x12\\x10\\n\\x08\\x63hunk_id\\x18\\x02 \\x01(\\x03\"5\\n\\x0e\\x45ventTimestamp\\x12\\r\\n\\x05\\x65vent\\x18\\x01 \\x01(\\t\\x12\\x14\\n\\x0ctimestamp_ms\\x18\\x02 \\x01(\\x03\"k\\n\\rRpcNotifyMeta\\x12\\x12\\n\\nservice_id\\x18\\x01 \\x01(\\x03\\x12\\x11\\n\\tmethod_id\\x18\\x02 \\x01(\\x03\\x12\\x0e\\n\\x06log_id\\x18\\x03 \\x01(\\x03\\x12#\\n\\nevent_list\\x18\\x04 \\x03(\\x0b\\x32\\x0f.EventTimestamp\"\\x81\\x01\\n\\x0eRpcRequestMeta\\x12\\x12\\n\\nservice_id\\x18\\x01 \\x01(\\x03\\x12\\x11\\n\\tmethod_id\\x18\\x02 \\x01(\\x03\\x12\\x0e\\n\\x06log_id\\x18\\x03 \\x01(\\x03\\x12\\x13\\n\\x0bneed_common\\x18\\x04 \\x01(\\x05\\x12#\\n\\nevent_list\\x18\\x05 \\x03(\\x0b\\x32\\x0f.EventTimestamp\"\\x95\\x01\\n\\x0fRpcResponseMeta\\x12\\x12\\n\\nservice_id\\x18\\x01 \\x01(\\x03\\x12\\x11\\n\\tmethod_id\\x18\\x02 \\x01(\\x03\\x12\\x0e\\n\\x06log_id\\x18\\x03 \\x01(\\x03\\x12\\x12\\n\\nerror_code\\x18\\x04 \\x01(\\x05\\x12\\x12\\n\\nerror_text\\x18\\x05 \\x01(\\t\\x12#\\n\\nevent_list\\x18\\x06 \\x03(\\x0b\\x32\\x0f.EventTimestamp\"\\xa9\\x02\\n\\x07RpcMeta\\x12 \\n\\x07request\\x18\\x01 \\x01(\\x0b\\x32\\x0f.RpcRequestMeta\\x12\"\\n\\x08response\\x18\\x02 \\x01(\\x0b\\x32\\x10.RpcResponseMeta\\x12\\x1a\\n\\rcompress_type\\x18\\x03 \\x01(\\x05H\\x00\\x88\\x01\\x01\\x12\\x16\\n\\x0e\\x63orrelation_id\\x18\\x04 \\x01(\\x03\\x12\\x17\\n\\x0f\\x61ttachment_size\\x18\\x05 \\x01(\\x05\\x12\\x1e\\n\\nchunk_info\\x18\\x06 \\x01(\\x0b\\x32\\n.ChunkInfo\\x12\\x1b\\n\\x13\\x61uthentication_data\\x18\\x07 \\x01(\\x0c\\x12\\x1e\\n\\x06notify\\x18\\x08 \\x01(\\x0b\\x32\\x0e.RpcNotifyMeta\\x12\\x1c\\n\\x14\\x61\\x63\\x63\\x65pt_compress_type\\x18\\t \\x01(\\x05\\x42\\x10\\n\\x0e_compress_typeb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"Rpc_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_CHUNKINFO\"]._serialized_start = 13\n    _globals[\"_CHUNKINFO\"]._serialized_end = 61\n    _globals[\"_EVENTTIMESTAMP\"]._serialized_start = 63\n    _globals[\"_EVENTTIMESTAMP\"]._serialized_end = 116\n    _globals[\"_RPCNOTIFYMETA\"]._serialized_start = 118\n    _globals[\"_RPCNOTIFYMETA\"]._serialized_end = 225\n    _globals[\"_RPCREQUESTMETA\"]._serialized_start = 228\n    _globals[\"_RPCREQUESTMETA\"]._serialized_end = 357\n    _globals[\"_RPCRESPONSEMETA\"]._serialized_start = 360\n    _globals[\"_RPCRESPONSEMETA\"]._serialized_end = 509\n    _globals[\"_RPCMETA\"]._serialized_start = 512\n    _globals[\"_RPCMETA\"]._serialized_end = 809\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/SimpleForum.proto",
    "content": "// tbclient.SimpleForum\nsyntax = \"proto3\";\n\nmessage SimpleForum {\n    int64 id = 1;\n    string name = 2;\n    string first_class = 7;\n    string second_class = 8;\n    int32 member_num = 12;\n    int32 post_num = 13;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/SimpleForum_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x11SimpleForum.proto\"x\\n\\x0bSimpleForum\\x12\\n\\n\\x02id\\x18\\x01 \\x01(\\x03\\x12\\x0c\\n\\x04name\\x18\\x02 \\x01(\\t\\x12\\x13\\n\\x0b\\x66irst_class\\x18\\x07 \\x01(\\t\\x12\\x14\\n\\x0csecond_class\\x18\\x08 \\x01(\\t\\x12\\x12\\n\\nmember_num\\x18\\x0c \\x01(\\x05\\x12\\x10\\n\\x08post_num\\x18\\r \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"SimpleForum_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_SIMPLEFORUM\"]._serialized_start = 21\n    _globals[\"_SIMPLEFORUM\"]._serialized_end = 141\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/SubPostList.proto",
    "content": "// tbclient.SubPost\nsyntax = \"proto3\";\n\nimport \"PbContent.proto\";\nimport \"User.proto\";\nimport \"Agree.proto\";\n\nmessage SubPostList {\n    int64 id = 1;\n    repeated PbContent content = 2;\n    uint32 time = 3;\n    int64 author_id = 4;\n    string title = 5;\n    uint32 floor = 6;\n    User author = 7;\n    Agree agree = 9;\n}\n\nmessage SubPost {\n    uint64 pid = 1;\n    repeated SubPostList sub_post_list = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/SubPostList_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom . import Agree_pb2 as Agree__pb2\nfrom . import PbContent_pb2 as PbContent__pb2\nfrom . import User_pb2 as User__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x11SubPostList.proto\\x1a\\x0fPbContent.proto\\x1a\\nUser.proto\\x1a\\x0b\\x41gree.proto\"\\xa3\\x01\\n\\x0bSubPostList\\x12\\n\\n\\x02id\\x18\\x01 \\x01(\\x03\\x12\\x1b\\n\\x07\\x63ontent\\x18\\x02 \\x03(\\x0b\\x32\\n.PbContent\\x12\\x0c\\n\\x04time\\x18\\x03 \\x01(\\r\\x12\\x11\\n\\tauthor_id\\x18\\x04 \\x01(\\x03\\x12\\r\\n\\x05title\\x18\\x05 \\x01(\\t\\x12\\r\\n\\x05\\x66loor\\x18\\x06 \\x01(\\r\\x12\\x15\\n\\x06\\x61uthor\\x18\\x07 \\x01(\\x0b\\x32\\x05.User\\x12\\x15\\n\\x05\\x61gree\\x18\\t \\x01(\\x0b\\x32\\x06.Agree\";\\n\\x07SubPost\\x12\\x0b\\n\\x03pid\\x18\\x01 \\x01(\\x04\\x12#\\n\\rsub_post_list\\x18\\x02 \\x03(\\x0b\\x32\\x0c.SubPostListb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"SubPostList_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_SUBPOSTLIST\"]._serialized_start = 64\n    _globals[\"_SUBPOSTLIST\"]._serialized_end = 227\n    _globals[\"_SUBPOST\"]._serialized_start = 229\n    _globals[\"_SUBPOST\"]._serialized_end = 288\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/ThreadInfo.proto",
    "content": "// tbclient.ThreadInfo\nsyntax = \"proto3\";\n\nimport \"User.proto\";\nimport \"Voice.proto\";\nimport \"PollInfo.proto\";\nimport \"VideoInfo.proto\";\nimport \"PbContent.proto\";\nimport \"Agree.proto\";\nimport \"Media.proto\";\n\nmessage ThreadInfo {\n    int64 id = 1;\n    string title = 3;\n    int32 reply_num = 4;\n    int32 view_num = 5;\n    int32 last_time_int = 7;\n    int32 is_top = 9;\n    int32 is_good = 10;\n    int32 is_voice_thread = 15;\n    User author = 18;\n    User last_replyer = 19;\n    repeated Voice voice_info = 23;\n    int32 thread_type = 26;\n    int64 fid = 27;\n    string fname = 28;\n    int32 is_livepost = 30;\n    int64 first_post_id = 40;\n    int32 create_time = 45;\n    int64 post_id = 52;\n    int64 author_id = 56;\n    uint32 is_ad = 59;\n    PollInfo poll_info = 74;\n    VideoInfo video_info = 79;\n    int32 is_godthread_recommend = 85;\n    Agree agree = 126;\n    int32 share_num = 135;\n\n    message OriginThreadInfo {\n        string title = 1;\n        repeated Media media = 2;\n        string fname = 4;\n        string tid = 5;\n        int64 fid = 7;\n        repeated Voice voice_info = 12;\n        VideoInfo video_info = 13;\n        repeated PbContent content = 14;\n        PollInfo poll_info = 21;\n        int64 pid = 25;\n    }\n    OriginThreadInfo origin_thread_info = 141;\n\n    repeated PbContent first_post_content = 142;\n    int32 is_share_thread = 143;\n    int32 tab_id = 175;\n    int32 is_deleted = 181;\n    int32 is_frs_mask = 198;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/ThreadInfo_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom . import Agree_pb2 as Agree__pb2\nfrom . import Media_pb2 as Media__pb2\nfrom . import PbContent_pb2 as PbContent__pb2\nfrom . import PollInfo_pb2 as PollInfo__pb2\nfrom . import User_pb2 as User__pb2\nfrom . import VideoInfo_pb2 as VideoInfo__pb2\nfrom . import Voice_pb2 as Voice__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b\"\\n\\x10ThreadInfo.proto\\x1a\\nUser.proto\\x1a\\x0bVoice.proto\\x1a\\x0ePollInfo.proto\\x1a\\x0fVideoInfo.proto\\x1a\\x0fPbContent.proto\\x1a\\x0b\\x41gree.proto\\x1a\\x0bMedia.proto\\\"\\xbd\\x07\\n\\nThreadInfo\\x12\\n\\n\\x02id\\x18\\x01 \\x01(\\x03\\x12\\r\\n\\x05title\\x18\\x03 \\x01(\\t\\x12\\x11\\n\\treply_num\\x18\\x04 \\x01(\\x05\\x12\\x10\\n\\x08view_num\\x18\\x05 \\x01(\\x05\\x12\\x15\\n\\rlast_time_int\\x18\\x07 \\x01(\\x05\\x12\\x0e\\n\\x06is_top\\x18\\t \\x01(\\x05\\x12\\x0f\\n\\x07is_good\\x18\\n \\x01(\\x05\\x12\\x17\\n\\x0fis_voice_thread\\x18\\x0f \\x01(\\x05\\x12\\x15\\n\\x06\\x61uthor\\x18\\x12 \\x01(\\x0b\\x32\\x05.User\\x12\\x1b\\n\\x0clast_replyer\\x18\\x13 \\x01(\\x0b\\x32\\x05.User\\x12\\x1a\\n\\nvoice_info\\x18\\x17 \\x03(\\x0b\\x32\\x06.Voice\\x12\\x13\\n\\x0bthread_type\\x18\\x1a \\x01(\\x05\\x12\\x0b\\n\\x03\\x66id\\x18\\x1b \\x01(\\x03\\x12\\r\\n\\x05\\x66name\\x18\\x1c \\x01(\\t\\x12\\x13\\n\\x0bis_livepost\\x18\\x1e \\x01(\\x05\\x12\\x15\\n\\rfirst_post_id\\x18( \\x01(\\x03\\x12\\x13\\n\\x0b\\x63reate_time\\x18- \\x01(\\x05\\x12\\x0f\\n\\x07post_id\\x18\\x34 \\x01(\\x03\\x12\\x11\\n\\tauthor_id\\x18\\x38 \\x01(\\x03\\x12\\r\\n\\x05is_ad\\x18; \\x01(\\r\\x12\\x1c\\n\\tpoll_info\\x18J \\x01(\\x0b\\x32\\t.PollInfo\\x12\\x1e\\n\\nvideo_info\\x18O \\x01(\\x0b\\x32\\n.VideoInfo\\x12\\x1e\\n\\x16is_godthread_recommend\\x18U \\x01(\\x05\\x12\\x15\\n\\x05\\x61gree\\x18~ \\x01(\\x0b\\x32\\x06.Agree\\x12\\x12\\n\\tshare_num\\x18\\x87\\x01 \\x01(\\x05\\x12\\x39\\n\\x12origin_thread_info\\x18\\x8d\\x01 \\x01(\\x0b\\x32\\x1c.ThreadInfo.OriginThreadInfo\\x12'\\n\\x12\\x66irst_post_content\\x18\\x8e\\x01 \\x03(\\x0b\\x32\\n.PbContent\\x12\\x18\\n\\x0fis_share_thread\\x18\\x8f\\x01 \\x01(\\x05\\x12\\x0f\\n\\x06tab_id\\x18\\xaf\\x01 \\x01(\\x05\\x12\\x13\\n\\nis_deleted\\x18\\xb5\\x01 \\x01(\\x05\\x12\\x14\\n\\x0bis_frs_mask\\x18\\xc6\\x01 \\x01(\\x05\\x1a\\xe5\\x01\\n\\x10OriginThreadInfo\\x12\\r\\n\\x05title\\x18\\x01 \\x01(\\t\\x12\\x15\\n\\x05media\\x18\\x02 \\x03(\\x0b\\x32\\x06.Media\\x12\\r\\n\\x05\\x66name\\x18\\x04 \\x01(\\t\\x12\\x0b\\n\\x03tid\\x18\\x05 \\x01(\\t\\x12\\x0b\\n\\x03\\x66id\\x18\\x07 \\x01(\\x03\\x12\\x1a\\n\\nvoice_info\\x18\\x0c \\x03(\\x0b\\x32\\x06.Voice\\x12\\x1e\\n\\nvideo_info\\x18\\r \\x01(\\x0b\\x32\\n.VideoInfo\\x12\\x1b\\n\\x07\\x63ontent\\x18\\x0e \\x03(\\x0b\\x32\\n.PbContent\\x12\\x1c\\n\\tpoll_info\\x18\\x15 \\x01(\\x0b\\x32\\t.PollInfo\\x12\\x0b\\n\\x03pid\\x18\\x19 \\x01(\\x03\\x62\\x06proto3\"\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"ThreadInfo_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_THREADINFO\"]._serialized_start = 122\n    _globals[\"_THREADINFO\"]._serialized_end = 1079\n    _globals[\"_THREADINFO_ORIGINTHREADINFO\"]._serialized_start = 850\n    _globals[\"_THREADINFO_ORIGINTHREADINFO\"]._serialized_end = 1079\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/User.proto",
    "content": "// tbclient.User\nsyntax = \"proto3\";\n\nmessage User {\n    int64 id = 2;\n    string name = 3;\n    string name_show = 4;\n    string portrait = 5;\n    message Icon {\n        string name = 1;\n    }\n    repeated Icon iconinfo = 17;\n    int32 is_coreuser = 20;\n    int32 level_id = 23;\n    int32 is_bawu = 25;\n    string bawu_type = 26;\n    string BDUSS = 29;\n    int32 fans_num = 30;\n    int32 concern_num = 31;\n    int32 sex = 32;\n    int32 my_like_num = 33;\n    string intro = 34;\n    int32 post_num = 37;\n    string tb_age = 38;\n    int32 gender = 42;\n    message PrivSets {\n        int32 location = 1;\n        int32 like = 2;\n        int32 group = 3;\n        int32 post = 4;\n        int32 friend = 5;\n        int32 live = 6;\n        int32 reply = 7;\n        int32 bazhu_show_inside = 8;\n        int32 bazhu_show_outside = 9;\n    }\n    PrivSets priv_sets = 45;\n    int32 is_friend = 46;\n    message LikeForumInfo {\n        string forum_name = 1;\n        uint64 forum_id = 2;\n    }\n    repeated LikeForumInfo likeForum = 47;\n    int32 is_guanfang = 52;\n    message UserVipInfo {\n        uint32 v_status = 1;\n        uint32 v_level = 5;\n    }\n    UserVipInfo vipInfo = 61;\n    message TshowInfo {\n        string name = 2;\n    }\n    repeated TshowInfo new_tshow_icon = 65;\n    int32 is_fans = 91;\n    message NewGodInfo {\n        int32 status = 1;\n        uint32 field_id = 2;\n        string field_name = 3;\n    }\n    NewGodInfo new_god_data = 101;\n    int32 is_default_avatar = 106;\n    string tieba_uid = 120;\n    string ip_address = 127;\n    message UserGrowth {\n        uint32 level_id = 1;\n    }\n    UserGrowth user_growth = 137;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/User_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\nUser.proto\"\\xd6\\x08\\n\\x04User\\x12\\n\\n\\x02id\\x18\\x02 \\x01(\\x03\\x12\\x0c\\n\\x04name\\x18\\x03 \\x01(\\t\\x12\\x11\\n\\tname_show\\x18\\x04 \\x01(\\t\\x12\\x10\\n\\x08portrait\\x18\\x05 \\x01(\\t\\x12\\x1c\\n\\x08iconinfo\\x18\\x11 \\x03(\\x0b\\x32\\n.User.Icon\\x12\\x13\\n\\x0bis_coreuser\\x18\\x14 \\x01(\\x05\\x12\\x10\\n\\x08level_id\\x18\\x17 \\x01(\\x05\\x12\\x0f\\n\\x07is_bawu\\x18\\x19 \\x01(\\x05\\x12\\x11\\n\\tbawu_type\\x18\\x1a \\x01(\\t\\x12\\r\\n\\x05\\x42\\x44USS\\x18\\x1d \\x01(\\t\\x12\\x10\\n\\x08\\x66\\x61ns_num\\x18\\x1e \\x01(\\x05\\x12\\x13\\n\\x0b\\x63oncern_num\\x18\\x1f \\x01(\\x05\\x12\\x0b\\n\\x03sex\\x18  \\x01(\\x05\\x12\\x13\\n\\x0bmy_like_num\\x18! \\x01(\\x05\\x12\\r\\n\\x05intro\\x18\" \\x01(\\t\\x12\\x10\\n\\x08post_num\\x18% \\x01(\\x05\\x12\\x0e\\n\\x06tb_age\\x18& \\x01(\\t\\x12\\x0e\\n\\x06gender\\x18* \\x01(\\x05\\x12!\\n\\tpriv_sets\\x18- \\x01(\\x0b\\x32\\x0e.User.PrivSets\\x12\\x11\\n\\tis_friend\\x18. \\x01(\\x05\\x12&\\n\\tlikeForum\\x18/ \\x03(\\x0b\\x32\\x13.User.LikeForumInfo\\x12\\x13\\n\\x0bis_guanfang\\x18\\x34 \\x01(\\x05\\x12\"\\n\\x07vipInfo\\x18= \\x01(\\x0b\\x32\\x11.User.UserVipInfo\\x12\\'\\n\\x0enew_tshow_icon\\x18\\x41 \\x03(\\x0b\\x32\\x0f.User.TshowInfo\\x12\\x0f\\n\\x07is_fans\\x18[ \\x01(\\x05\\x12&\\n\\x0cnew_god_data\\x18\\x65 \\x01(\\x0b\\x32\\x10.User.NewGodInfo\\x12\\x19\\n\\x11is_default_avatar\\x18j \\x01(\\x05\\x12\\x11\\n\\ttieba_uid\\x18x \\x01(\\t\\x12\\x12\\n\\nip_address\\x18\\x7f \\x01(\\t\\x12&\\n\\x0buser_growth\\x18\\x89\\x01 \\x01(\\x0b\\x32\\x10.User.UserGrowth\\x1a\\x14\\n\\x04Icon\\x12\\x0c\\n\\x04name\\x18\\x01 \\x01(\\t\\x1a\\xab\\x01\\n\\x08PrivSets\\x12\\x10\\n\\x08location\\x18\\x01 \\x01(\\x05\\x12\\x0c\\n\\x04like\\x18\\x02 \\x01(\\x05\\x12\\r\\n\\x05group\\x18\\x03 \\x01(\\x05\\x12\\x0c\\n\\x04post\\x18\\x04 \\x01(\\x05\\x12\\x0e\\n\\x06\\x66riend\\x18\\x05 \\x01(\\x05\\x12\\x0c\\n\\x04live\\x18\\x06 \\x01(\\x05\\x12\\r\\n\\x05reply\\x18\\x07 \\x01(\\x05\\x12\\x19\\n\\x11\\x62\\x61zhu_show_inside\\x18\\x08 \\x01(\\x05\\x12\\x1a\\n\\x12\\x62\\x61zhu_show_outside\\x18\\t \\x01(\\x05\\x1a\\x35\\n\\rLikeForumInfo\\x12\\x12\\n\\nforum_name\\x18\\x01 \\x01(\\t\\x12\\x10\\n\\x08\\x66orum_id\\x18\\x02 \\x01(\\x04\\x1a\\x30\\n\\x0bUserVipInfo\\x12\\x10\\n\\x08v_status\\x18\\x01 \\x01(\\r\\x12\\x0f\\n\\x07v_level\\x18\\x05 \\x01(\\r\\x1a\\x19\\n\\tTshowInfo\\x12\\x0c\\n\\x04name\\x18\\x02 \\x01(\\t\\x1a\\x42\\n\\nNewGodInfo\\x12\\x0e\\n\\x06status\\x18\\x01 \\x01(\\x05\\x12\\x10\\n\\x08\\x66ield_id\\x18\\x02 \\x01(\\r\\x12\\x12\\n\\nfield_name\\x18\\x03 \\x01(\\t\\x1a\\x1e\\n\\nUserGrowth\\x12\\x10\\n\\x08level_id\\x18\\x01 \\x01(\\rb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"User_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_USER\"]._serialized_start = 15\n    _globals[\"_USER\"]._serialized_end = 1125\n    _globals[\"_USER_ICON\"]._serialized_start = 699\n    _globals[\"_USER_ICON\"]._serialized_end = 719\n    _globals[\"_USER_PRIVSETS\"]._serialized_start = 722\n    _globals[\"_USER_PRIVSETS\"]._serialized_end = 893\n    _globals[\"_USER_LIKEFORUMINFO\"]._serialized_start = 895\n    _globals[\"_USER_LIKEFORUMINFO\"]._serialized_end = 948\n    _globals[\"_USER_USERVIPINFO\"]._serialized_start = 950\n    _globals[\"_USER_USERVIPINFO\"]._serialized_end = 998\n    _globals[\"_USER_TSHOWINFO\"]._serialized_start = 1000\n    _globals[\"_USER_TSHOWINFO\"]._serialized_end = 1025\n    _globals[\"_USER_NEWGODINFO\"]._serialized_start = 1027\n    _globals[\"_USER_NEWGODINFO\"]._serialized_end = 1093\n    _globals[\"_USER_USERGROWTH\"]._serialized_start = 1095\n    _globals[\"_USER_USERGROWTH\"]._serialized_end = 1125\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/VideoInfo.proto",
    "content": "// tbclient.VideoInfo\nsyntax = \"proto3\";\n\nmessage VideoInfo {\n    string video_url = 2;\n    uint32 video_duration = 3;\n    uint32 video_width = 4;\n    uint32 video_height = 5;\n    string thumbnail_url = 6;\n    int32 play_count = 10;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/VideoInfo_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x0fVideoInfo.proto\"\\x8c\\x01\\n\\tVideoInfo\\x12\\x11\\n\\tvideo_url\\x18\\x02 \\x01(\\t\\x12\\x16\\n\\x0evideo_duration\\x18\\x03 \\x01(\\r\\x12\\x13\\n\\x0bvideo_width\\x18\\x04 \\x01(\\r\\x12\\x14\\n\\x0cvideo_height\\x18\\x05 \\x01(\\r\\x12\\x15\\n\\rthumbnail_url\\x18\\x06 \\x01(\\t\\x12\\x12\\n\\nplay_count\\x18\\n \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"VideoInfo_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_VIDEOINFO\"]._serialized_start = 20\n    _globals[\"_VIDEOINFO\"]._serialized_end = 160\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Voice.proto",
    "content": "// tbclient.Voice\nsyntax = \"proto3\";\n\nmessage Voice {\n    int32 during_time = 2;\n    string voice_md5 = 3;\n}\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/Voice_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x0bVoice.proto\"/\\n\\x05Voice\\x12\\x13\\n\\x0b\\x64uring_time\\x18\\x02 \\x01(\\x05\\x12\\x11\\n\\tvoice_md5\\x18\\x03 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"Voice_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_VOICE\"]._serialized_start = 15\n    _globals[\"_VOICE\"]._serialized_end = 62\n"
  },
  {
    "path": "src/aiotieba/api/_protobuf/__init__.py",
    "content": ""
  },
  {
    "path": "src/aiotieba/api/add_bawu/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/add_bawu/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...enums import BawuType\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n\nasync def request(http_core: HttpCore, fid: int, user_name: str, bawu_type: BawuType) -> BoolResponse:\n    data = [\n        (\"fn\", \"-\"),\n        (\"fid\", fid),\n        (\"team_un\", user_name),\n        (\"type\", bawu_type),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/bawuteamadd\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/add_bawu_blacklist/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/add_bawu_blacklist/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"errno\"]:\n        raise TiebaServerError(code, res_json[\"errmsg\"])\n\n\nasync def request(http_core: HttpCore, fname: str, user_id: int) -> BoolResponse:\n    data = [\n        (\"tbs\", http_core.account.tbs),\n        (\"user_id\", user_id),\n        (\"word\", fname),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/bawu2/platform/addBlack\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/add_blacklist_old/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/add_blacklist_old/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n    if code := int(res_json[\"errorno\"]):\n        raise TiebaServerError(code, res_json[\"errmsg\"])\n\n\nasync def request(http_core: HttpCore, user_id: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"mute_user\", user_id),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/user/userMuteAdd\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/add_post/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\n"
  },
  {
    "path": "src/aiotieba/api/add_post/_api.py",
    "content": "import datetime\nimport time\n\nimport yarl\n\nfrom ...__version__ import __version__\nfrom ...const import APP_BASE_HOST\nfrom ...core import Account, HttpCore, WsCore\nfrom ...exception import BoolResponse, TiebaServerError, TiebaValueError\nfrom .protobuf import AddPostReqIdl_pb2, AddPostResIdl_pb2\n\nCMD = 309731\n\n\ndef parse_body(body: bytes) -> None:\n    res_proto = AddPostResIdl_pb2.AddPostResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n    if int(res_proto.data.info.need_vcode):\n        raise TiebaValueError(\"Need verify code\")\n\n\ndef pack_proto(account: Account, fname: str, fid: int, tid: int, show_name: str, content: str) -> bytes:\n    req_proto = AddPostReqIdl_pb2.AddPostReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.common._client_type = 2\n    req_proto.data.common._client_version = \"12.35.1.0\"\n    req_proto.data.common._client_id = account.client_id\n    req_proto.data.common._phone_imei = \"000000000000000\"\n    req_proto.data.common._from = \"1008621x\"\n    req_proto.data.common.cuid = account.cuid_galaxy2\n    current_ts = time.time()\n    current_tsms = int(current_ts * 1000)\n    current_dt = datetime.datetime.fromtimestamp(current_ts)\n    req_proto.data.common._timestamp = current_tsms\n    req_proto.data.common.model = \"SM-G988N\"\n    req_proto.data.common.tbs = account.tbs\n    req_proto.data.common.net_type = 1\n    req_proto.data.common.pversion = \"1.0.3\"\n    req_proto.data.common._os_version = \"9\"\n    req_proto.data.common.brand = \"samsung\"\n    req_proto.data.common.lego_lib_version = \"3.0.0\"\n    req_proto.data.common.applist = \"\"\n    req_proto.data.common.stoken = account.STOKEN\n    req_proto.data.common.z_id = account.z_id\n    req_proto.data.common.cuid_galaxy2 = account.cuid_galaxy2\n    req_proto.data.common.cuid_gid = \"\"\n    req_proto.data.common.c3_aid = account.c3_aid\n    req_proto.data.common.sample_id = account.sample_id\n    req_proto.data.common.scr_w = 720\n    req_proto.data.common.scr_w = 1280\n    req_proto.data.common.scr_dip = 1.5\n    req_proto.data.common.q_type = 0\n    req_proto.data.common.is_teenager = 0\n    req_proto.data.common.sdk_ver = \"2.34.0\"\n    req_proto.data.common.framework_ver = \"3340042\"\n    req_proto.data.common.naws_game_ver = \"1038000\"\n    req_proto.data.common.active_timestamp = current_tsms - 86400 * 30\n    req_proto.data.common.first_install_time = current_tsms - 86400 * 30\n    req_proto.data.common.last_update_time = current_tsms - 86400 * 30\n    req_proto.data.common.event_day = f\"{current_dt.year}{current_dt.month}{current_dt.day}\"\n    req_proto.data.common.android_id = account.android_id\n    req_proto.data.common.cmode = 1\n    req_proto.data.common.start_scheme = \"\"\n    req_proto.data.common.start_type = 1\n    req_proto.data.common.idfv = \"0\"\n    req_proto.data.common.extra = \"\"\n    req_proto.data.common.user_agent = f\"aiotieba/{__version__}\"\n    req_proto.data.common.personalized_rec_switch = 1\n    req_proto.data.common.device_score = \"0.4\"\n\n    req_proto.data.anonymous = \"1\"\n    req_proto.data.can_no_forum = \"0\"\n    req_proto.data.is_feedback = \"0\"\n    req_proto.data.takephoto_num = \"0\"\n    req_proto.data.entrance_type = \"0\"\n    req_proto.data.vcode_tag = \"12\"\n    req_proto.data.new_vcode = \"1\"\n    req_proto.data.content = content\n    req_proto.data.fid = str(fid)\n    req_proto.data.v_fid = \"\"\n    req_proto.data.v_fname = \"\"\n    req_proto.data.kw = fname\n    req_proto.data.is_barrage = \"0\"\n    req_proto.data.from_fourm_id = str(fid)\n    req_proto.data.tid = str(tid)\n    req_proto.data.is_ad = \"0\"\n    req_proto.data.post_from = \"3\"\n    req_proto.data.name_show = show_name\n    req_proto.data.is_pictxt = \"0\"\n    req_proto.data.show_custom_figure = 0\n    req_proto.data.is_show_bless = 0\n\n    return req_proto.SerializeToString()\n\n\nasync def request_http(\n    http_core: HttpCore, fname: str, fid: int, tid: int, show_name: str, content: str\n) -> BoolResponse:\n    data = pack_proto(http_core.account, fname, fid, tid, show_name, content)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/post/add\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n\n\nasync def request_ws(ws_core: WsCore, fname: str, fid: int, tid: int, show_name: str, content: str) -> BoolResponse:\n    data = pack_proto(ws_core.account, fname, fid, tid, show_name, content)\n\n    response = await ws_core.send(data, CMD)\n    parse_body(await response.read())\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/add_post/protobuf/AddPostReqIdl.proto",
    "content": "// tbclient.AddPost.AddPostReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage AddPostReqIdl {\n    message DataReq {\n        CommonReq common = 1;\n        string anonymous = 6;\n        string can_no_forum = 7;\n        string is_feedback = 8;\n        string takephoto_num = 9;\n        string entrance_type = 10;\n        string vcode_tag = 16;\n        string new_vcode = 18;\n        string content = 19;\n        string fid = 26;\n        string v_fid = 28;\n        string v_fname = 29;\n        string kw = 30;\n        string is_barrage = 31;\n        string barrage_time = 32;\n        string from_fourm_id = 44;\n        string tid = 45;\n        string is_ad = 51;\n        string post_from = 55;\n        string name_show = 58;\n        string is_pictxt = 60;\n        int32 show_custom_figure = 64;\n        int32 is_show_bless = 67;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/add_post/protobuf/AddPostReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13\\x41\\x64\\x64PostReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\x82\\x04\\n\\rAddPostReqIdl\\x12$\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x16.AddPostReqIdl.DataReq\\x1a\\xca\\x03\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x01 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\x11\\n\\tanonymous\\x18\\x06 \\x01(\\t\\x12\\x14\\n\\x0c\\x63\\x61n_no_forum\\x18\\x07 \\x01(\\t\\x12\\x13\\n\\x0bis_feedback\\x18\\x08 \\x01(\\t\\x12\\x15\\n\\rtakephoto_num\\x18\\t \\x01(\\t\\x12\\x15\\n\\rentrance_type\\x18\\n \\x01(\\t\\x12\\x11\\n\\tvcode_tag\\x18\\x10 \\x01(\\t\\x12\\x11\\n\\tnew_vcode\\x18\\x12 \\x01(\\t\\x12\\x0f\\n\\x07\\x63ontent\\x18\\x13 \\x01(\\t\\x12\\x0b\\n\\x03\\x66id\\x18\\x1a \\x01(\\t\\x12\\r\\n\\x05v_fid\\x18\\x1c \\x01(\\t\\x12\\x0f\\n\\x07v_fname\\x18\\x1d \\x01(\\t\\x12\\n\\n\\x02kw\\x18\\x1e \\x01(\\t\\x12\\x12\\n\\nis_barrage\\x18\\x1f \\x01(\\t\\x12\\x14\\n\\x0c\\x62\\x61rrage_time\\x18  \\x01(\\t\\x12\\x15\\n\\rfrom_fourm_id\\x18, \\x01(\\t\\x12\\x0b\\n\\x03tid\\x18- \\x01(\\t\\x12\\r\\n\\x05is_ad\\x18\\x33 \\x01(\\t\\x12\\x11\\n\\tpost_from\\x18\\x37 \\x01(\\t\\x12\\x11\\n\\tname_show\\x18: \\x01(\\t\\x12\\x11\\n\\tis_pictxt\\x18< \\x01(\\t\\x12\\x1a\\n\\x12show_custom_figure\\x18@ \\x01(\\x05\\x12\\x15\\n\\ris_show_bless\\x18\\x43 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"AddPostReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_ADDPOSTREQIDL\"]._serialized_start = 41\n    _globals[\"_ADDPOSTREQIDL\"]._serialized_end = 555\n    _globals[\"_ADDPOSTREQIDL_DATAREQ\"]._serialized_start = 97\n    _globals[\"_ADDPOSTREQIDL_DATAREQ\"]._serialized_end = 555\n"
  },
  {
    "path": "src/aiotieba/api/add_post/protobuf/AddPostResIdl.proto",
    "content": "// tbclient.AddPost.AddPostResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage AddPostResIdl {\n    Error error = 1;\n    message DataRes {\n        string video_id = 4;\n        string msg = 5;\n        string pre_msg = 6;\n        string color_msg = 7;\n        message PostAntiInfo {\n            string need_vcode = 3;\n        }\n        PostAntiInfo info = 14;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/add_post/protobuf/AddPostResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13\\x41\\x64\\x64PostResIdl.proto\\x1a\\x0b\\x45rror.proto\"\\xf2\\x01\\n\\rAddPostResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12$\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x16.AddPostResIdl.DataRes\\x1a\\xa3\\x01\\n\\x07\\x44\\x61taRes\\x12\\x10\\n\\x08video_id\\x18\\x04 \\x01(\\t\\x12\\x0b\\n\\x03msg\\x18\\x05 \\x01(\\t\\x12\\x0f\\n\\x07pre_msg\\x18\\x06 \\x01(\\t\\x12\\x11\\n\\tcolor_msg\\x18\\x07 \\x01(\\t\\x12\\x31\\n\\x04info\\x18\\x0e \\x01(\\x0b\\x32#.AddPostResIdl.DataRes.PostAntiInfo\\x1a\"\\n\\x0cPostAntiInfo\\x12\\x12\\n\\nneed_vcode\\x18\\x03 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"AddPostResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_ADDPOSTRESIDL\"]._serialized_start = 37\n    _globals[\"_ADDPOSTRESIDL\"]._serialized_end = 279\n    _globals[\"_ADDPOSTRESIDL_DATARES\"]._serialized_start = 116\n    _globals[\"_ADDPOSTRESIDL_DATARES\"]._serialized_end = 279\n    _globals[\"_ADDPOSTRESIDL_DATARES_POSTANTIINFO\"]._serialized_start = 245\n    _globals[\"_ADDPOSTRESIDL_DATARES_POSTANTIINFO\"]._serialized_end = 279\n"
  },
  {
    "path": "src/aiotieba/api/agree/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/agree/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(\n    http_core: HttpCore, tid: int, pid: int, is_comment: bool, is_disagree: bool, is_undo: bool\n) -> BoolResponse:\n    if pid:\n        obj_type = 2 if is_comment else 1\n    else:\n        obj_type = 3\n\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"agree_type\", 5 if is_disagree else 2),\n        (\"cuid\", http_core.account.cuid_galaxy2),\n        (\"obj_type\", obj_type),\n        (\"op_type\", str(int(is_undo))),\n        (\"post_id\", pid),\n        (\"tbs\", http_core.account.tbs),\n        (\"thread_id\", tid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/agree/opAgree\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/block/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/block/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int, portrait: str, day: int, reason: str) -> BoolResponse:\n    is_svip_block = 0 if day in [1, 3, 10] else 1\n\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"day\", day),\n        (\"fid\", fid),\n        (\"is_loop_ban\", is_svip_block),\n        (\"ntn\", \"banid\"),\n        (\"portrait\", portrait),\n        (\"reason\", reason),\n        (\"tbs\", http_core.account.tbs),\n        (\"word\", \"-\"),\n        (\"z\", 6),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/commitprison\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/del_bawu/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/del_bawu/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...enums import BawuType\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n\nasync def request(http_core: HttpCore, fid: int, portrait: str, bawu_type: BawuType) -> BoolResponse:\n    data = [\n        (\"fn\", \"-\"),\n        (\"fid\", fid),\n        (\"team_un\", \"-\"),\n        (\"team_uid\", portrait),\n        (\"bawu_type\", bawu_type),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/bawuteamclear\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/del_bawu_blacklist/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/del_bawu_blacklist/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"errno\"]:\n        raise TiebaServerError(code, res_json[\"errmsg\"])\n\n\nasync def request(http_core: HttpCore, fname: str, user_id: int) -> BoolResponse:\n    data = [\n        (\"word\", fname),\n        (\"tbs\", http_core.account.tbs),\n        (\"list[]\", user_id),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/bawu2/platform/cancelBlack\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/del_blacklist_old/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/del_blacklist_old/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n    if code := int(res_json[\"errorno\"]):\n        raise TiebaServerError(code, res_json[\"errmsg\"])\n\n\nasync def request(http_core: HttpCore, user_id: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"mute_user\", user_id),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/user/userMuteDel\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/del_post/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/del_post/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int, tid: int, pid: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"fid\", fid),\n        (\"pid\", pid),\n        (\"tbs\", http_core.account.tbs),\n        (\"z\", tid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/delpost\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/del_posts/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/del_posts/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\nif TYPE_CHECKING:\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int, tid: int, pids: list[int], block: bool) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"forum_id\", fid),\n        (\"post_ids\", \",\".join(map(str, pids))),\n        (\"tbs\", http_core.account.tbs),\n        (\"thread_id\", tid),\n        (\"type\", 2 if block else 1),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/multiDelPost\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/del_thread/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/del_thread/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int, tid: int, is_hide: bool) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"fid\", fid),\n        (\"is_frs_mask\", int(is_hide)),\n        (\"tbs\", http_core.account.tbs),\n        (\"z\", tid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/delthread\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/del_threads/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/del_threads/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\nif TYPE_CHECKING:\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int, tids: list[int], block: bool) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"forum_id\", fid),\n        (\"tbs\", http_core.account.tbs),\n        (\"thread_ids\", \",\".join(map(str, tids))),\n        (\"type\", 2 if block else 1),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/multiDelThread\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/dislike_forum/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/dislike_forum/_api.py",
    "content": "import time\n\nimport yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import pack_json, parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\n            \"dislike\",\n            pack_json([{\"tid\": 1, \"dislike_ids\": 7, \"fid\": fid, \"click_time\": int(time.time() * 1000)}]),\n        ),\n        (\"dislike_from\", \"homepage\"),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/excellent/submitDislike\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/follow_forum/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/follow_forum/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n    if \"error\" in res_json and (code := int(res_json[\"error\"][\"errno\"])):\n        raise TiebaServerError(code, res_json[\"error\"][\"errmsg\"])\n\n\nasync def request(http_core: HttpCore, fid: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"fid\", fid),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/forum/like\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/follow_user/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/follow_user/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, portrait: str) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"portrait\", portrait),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/user/follow\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/get_ats/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import At, Ats\n"
  },
  {
    "path": "src/aiotieba/api/get_ats/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import Ats\n\n\ndef parse_body(body: bytes) -> Ats:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    ats = Ats.from_json(res_json)\n\n    return ats\n\n\nasync def request(http_core: HttpCore, pn: int) -> Ats:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"pn\", pn),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/feed/atme\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_ats/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...enums import PrivLike, PrivReply\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass Page_at:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n\n    has_more: bool = False\n    has_prev: int = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Page_at:\n        current_page = int(data_map[\"current_page\"])\n        has_more = bool(int(data_map[\"has_more\"]))\n        has_prev = bool(int(data_map[\"has_prev\"]))\n        return Page_at(current_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass UserInfo_at:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_at:\n        user_id = int(data_map[\"id\"])\n        portrait = data_map[\"portrait\"]\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_map[\"name\"]\n        nick_name_new = data_map[\"name_show\"]\n        if priv_sets := data_map[\"priv_sets\"]:\n            priv_like = PrivLike(int(priv_sets.get(\"like\", 1)))\n            priv_reply = PrivReply(int(priv_sets.get(\"reply\", 1)))\n        else:\n            priv_like = PrivLike.PUBLIC\n            priv_reply = PrivReply.ALL\n\n        return UserInfo_at(user_id, portrait, user_name, nick_name_new, priv_like, priv_reply)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_at) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass At:\n    \"\"\"\n    @信息\n\n    Attributes:\n        text (str): 文本内容\n\n        fname (str): 所在贴吧名\n        tid (int): 所在主题帖id\n        pid (int): 回复id\n        user (UserInfo_at): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        is_comment (bool): 是否楼中楼\n        is_thread (bool): 是否主题帖\n\n        create_time (int): 创建时间\n    \"\"\"\n\n    text: str = \"\"\n\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_at = dcs.field(default_factory=UserInfo_at)\n\n    is_comment: bool = False\n    is_thread: bool = False\n\n    create_time: int = 0\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> At:\n        text = data_map[\"content\"]\n        fname = data_map[\"fname\"]\n        tid = int(data_map[\"thread_id\"])\n        pid = int(data_map[\"post_id\"])\n        user = UserInfo_at.from_json(data_map[\"replyer\"])\n        is_comment = bool(int(data_map[\"is_floor\"]))\n        is_thread = bool(int(data_map[\"is_first_post\"]))\n        create_time = int(data_map[\"time\"])\n        return At(text, fname, tid, pid, user, is_comment, is_thread, create_time)\n\n    def __eq__(self, obj: At) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n\n@dcs.dataclass\nclass Ats(TbErrorExt, Containers[At]):\n    \"\"\"\n    @信息列表\n\n    Attributes:\n        objs (list[At]): @信息列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_at): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_at = dcs.field(default_factory=Page_at)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Ats:\n        objs = [At.from_json(m) for m in data_map.get(\"at_list\", [])]\n        page = Page_at.from_json(data_map[\"page\"])\n        return Ats(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_blacklist/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import BawuBlacklistUser, BawuBlacklistUsers\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_blacklist/_api.py",
    "content": "import bs4\nimport yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ._classdef import BawuBlacklistUsers\n\n\ndef parse_body(body: bytes) -> BawuBlacklistUsers:\n    soup = bs4.BeautifulSoup(body, \"lxml\")\n    bawu_blacklist_users = BawuBlacklistUsers.from_xml(soup)\n\n    return bawu_blacklist_users\n\n\nasync def request(http_core: HttpCore, fname: str, pn: int) -> BawuBlacklistUsers:\n    params = [\n        (\"word\", fname),\n        (\"pn\", pn),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/bawu2/platform/listBlackUser\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=16 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_blacklist/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    import bs4\n\n\n@dcs.dataclass\nclass BawuBlacklistUser:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> BawuBlacklistUser:\n        user_info_item = data_tag.previous_sibling.input\n        user_name = user_info_item[\"data-user-name\"]\n        user_id = int(user_info_item[\"data-user-id\"])\n        portrait = data_tag.a[\"href\"][14:-17]\n        return BawuBlacklistUser(user_id, portrait, user_name)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: BawuBlacklistUser) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def log_name(self) -> str:\n        return str(self)\n\n\n@dcs.dataclass\nclass Page_bwblacklist:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    def from_xml(data_soup: bs4.BeautifulSoup) -> Page_bwblacklist:\n        total_count_tag = data_soup.find(\"div\", class_=\"breadcrumbs\")\n        total_count = int(total_count_tag.em.text)\n\n        page_tag = data_soup.find(\"div\", class_=\"tbui_pagination\").find(\"li\", class_=\"active\")\n        if page_tag is None:\n            if total_count != 0:\n                current_page = 1\n                total_page = 1\n            else:\n                current_page = 0\n                total_page = 0\n        else:\n            current_page = int(page_tag.text)\n            total_page_item = page_tag.parent.next_sibling\n            total_page = int(total_page_item.text[1:-1])\n\n        has_more = current_page < total_page\n        has_prev = current_page > 1\n\n        return Page_bwblacklist(current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass BawuBlacklistUsers(TbErrorExt, Containers[BawuBlacklistUser]):\n    \"\"\"\n    吧务黑名单列表\n\n    Attributes:\n        objs (list[BawuBlacklistUser]): 吧务黑名单列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_bwblacklist): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_bwblacklist = dcs.field(default_factory=Page_bwblacklist)\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> BawuBlacklistUsers:\n        objs = [BawuBlacklistUser.from_xml(t) for t in data_soup(\"td\", class_=\"left_cell\")]\n        page = Page_bwblacklist.from_xml(data_soup)\n        return BawuBlacklistUsers(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_info/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import BawuInfo, UserInfo_bawu\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_info/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import BawuInfo\nfrom .protobuf import GetBawuInfoReqIdl_pb2, GetBawuInfoResIdl_pb2\n\nCMD = 301007\n\n\ndef pack_proto(fid: int) -> bytes:\n    req_proto = GetBawuInfoReqIdl_pb2.GetBawuInfoReqIdl()\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.fid = fid\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> BawuInfo:\n    res_proto = GetBawuInfoResIdl_pb2.GetBawuInfoResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    bawu_info = BawuInfo.from_proto(data_proto)\n\n    return bawu_info\n\n\nasync def request_http(http_core: HttpCore, fid: int) -> BawuInfo:\n    data = pack_proto(fid)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/f/forum/getBawuInfo\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, fid: int) -> BawuInfo:\n    data = pack_proto(fid)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_info/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from .._classdef import TypeMessage\n\n\n@dcs.dataclass\nclass UserInfo_bawu:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        level (int): 等级\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    level: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_bawu:\n        user_id = data_proto.user_id\n        portrait = data_proto.portrait\n        user_name = data_proto.user_name\n        nick_name_new = data_proto.name_show\n        level = data_proto.user_level\n        return UserInfo_bawu(user_id, portrait, user_name, nick_name_new, level)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_bawu) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass BawuInfo:\n    \"\"\"\n    吧务团队信息\n\n    Attributes:\n        all (list[UserInfo_bawu]): 所有吧务\n\n        admin (list[UserInfo_bawu]): 大吧主\n        manager (list[UserInfo_bawu]): 小吧主\n        voice_editor (list[UserInfo_bawu]): 语音小编\n        image_editor (list[UserInfo_bawu]): 图片小编\n        video_editor (list[UserInfo_bawu]): 视频小编\n        broadcast_editor (list[UserInfo_bawu]): 广播小编\n        journal_chief_editor (list[UserInfo_bawu]): 吧刊主编\n        journal_editor (list[UserInfo_bawu]): 吧刊小编\n        profess_admin (list[UserInfo_bawu]): 职业吧主\n        fourth_admin (list[UserInfo_bawu]): 第四吧主\n    \"\"\"\n\n    all: list[UserInfo_bawu] = dcs.field(default_factory=list, repr=False)\n\n    admin: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    manager: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    voice_editor: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    image_editor: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    video_editor: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    broadcast_editor: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    journal_chief_editor: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    journal_editor: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    profess_admin: list[UserInfo_bawu] = dcs.field(default_factory=list)\n    fourth_admin: list[UserInfo_bawu] = dcs.field(default_factory=list)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> BawuInfo:\n        all_ = []\n        r_protos = data_proto.bawu_team_info.bawu_team_list\n        _dict = {r_proto.role_name: [UserInfo_bawu.from_proto(p) for p in r_proto.role_info] for r_proto in r_protos}\n\n        def extract(role_name: str) -> list[UserInfo_bawu]:\n            if users := _dict.get(role_name):\n                all_.extend(users)\n            else:\n                users = []\n            return users\n\n        admin = extract(\"吧主\")\n        manager = extract(\"小吧主\")\n        voice_editor = extract(\"语音小编\")\n        image_editor = extract(\"图片小编\")\n        video_editor = extract(\"视频小编\")\n        broadcast_editor = extract(\"广播小编\")\n        journal_chief_editor = extract(\"吧刊主编\")\n        journal_editor = extract(\"吧刊小编\")\n        profess_admin = extract(\"职业吧主\")\n        fourth_admin = extract(\"第四吧主\")\n\n        return BawuInfo(\n            all_,\n            admin,\n            manager,\n            voice_editor,\n            image_editor,\n            video_editor,\n            broadcast_editor,\n            journal_chief_editor,\n            journal_editor,\n            profess_admin,\n            fourth_admin,\n        )\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_info/protobuf/GetBawuInfoReqIdl.proto",
    "content": "// tbclient.GetBawuInfo.GetBawuInfoReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage GetBawuInfoReqIdl {\n    message DataReq {\n        CommonReq common = 1;\n        uint64 fid = 2;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_info/protobuf/GetBawuInfoReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x17GetBawuInfoReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"q\\n\\x11GetBawuInfoReqIdl\\x12(\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1a.GetBawuInfoReqIdl.DataReq\\x1a\\x32\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x01 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\x0b\\n\\x03\\x66id\\x18\\x02 \\x01(\\x04\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetBawuInfoReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETBAWUINFOREQIDL\"]._serialized_start = 44\n    _globals[\"_GETBAWUINFOREQIDL\"]._serialized_end = 157\n    _globals[\"_GETBAWUINFOREQIDL_DATAREQ\"]._serialized_start = 107\n    _globals[\"_GETBAWUINFOREQIDL_DATAREQ\"]._serialized_end = 157\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_info/protobuf/GetBawuInfoResIdl.proto",
    "content": "// tbclient.GetBawuInfo.GetBawuInfoResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage GetBawuInfoResIdl {\n    message DataRes {\n        message BawuTeam {\n            int32 total_num = 1;\n            message BawuRoleDes {\n                string role_name = 1;\n                message BawuRoleInfoPub {\n                    int64 user_id = 2;\n                    string portrait = 5;\n                    int32 user_level = 6;\n                    string user_name = 8;\n                    string name_show = 9;\n                }\n                repeated BawuRoleInfoPub role_info = 2;\n            }\n            repeated BawuRoleDes bawu_team_list = 2;\n        }\n        BawuTeam bawu_team_info = 1;\n    }\n    DataRes data = 1;\n    Error error = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_info/protobuf/GetBawuInfoResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x17GetBawuInfoResIdl.proto\\x1a\\x0b\\x45rror.proto\"\\xed\\x03\\n\\x11GetBawuInfoResIdl\\x12(\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1a.GetBawuInfoResIdl.DataRes\\x12\\x15\\n\\x05\\x65rror\\x18\\x02 \\x01(\\x0b\\x32\\x06.Error\\x1a\\x96\\x03\\n\\x07\\x44\\x61taRes\\x12;\\n\\x0e\\x62\\x61wu_team_info\\x18\\x01 \\x01(\\x0b\\x32#.GetBawuInfoResIdl.DataRes.BawuTeam\\x1a\\xcd\\x02\\n\\x08\\x42\\x61wuTeam\\x12\\x11\\n\\ttotal_num\\x18\\x01 \\x01(\\x05\\x12G\\n\\x0e\\x62\\x61wu_team_list\\x18\\x02 \\x03(\\x0b\\x32/.GetBawuInfoResIdl.DataRes.BawuTeam.BawuRoleDes\\x1a\\xe4\\x01\\n\\x0b\\x42\\x61wuRoleDes\\x12\\x11\\n\\trole_name\\x18\\x01 \\x01(\\t\\x12R\\n\\trole_info\\x18\\x02 \\x03(\\x0b\\x32?.GetBawuInfoResIdl.DataRes.BawuTeam.BawuRoleDes.BawuRoleInfoPub\\x1an\\n\\x0f\\x42\\x61wuRoleInfoPub\\x12\\x0f\\n\\x07user_id\\x18\\x02 \\x01(\\x03\\x12\\x10\\n\\x08portrait\\x18\\x05 \\x01(\\t\\x12\\x12\\n\\nuser_level\\x18\\x06 \\x01(\\x05\\x12\\x11\\n\\tuser_name\\x18\\x08 \\x01(\\t\\x12\\x11\\n\\tname_show\\x18\\t \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetBawuInfoResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETBAWUINFORESIDL\"]._serialized_start = 41\n    _globals[\"_GETBAWUINFORESIDL\"]._serialized_end = 534\n    _globals[\"_GETBAWUINFORESIDL_DATARES\"]._serialized_start = 128\n    _globals[\"_GETBAWUINFORESIDL_DATARES\"]._serialized_end = 534\n    _globals[\"_GETBAWUINFORESIDL_DATARES_BAWUTEAM\"]._serialized_start = 201\n    _globals[\"_GETBAWUINFORESIDL_DATARES_BAWUTEAM\"]._serialized_end = 534\n    _globals[\"_GETBAWUINFORESIDL_DATARES_BAWUTEAM_BAWUROLEDES\"]._serialized_start = 306\n    _globals[\"_GETBAWUINFORESIDL_DATARES_BAWUTEAM_BAWUROLEDES\"]._serialized_end = 534\n    _globals[\"_GETBAWUINFORESIDL_DATARES_BAWUTEAM_BAWUROLEDES_BAWUROLEINFOPUB\"]._serialized_start = 424\n    _globals[\"_GETBAWUINFORESIDL_DATARES_BAWUTEAM_BAWUROLEDES_BAWUROLEINFOPUB\"]._serialized_end = 534\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_perm/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import BawuPerm\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_perm/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import BawuPerm\n\n\ndef parse_body(body: bytes) -> BawuPerm:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n    data_map = res_json[\"data\"]\n    perm = BawuPerm.from_json(data_map)\n\n    return perm\n\n\nasync def request(http_core: HttpCore, fid: int, portrait: str) -> BawuPerm:\n    params = [\n        (\"forum_id\", fid),\n        (\"portrait\", portrait),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/getAuthToolPerm\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_perm/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...enums import BawuPermType\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass BawuPerm(TbErrorExt):\n    \"\"\"\n    吧务已分配的权限\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n        perms (BawuPermType): 吧务已分配的权限\n    \"\"\"\n\n    perms: BawuPermType = BawuPermType.NULL\n\n    def from_json(data_map: Mapping) -> BawuPerm:\n        perms = BawuPermType.NULL\n\n        for cate in [\"category_user\", \"category_thread\"]:\n            perm_setting = data_map[\"perm_setting\"]\n            for unblock_perm_dict in perm_setting[cate]:\n                if not unblock_perm_dict[\"switch\"]:\n                    continue\n\n                perm_idx: int = unblock_perm_dict[\"perm\"] - 2\n                perm = [\n                    BawuPermType.RECOVER_APPEAL,\n                    BawuPermType.RECOVER,\n                    BawuPermType.UNBLOCK,\n                    BawuPermType.UNBLOCK_APPEAL,\n                ][perm_idx]\n\n                perms |= perm\n\n        return BawuPerm(perms)\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_postlogs/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Postlog, Postlogs\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_postlogs/_api.py",
    "content": "from __future__ import annotations\n\nimport time\nfrom typing import TYPE_CHECKING\nfrom urllib.parse import quote\n\nimport bs4\nimport yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...enums import BawuSearchType\nfrom ._classdef import Postlogs\n\nif TYPE_CHECKING:\n    import datetime\n\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> Postlogs:\n    soup = bs4.BeautifulSoup(body, \"lxml\")\n    bawu_postlogs = Postlogs.from_xml(soup)\n\n    return bawu_postlogs\n\n\nasync def request(\n    http_core: HttpCore,\n    fname: str,\n    pn: int,\n    search_value: str,\n    search_type: BawuSearchType,\n    start_dt: datetime.datetime | None,\n    end_dt: datetime.datetime | None,\n    op_type: int,\n) -> Postlogs:\n    params = [\n        (\"word\", fname),\n        (\"pn\", pn),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    if op_type:\n        params.append((\"op_type\", op_type))\n\n    if search_value:\n        if search_type == BawuSearchType.USER:\n            search_value = quote(search_value)\n            extend_params = [\n                (\"svalue\", search_value),\n                (\"stype\", \"post_uname\"),\n            ]\n        else:\n            extend_params = [\n                (\"svalue\", search_value),\n                (\"stype\", \"op_uname\"),\n            ]\n        params += extend_params\n\n    if start_dt:\n        begin = int(start_dt.timestamp())\n        if end_dt is None:\n            end = int(time.time())\n        else:\n            end = int(end_dt.timestamp())\n        extend_params = [\n            (\"end\", end),\n            (\"begin\", begin),\n        ]\n        params += extend_params\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/bawu2/platform/listPostLog\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=32 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_postlogs/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom datetime import datetime\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom ...helper import default_datetime\nfrom .._classdef import Containers\nfrom .._classdef.contents import _IMAGEHASH_EXP\n\nif TYPE_CHECKING:\n    import bs4\n\n\n@dcs.dataclass\nclass Media_postlog:\n    \"\"\"\n    媒体信息\n\n    Attributes:\n        src (str): 小图链接\n        origin_src (str): 原图链接\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    hash: str = \"\"\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> Media_postlog:\n        if img_item := data_tag.img:\n            src = img_item[\"original\"]\n            hash_ = _IMAGEHASH_EXP.search(src).group(1)\n        else:\n            src = \"\"\n            hash_ = \"\"\n        origin_src = data_tag[\"href\"]\n        return Media_postlog(src, origin_src, hash_)\n\n\n@dcs.dataclass\nclass Postlog:\n    \"\"\"\n    吧务帖子管理日志\n\n    Attributes:\n        text (str): 文本内容\n        title (str): 所在主题帖标题\n        medias (list[Media_postlog]): 媒体列表\n\n        tid (int): 所在主题帖id\n        pid (int): 回复id\n\n        op_type (str): 操作类型\n        post_portrait (str): 发帖用户的portrait\n        post_time (datetime): 发帖时间 不含年份\n        op_user_name (str): 操作人用户名\n        op_time (datetime): 操作时间\n    \"\"\"\n\n    text: str = \"\"\n    title: str = \"\"\n    medias: list[Media_postlog] = dcs.field(default_factory=list)\n\n    tid: int = 0\n    pid: int = 0\n\n    op_type: str = \"\"\n    post_portrait: str = \"\"\n    post_time: datetime = dcs.field(default_factory=default_datetime)\n    op_user_name: str = \"\"\n    op_time: datetime = dcs.field(default_factory=default_datetime)\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> Postlog:\n        left_cell_item = data_tag.td\n\n        post_meta_item = left_cell_item.find(\"div\", class_=\"post_meta\")\n        post_user_item = post_meta_item.div\n        post_portrait = post_user_item.a[\"href\"][14:-17]\n        post_time_item = post_meta_item.time\n        post_time_str = post_time_item.text\n        post_time_month = int(post_time_str[:2])\n        post_time_day = int(post_time_str[3:5])\n        post_time_hour = int(post_time_str[7:9])\n        post_time_minute = int(post_time_str[10:])\n        post_time = datetime(1904, post_time_month, post_time_day, post_time_hour, post_time_minute)\n\n        post_content_item = post_meta_item.next_sibling\n        title_item = post_content_item.h1.a\n        url: str = title_item[\"href\"]\n        tid = int(url[3 : url.find(\"?\")])\n        pid = int(url[url.rfind(\"#\") + 1 :])\n        title: str = title_item[\"title\"]\n\n        text_item = post_content_item.div\n        text = text_item.string[12:]\n\n        if pid == tid or not title.startswith(\"回复：\"):\n            # is thread\n            pid = 0\n            text = f\"{title}\\n{text}\"\n        else:\n            title = title.removeprefix(\"回复：\")\n\n        if media_list_item := text_item.next_sibling:\n            medias = [Media_postlog.from_xml(tag) for tag in media_list_item.find_all(\"a\")]\n        else:\n            medias = []\n\n        op_type_item = left_cell_item.next_sibling\n        op_type = op_type_item.string\n\n        op_user_name_item = op_type_item.next_sibling\n        op_user_name = op_user_name_item.string\n\n        op_time_item = op_user_name_item.next_sibling\n        op_time = datetime.strptime(op_time_item.text, \"%Y-%m-%d%H:%M\")\n\n        return Postlog(text, title, medias, tid, pid, op_type, post_portrait, post_time, op_user_name, op_time)\n\n\n@dcs.dataclass\nclass Page_postlog:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> Page_postlog:\n        total_count_tag = data_soup.find(\"div\", class_=\"breadcrumbs\")\n        total_count = int(total_count_tag.em.text)\n\n        page_tag = data_soup.find(\"div\", class_=\"tbui_pagination\").find(\"li\", class_=\"active\")\n        if page_tag is None:\n            if total_count != 0:\n                current_page = 1\n                total_page = 1\n            else:\n                current_page = 0\n                total_page = 0\n        else:\n            current_page = int(page_tag.text)\n            total_page_item = page_tag.parent.next_sibling\n            total_page = int(total_page_item.text[1:-1])\n\n        has_more = current_page < total_page\n        has_prev = current_page > 1\n\n        return Page_postlog(current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Postlogs(TbErrorExt, Containers[Postlog]):\n    \"\"\"\n    吧务帖子管理日志表\n\n    Attributes:\n        objs (list[Postlog]): 吧务帖子管理日志表\n        err (Exception | None): 捕获的异常\n\n        page (Page_postlog): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_postlog = dcs.field(default_factory=Page_postlog)\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> Postlogs:\n        objs = [Postlog.from_xml(t) for t in data_soup.find(\"tbody\").find_all(\"tr\")]\n        page = Page_postlog.from_xml(data_soup)\n        return Postlogs(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_userlogs/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Userlog, Userlogs\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_userlogs/_api.py",
    "content": "from __future__ import annotations\n\nimport time\nfrom typing import TYPE_CHECKING\nfrom urllib.parse import quote\n\nimport bs4\nimport yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...enums import BawuSearchType\nfrom ._classdef import Userlogs\n\nif TYPE_CHECKING:\n    import datetime\n\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> Userlogs:\n    soup = bs4.BeautifulSoup(body, \"lxml\")\n    bawu_userlogs = Userlogs.from_xml(soup)\n\n    return bawu_userlogs\n\n\nasync def request(\n    http_core: HttpCore,\n    fname: str,\n    pn: int,\n    search_value: str,\n    search_type: BawuSearchType,\n    start_dt: datetime.datetime | None,\n    end_dt: datetime.datetime | None,\n    op_type: int,\n) -> Userlogs:\n    params = [\n        (\"word\", fname),\n        (\"pn\", pn),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    if op_type:\n        params.append((\"op_type\", op_type))\n\n    if search_value:\n        if search_type == BawuSearchType.USER:\n            search_value = quote(search_value)\n            extend_params = [\n                (\"svalue\", search_value),\n                (\"stype\", \"post_uname\"),\n            ]\n        else:\n            extend_params = [\n                (\"svalue\", search_value),\n                (\"stype\", \"op_uname\"),\n            ]\n        params += extend_params\n\n    if start_dt:\n        begin = int(start_dt.timestamp())\n        if end_dt is None:\n            end = int(time.time())\n        else:\n            end = int(end_dt.timestamp())\n        extend_params = [\n            (\"end\", end),\n            (\"begin\", begin),\n        ]\n        params += extend_params\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/bawu2/platform/listUserLog\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=16 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_bawu_userlogs/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom datetime import datetime\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom ...helper import default_datetime\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    import bs4\n\n\n@dcs.dataclass\nclass Userlog:\n    \"\"\"\n    吧务用户管理日志\n\n    Attributes:\n        op_type (str): 操作类型\n        op_duration (int): 操作作用时长\n        user_portrait (str): 被操作用户的portrait\n        op_user_name (str): 操作人用户名\n        op_time (datetime.datetime): 操作时间\n    \"\"\"\n\n    op_type: str = \"\"\n    op_duration: int = 0\n    user_portrait: str = \"\"\n    op_user_name: str = \"\"\n    op_time: datetime = dcs.field(default_factory=default_datetime)\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> Userlog:\n        left_cell_item = data_tag.td\n\n        post_user_item = left_cell_item.a\n        user_portrait = post_user_item[\"href\"][14:-17]\n\n        op_type_item = left_cell_item.next_sibling.next_sibling\n        op_type = op_type_item.string\n\n        op_duration_item = op_type_item.next_sibling\n        op_duration = op_duration_item.string.replace(\" \", \"\")\n        op_duration = 0 if \"天\" not in op_duration else int(op_duration[:-1])\n\n        op_user_name_item = op_duration_item.next_sibling\n        op_user_name = op_user_name_item.string\n\n        op_time_item = op_user_name_item.next_sibling\n        op_time = datetime.strptime(op_time_item.text, \"%Y-%m-%d %H:%M\")\n\n        return Userlog(op_type, op_duration, user_portrait, op_user_name, op_time)\n\n\n@dcs.dataclass\nclass Page_userlog:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> Page_userlog:\n        total_count_tag = data_soup.find(\"div\", class_=\"breadcrumbs\")\n        total_count = int(total_count_tag.em.text)\n\n        page_tag = data_soup.find(\"div\", class_=\"tbui_pagination\").find(\"li\", class_=\"active\")\n        if page_tag is None:\n            if total_count != 0:\n                current_page = 1\n                total_page = 1\n            else:\n                current_page = 0\n                total_page = 0\n        else:\n            current_page = int(page_tag.text)\n            total_page_item = page_tag.parent.next_sibling\n            total_page = int(total_page_item.text[1:-1])\n\n        has_more = current_page < total_page\n        has_prev = current_page > 1\n\n        return Page_userlog(current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Userlogs(TbErrorExt, Containers[Userlog]):\n    \"\"\"\n    吧务用户管理日志表\n\n    Attributes:\n        objs (list[Postlog]): 吧务用户管理日志表\n        err (Exception | None): 捕获的异常\n\n        page (Page_userlog): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_userlog = dcs.field(default_factory=Page_userlog)\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> Userlogs:\n        objs = [Userlog.from_xml(t) for t in data_soup.find(\"tbody\").find_all(\"tr\")]\n        page = Page_userlog.from_xml(data_soup)\n        return Userlogs(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import BlacklistUser, BlacklistUsers\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import BlacklistUsers\n\n\ndef parse_body(body: bytes) -> BlacklistUsers:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    blacklist_users = BlacklistUsers.from_json(res_json)\n\n    return blacklist_users\n\n\nasync def request(http_core: HttpCore) -> BlacklistUsers:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/user/userBlackPage\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=32 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...enums import BlacklistType\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass BlacklistUser:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        btype (BlacklistType): 黑名单类型 FOLLOW禁止关注 INTERACT禁止互动 CHAT禁止私信\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    btype: BlacklistType = BlacklistType.NULL\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> BlacklistUser:\n        user_id = int(data_map[\"uid\"])\n        portrait = data_map[\"portrait\"]\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_map[\"user_name\"]\n        nick_name_new = data_map[\"name_show\"]\n\n        btype = BlacklistType.NULL\n        perm: dict[str, str] = data_map[\"perm_list\"]\n        if int(perm[\"follow\"]):\n            btype |= BlacklistType.FOLLOW\n        if int(perm[\"chat\"]):\n            btype |= BlacklistType.CHAT\n        if int(perm[\"interact\"]):\n            btype |= BlacklistType.INTERACT\n\n        return BlacklistUser(user_id, portrait, user_name, nick_name_new, btype)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: BlacklistUser) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass BlacklistUsers(TbErrorExt, Containers[BlacklistUser]):\n    \"\"\"\n    新版用户黑名单列表\n\n    Attributes:\n        objs (list[BlacklistUser]): 新版用户黑名单列表\n        err (Exception | None): 捕获的异常\n    \"\"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> BlacklistUsers:\n        objs = [BlacklistUser.from_json(m) for m in data_map.get(\"user_perm_list\", [])]\n        return BlacklistUsers(objs)\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist_old/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import BlacklistOldUser, BlacklistOldUsers\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist_old/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import Account, HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import BlacklistOldUsers\nfrom .protobuf import UserMuteQueryReqIdl_pb2, UserMuteQueryResIdl_pb2\n\nCMD = 303028\n\n\ndef pack_proto(account: Account, pn: int, rn: int) -> bytes:\n    req_proto = UserMuteQueryReqIdl_pb2.UserMuteQueryReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.pn = pn\n    req_proto.data.rn = rn\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(proto: bytes) -> BlacklistOldUsers:\n    res_proto = UserMuteQueryResIdl_pb2.UserMuteQueryResIdl()\n    res_proto.ParseFromString(proto)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    blacklist_users = BlacklistOldUsers.from_proto(data_proto)\n\n    return blacklist_users\n\n\nasync def request_http(http_core: HttpCore, pn: int, rn: int) -> BlacklistOldUsers:\n    data = pack_proto(http_core.account, pn, rn)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/user/userMuteQuery\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, pn: int, rn: int) -> BlacklistOldUsers:\n    data = pack_proto(ws_core.account, pn, rn)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist_old/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers, TypeMessage\n\n\n@dcs.dataclass\nclass BlacklistOldUser:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_old (str): 旧版昵称\n\n        until_time (int): 解禁时间 10位时间戳 以秒为单位\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_old: str = \"\"\n\n    until_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> BlacklistOldUser:\n        user_id = data_proto.user_id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.user_name\n        nick_name_old = data_proto.name_show\n        until_time = data_proto.mute_time\n        return BlacklistOldUser(user_id, portrait, user_name, nick_name_old, until_time)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: BlacklistOldUser) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_old\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_old}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass Page_blacklist:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Page_blacklist:\n        current_page = data_proto.current_page\n        has_more = bool(data_proto.has_more)\n        has_prev = bool(data_proto.has_prev)\n        return Page_blacklist(current_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass BlacklistOldUsers(TbErrorExt, Containers[BlacklistOldUser]):\n    \"\"\"\n    旧版用户黑名单列表\n\n    Attributes:\n        objs (list[BlacklistOldUser]): 旧版用户黑名单列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_blacklist): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_blacklist = dcs.field(default_factory=Page_blacklist)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> BlacklistOldUsers:\n        objs = [BlacklistOldUser.from_proto(p) for p in data_proto.mute_user]\n        page = Page_blacklist.from_proto(data_proto.page)\n        return BlacklistOldUsers(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist_old/protobuf/UserMuteQueryReqIdl.proto",
    "content": "// tbclient.UserMuteQuery.UserMuteQueryReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage UserMuteQueryReqIdl {\n    message DataReq {\n        CommonReq common = 2;\n        uint32 pn = 4;\n        uint32 rn = 5;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist_old/protobuf/UserMuteQueryReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x19UserMuteQueryReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\x80\\x01\\n\\x13UserMuteQueryReqIdl\\x12*\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1c.UserMuteQueryReqIdl.DataReq\\x1a=\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x02 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\n\\n\\x02pn\\x18\\x04 \\x01(\\r\\x12\\n\\n\\x02rn\\x18\\x05 \\x01(\\rb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"UserMuteQueryReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_USERMUTEQUERYREQIDL\"]._serialized_start = 47\n    _globals[\"_USERMUTEQUERYREQIDL\"]._serialized_end = 175\n    _globals[\"_USERMUTEQUERYREQIDL_DATAREQ\"]._serialized_start = 114\n    _globals[\"_USERMUTEQUERYREQIDL_DATAREQ\"]._serialized_end = 175\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist_old/protobuf/UserMuteQueryResIdl.proto",
    "content": "// tbclient.UserMuteQuery.UserMuteQueryResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"Page.proto\";\n\nmessage UserMuteQueryResIdl {\n    message DataRes {\n        message MuteUser {\n            int64 user_id = 1;\n            string user_name = 2;\n            int32 mute_time = 3;\n            string portrait = 4;\n            string name_show = 5;\n        }\n        repeated MuteUser mute_user = 1;\n        Page page = 2;\n    }\n    DataRes data = 1;\n    Error error = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_blacklist_old/protobuf/UserMuteQueryResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import Page_pb2 as Page__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x19UserMuteQueryResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nPage.proto\"\\x9b\\x02\\n\\x13UserMuteQueryResIdl\\x12*\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1c.UserMuteQueryResIdl.DataRes\\x12\\x15\\n\\x05\\x65rror\\x18\\x02 \\x01(\\x0b\\x32\\x06.Error\\x1a\\xc0\\x01\\n\\x07\\x44\\x61taRes\\x12\\x38\\n\\tmute_user\\x18\\x01 \\x03(\\x0b\\x32%.UserMuteQueryResIdl.DataRes.MuteUser\\x12\\x13\\n\\x04page\\x18\\x02 \\x01(\\x0b\\x32\\x05.Page\\x1a\\x66\\n\\x08MuteUser\\x12\\x0f\\n\\x07user_id\\x18\\x01 \\x01(\\x03\\x12\\x11\\n\\tuser_name\\x18\\x02 \\x01(\\t\\x12\\x11\\n\\tmute_time\\x18\\x03 \\x01(\\x05\\x12\\x10\\n\\x08portrait\\x18\\x04 \\x01(\\t\\x12\\x11\\n\\tname_show\\x18\\x05 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"UserMuteQueryResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_USERMUTEQUERYRESIDL\"]._serialized_start = 55\n    _globals[\"_USERMUTEQUERYRESIDL\"]._serialized_end = 338\n    _globals[\"_USERMUTEQUERYRESIDL_DATARES\"]._serialized_start = 146\n    _globals[\"_USERMUTEQUERYRESIDL_DATARES\"]._serialized_end = 338\n    _globals[\"_USERMUTEQUERYRESIDL_DATARES_MUTEUSER\"]._serialized_start = 236\n    _globals[\"_USERMUTEQUERYRESIDL_DATARES_MUTEUSER\"]._serialized_end = 338\n"
  },
  {
    "path": "src/aiotieba/api/get_blocks/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Block, Blocks\n"
  },
  {
    "path": "src/aiotieba/api/get_blocks/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import Blocks\n\n\ndef parse_body(body: bytes) -> Blocks:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n    blocks = Blocks.from_json(res_json)\n\n    return blocks\n\n\nasync def request(http_core: HttpCore, fid: int, name: str, pn: int) -> Blocks:\n    params = [\n        (\"fn\", \"-\"),\n        (\"fid\", fid),\n        (\"word\", name),\n        (\"is_ajax\", 1),\n        (\"pn\", pn),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/bawublock\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=32 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_blocks/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nimport bs4\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass Block:\n    \"\"\"\n    待解封用户信息\n\n    Attributes:\n        user_id (int): user_id\n        user_name (str): 用户名\n        nick_name_old (str): 旧版昵称\n        day (int): 封禁天数\n    \"\"\"\n\n    user_id: int = 0\n    user_name: str = \"\"\n    nick_name_old: str = \"\"\n    day: int = 0\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> Block:\n        id_tag = data_tag.a\n        user_id = int(id_tag[\"attr-uid\"])\n        user_name = id_tag[\"attr-un\"]\n        nick_name_old = id_tag[\"attr-nn\"]\n        day = int(id_tag[\"attr-blockday\"])\n        return Block(user_id, user_name, nick_name_old, day)\n\n\n@dcs.dataclass\nclass Page_block:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Page_block:\n        page_size = data_map[\"size\"]\n        current_page = data_map[\"pn\"]\n        total_page = data_map[\"total_page\"]\n        total_count = data_map[\"total_count\"]\n        has_more = data_map[\"have_next\"]\n        has_prev = current_page > 1\n        return Page_block(page_size, current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Blocks(TbErrorExt, Containers[Block]):\n    \"\"\"\n    待恢复帖子列表\n\n    Attributes:\n        objs (list[Block]): 待恢复帖子列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_block): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_block = dcs.field(default_factory=Page_block)\n\n    def from_json(data_map: Mapping) -> Blocks:\n        data_soup = bs4.BeautifulSoup(data_map[\"data\"][\"content\"], \"lxml\")\n        objs = [Block.from_xml(t) for t in data_soup(\"li\")]\n        page = Page_block.from_json(data_map[\"data\"][\"page\"])\n        return Blocks(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_cid/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/get_cid/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\n\nif TYPE_CHECKING:\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> list[dict[str, str | int]]:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    cates = res_json[\"cates\"]\n\n    return cates\n\n\nasync def request(http_core: HttpCore, fname: str) -> list[dict[str, str | int]]:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"word\", fname),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/goodlist\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_comments/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import Comment, Comments, Post_c, Thread_c, UserInfo_c, UserInfo_cp, UserInfo_ct\n"
  },
  {
    "path": "src/aiotieba/api/get_comments/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, STABLE_VERSION\nfrom ...core import HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import Comments\nfrom .protobuf import PbFloorReqIdl_pb2, PbFloorResIdl_pb2\n\nCMD = 302002\n\n\ndef pack_proto(tid: int, pid: int, pn: int, is_comment: bool) -> bytes:\n    req_proto = PbFloorReqIdl_pb2.PbFloorReqIdl()\n    req_proto.data.common._client_type = 2\n    req_proto.data.common._client_version = STABLE_VERSION\n    req_proto.data.kz = tid\n    if is_comment:\n        req_proto.data.spid = pid\n    else:\n        req_proto.data.pid = pid\n    req_proto.data.pn = pn\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> Comments:\n    res_proto = PbFloorResIdl_pb2.PbFloorResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    comments = Comments.from_proto(data_proto)\n\n    return comments\n\n\nasync def request_http(http_core: HttpCore, tid: int, pid: int, pn: int, is_comment: bool) -> Comments:\n    data = pack_proto(tid, pid, pn, is_comment)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/f/pb/floor\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, tid: int, pid: int, pn: int, is_comment: bool) -> Comments:\n    data = pack_proto(tid, pid, pn, is_comment)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_comments/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\n\nfrom ...enums import Gender, PrivLike, PrivReply, ThreadType\nfrom ...exception import TbErrorExt\nfrom ...helper import deprecated\nfrom ...logging import get_logger as LOG\nfrom .._classdef import Containers, TypeMessage\nfrom .._classdef.contents import (\n    _IMAGEHASH_EXP,\n    FragAt,\n    FragEmoji,\n    FragLink,\n    FragText,\n    FragTiebaPlus,\n    FragUnknown,\n    FragVoice,\n    TypeFragment,\n    TypeFragText,\n)\n\nFragText_c = FragText_cp = FragText\nFragEmoji_c = FragEmoji_cp = FragEmoji\nFragAt_c = FragAt_cp = FragAt\nFragLink_c = FragLink_cp = FragLink\nFragTiebaPlus_c = FragTiebaPlus_cp = FragTiebaPlus\nFragVoice_c = FragVoice_cp = FragVoice\n\n\n@dcs.dataclass\nclass Contents_c(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_c]): 表情碎片列表\n        ats (list[FragAt_c]): @碎片列表\n        links (list[FragLink_c]): 链接碎片列表\n        tiebapluses (list[FragTiebaPlus_c]): 贴吧plus碎片列表\n        voice (FragVoice_c): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_c] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_c] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_c] = dcs.field(default_factory=list, repr=False)\n    tiebapluses: list[FragTiebaPlus_c] = dcs.field(default_factory=list, repr=False)\n    voice: FragVoice_c = dcs.field(default_factory=FragVoice_c, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_c:\n        content_protos = data_proto.content\n\n        texts = []\n        emojis = []\n        ats = []\n        links = []\n        tiebapluses = []\n        voice = FragVoice_c()\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条\n                if _type in [0, 9, 18, 27]:\n                    frag = FragText_c.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_c.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                elif _type == 4:\n                    frag = FragAt_c.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_c.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 10:  # voice\n                    frag = FragVoice_c.from_proto(proto)\n                    nonlocal voice\n                    voice = frag\n                    yield frag\n                # 35|36:tid=7769728331 / 37:tid=7760184147\n                elif _type in [35, 36, 37]:\n                    frag = FragTiebaPlus_c.from_proto(proto)\n                    tiebapluses.append(frag)\n                    texts.append(frag)\n                    yield frag\n                # outdated tiebaplus\n                elif _type == 34:\n                    continue\n                else:\n                    yield FragUnknown.from_proto(proto)\n\n        objs = list(_frags())\n\n        return Contents_c(objs, texts, emojis, ats, links, tiebapluses, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass UserInfo_c:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        level (int): 等级\n        gender (Gender): 性别\n        icons (list[str]): 印记信息\n\n        is_bawu (bool): 是否吧务\n        is_vip (bool): 是否超级会员\n        is_god (bool): 是否大神\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    level: int = 0\n    gender: Gender = Gender.UNKNOWN\n    icons: list[str] = dcs.field(default_factory=list)\n\n    is_bawu: bool = False\n    is_vip: bool = False\n    is_god: bool = False\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_c:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        level = data_proto.level_id\n        gender = Gender(data_proto.gender)\n        icons = [name for i in data_proto.iconinfo if (name := i.name)]\n        is_bawu = bool(data_proto.is_bawu)\n        is_vip = bool(data_proto.new_tshow_icon)\n        is_god = bool(data_proto.new_god_data.status)\n        priv_like = PrivLike(priv_like) if (priv_like := data_proto.priv_sets.like) else PrivLike.PUBLIC\n        priv_reply = PrivReply(priv_reply) if (priv_reply := data_proto.priv_sets.reply) else PrivReply.ALL\n        return UserInfo_c(\n            user_id,\n            portrait,\n            user_name,\n            nick_name_new,\n            level,\n            gender,\n            icons,\n            is_bawu,\n            is_vip,\n            is_god,\n            priv_like,\n            priv_reply,\n        )\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_c) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass Comment:\n    \"\"\"\n    楼中楼信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_c): 正文内容碎片列表\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 所在主题帖id\n        ppid (int): 所在楼层id\n        pid (int): 楼中楼id\n        user (UserInfo_c): 发布者的用户信息\n        author_id (int): 发布者的user_id\n        reply_to_id (int): 被回复者的user_id\n\n        floor (int): 所在楼层数\n        agree (int): 点赞数\n        disagree (int): 点踩数\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n        is_thread_author (bool): 是否楼主\n    \"\"\"\n\n    contents: Contents_c = dcs.field(default_factory=Contents_c)\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    ppid: int = 0\n    pid: int = 0\n    user: UserInfo_c = dcs.field(default_factory=UserInfo_c)\n    reply_to_id: int = 0\n\n    floor: int = 0\n    agree: int = 0\n    disagree: int = 0\n    create_time: int = 0\n    is_thread_author: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> None:\n        contents = Contents_c.from_proto(data_proto)\n\n        reply_to_id = 0\n        if contents:\n            first_frag = contents[0]\n            if (\n                isinstance(first_frag, FragText_c)\n                and first_frag.text == \"回复 \"\n                and (reply_to_id := data_proto.content[1].uid)\n            ):\n                reply_to_id = reply_to_id\n                if isinstance(contents[1], FragAt_c):\n                    del contents.ats[0]\n                contents.objs = contents.objs[2:]\n                contents.texts = contents.texts[2:]\n                if contents.texts:\n                    first_text_frag = contents.texts[0]\n                    first_text_frag.text = first_text_frag.text.removeprefix(\" :\")\n\n        pid = data_proto.id\n        user = UserInfo_c.from_proto(data_proto.author)\n        agree = data_proto.agree.agree_num\n        disagree = data_proto.agree.disagree_num\n        create_time = data_proto.time\n\n        return Comment(contents, 0, \"\", 0, 0, pid, user, reply_to_id, 0, agree, disagree, create_time, False)\n\n    def __eq__(self, obj: Comment) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @property\n    def text(self) -> str:\n        return self.contents.text\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n\n@dcs.dataclass\nclass Page_c:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Page_c:\n        page_size = data_proto.page_size\n        current_page = data_proto.current_page\n        total_page = data_proto.total_page\n        total_count = data_proto.total_count\n        has_more = current_page < total_page\n        has_prev = current_page > 1\n        return Page_c(page_size, current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Forum_c:\n    \"\"\"\n    吧信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n\n        category (str): 一级分类\n        subcategory (str): 二级分类\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n\n    category: str = \"\"\n    subcategory: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Forum_c:\n        fid = data_proto.id\n        fname = data_proto.name\n        category = data_proto.first_class\n        subcategory = data_proto.second_class\n        return Forum_c(fid, fname, category, subcategory)\n\n\n@dcs.dataclass\nclass UserInfo_ct:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        level (int): 等级\n\n        is_god (bool): 是否大神\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    level: int = 0\n    is_god: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_ct:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        level = data_proto.level_id\n        is_god = bool(data_proto.new_god_data.status)\n        return UserInfo_ct(user_id, portrait, user_name, nick_name_new, level, is_god)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_ct) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass Thread_c:\n    \"\"\"\n    主题帖信息\n\n    Attributes:\n        title (str): 标题内容\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 主题帖tid\n        user (UserInfo_ct): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        type (ThreadType): 帖子类型\n        is_help (bool): 是否为求助帖\n\n        reply_num (int): 回复数\n    \"\"\"\n\n    title: str = \"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    user: UserInfo_ct = dcs.field(default_factory=UserInfo_ct)\n\n    type: ThreadType = ThreadType.UNKNOWN\n\n    reply_num: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Thread_c:\n        title = data_proto.title\n        tid = data_proto.id\n        user = UserInfo_ct.from_proto(data_proto.author)\n\n        type_ = ThreadType(data_proto.thread_type)\n        if type_ == ThreadType.UNKNOWN:\n            LOG().debug(\"Unknown thread type. tid=%d, type=%s\", tid, data_proto.thread_type)\n\n        reply_num = data_proto.reply_num\n        return Thread_c(title, 0, \"\", tid, user, type_, reply_num)\n\n    def __eq__(self, obj: Thread_c) -> bool:\n        return self.tid == obj.tid\n\n    def __hash__(self) -> int:\n        return self.tid\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n    @property\n    @deprecated(\"使用 thread.type == ThreadType.HELP 作为替代\")\n    def is_help(self) -> bool:\n        return self.type == ThreadType.HELP\n\n\n@dcs.dataclass\nclass FragImage_cp:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 小图链接 宽720px 一定是静态图\n        big_src (str): 大图链接 宽960px\n        origin_src (str): 原图链接\n        origin_size (int): 原图大小\n        show_width (int): 图像在客户端预览显示的宽度\n        show_height (int): 图像在客户端预览显示的高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    big_src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    origin_size: int = 0\n    show_width: int = 0\n    show_height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragImage_cp:\n        src = data_proto.cdn_src\n        big_src = data_proto.big_cdn_src\n        origin_src = data_proto.origin_src\n        origin_size = data_proto.origin_size\n\n        show_width, _, show_height = data_proto.bsize.partition(\",\")\n        show_width = int(show_width)\n        show_height = int(show_height)\n\n        if hash_obj := _IMAGEHASH_EXP.search(src):\n            hash_ = hash_obj.group(1)\n        else:\n            hash_ = \"\"\n\n        return FragImage_cp(src, big_src, origin_src, origin_size, show_width, show_height, hash_)\n\n\n@dcs.dataclass\nclass Contents_cp(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_cp]): 表情碎片列表\n        imgs (list[FragImage_cp]): 图像碎片列表\n        ats (list[FragAt_cp]): @碎片列表\n        links (list[FragLink_cp]): 链接碎片列表\n        tiebapluses (list[FragTiebaPlus_cp]): 贴吧plus碎片列表\n        voice (FragVoice_cp): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_cp] = dcs.field(default_factory=list, repr=False)\n    imgs: list[FragImage_cp] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_cp] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_cp] = dcs.field(default_factory=list, repr=False)\n    tiebapluses: list[FragTiebaPlus_cp] = dcs.field(default_factory=list, repr=False)\n    voice: FragVoice_cp = dcs.field(default_factory=FragVoice_cp, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_cp:\n        content_protos = data_proto.content\n\n        texts = []\n        emojis = []\n        imgs = []\n        ats = []\n        links = []\n        tiebapluses = []\n        voice = FragVoice_cp()\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条\n                if _type in [0, 9, 18, 27]:\n                    frag = FragText_cp.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_cp.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                # 20:tid=5470214675\n                elif _type in [3, 20]:\n                    frag = FragImage_cp.from_proto(proto)\n                    imgs.append(frag)\n                    yield frag\n                elif _type == 4:\n                    frag = FragAt_cp.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_cp.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 10:  # voice\n                    frag = FragVoice_cp.from_proto(proto)\n                    nonlocal voice\n                    voice = frag\n                    yield frag\n                # 35|36:tid=7769728331 / 37:tid=7760184147\n                elif _type in [35, 36, 37]:\n                    frag = FragTiebaPlus_cp.from_proto(proto)\n                    tiebapluses.append(frag)\n                    texts.append(frag)\n                    yield frag\n                # outdated tiebaplus\n                elif _type == 34:\n                    continue\n                else:\n                    yield FragUnknown.from_proto(proto)\n\n        objs = list(_frags())\n\n        return Contents_cp(objs, texts, emojis, imgs, ats, links, tiebapluses, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass UserInfo_cp:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        level (int): 等级\n        gender (Gender): 性别\n\n        is_bawu (bool): 是否吧务\n        is_vip (bool): 是否超级会员\n        is_god (bool): 是否大神\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    level: int = 0\n    gender: Gender = Gender.UNKNOWN\n\n    is_bawu: bool = False\n    is_vip: bool = False\n    is_god: bool = False\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_cp:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        level = data_proto.level_id\n        gender = Gender(data_proto.gender)\n        is_bawu = bool(data_proto.is_bawu)\n        is_vip = bool(data_proto.new_tshow_icon)\n        is_god = bool(data_proto.new_god_data.status)\n        priv_like = PrivLike(priv_like) if (priv_like := data_proto.priv_sets.like) else PrivLike.PUBLIC\n        priv_reply = PrivReply(priv_reply) if (priv_reply := data_proto.priv_sets.reply) else PrivReply.ALL\n        return UserInfo_cp(\n            user_id, portrait, user_name, nick_name_new, level, gender, is_bawu, is_vip, is_god, priv_like, priv_reply\n        )\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_cp) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass Post_c:\n    \"\"\"\n    楼层信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_cp): 正文内容碎片列表\n        sign (str): 小尾巴文本内容\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 所在主题帖id\n        pid (int): 回复id\n        user (UserInfo_cp): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        floor (int): 楼层数\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    contents: Contents_cp = dcs.field(default_factory=Contents_cp)\n    sign: str = \"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_cp = dcs.field(default_factory=UserInfo_cp)\n\n    floor: int = 0\n    create_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Post_c:\n        contents = Contents_cp.from_proto(data_proto)\n        sign = \"\".join(p.text for p in data_proto.signature.content if p.type == 0)\n        pid = data_proto.id\n        user = UserInfo_cp.from_proto(data_proto.author)\n        floor = data_proto.floor\n        create_time = data_proto.time\n        return Post_c(contents, sign, 0, \"\", 0, pid, user, floor, create_time)\n\n    def __eq__(self, obj: Post_c) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @cached_property\n    def text(self) -> str:\n        if self.sign:\n            text = f\"{self.contents.text}\\n{self.sign}\"\n        else:\n            text = self.contents.text\n        return text\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n\n@dcs.dataclass\nclass Comments(TbErrorExt, Containers[Comment]):\n    \"\"\"\n    楼中楼列表\n\n    Attributes:\n        objs (list[Comment]): 楼中楼列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_c): 页信息\n        has_more (bool): 是否还有下一页\n\n        forum (Forum_c): 所在吧信息\n        thread (Thread_c): 所在主题帖信息\n        post (Post_c): 所在楼层信息\n    \"\"\"\n\n    page: Page_c = dcs.field(default_factory=Page_c)\n    forum: Forum_c = dcs.field(default_factory=Forum_c)\n    thread: Thread_c = dcs.field(default_factory=Thread_c)\n    post: Post_c = dcs.field(default_factory=Post_c)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Comments:\n        page = Page_c.from_proto(data_proto.page)\n        forum = Forum_c.from_proto(data_proto.forum)\n        thread = Thread_c.from_proto(data_proto.thread)\n        thread.fid = forum.fid\n        thread.fname = forum.fname\n        post = Post_c.from_proto(data_proto.post)\n        post.fid = thread.fid\n        post.fname = thread.fname\n        post.tid = thread.tid\n\n        objs = [Comment.from_proto(p) for p in data_proto.subpost_list]\n        for comment in objs:\n            comment.fid = forum.fid\n            comment.fname = forum.fname\n            comment.tid = thread.tid\n            comment.ppid = post.pid\n            comment.floor = post.floor\n            comment.is_thread_author = thread.author_id == comment.author_id\n\n        return Comments(objs, page, forum, thread, post)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_comments/protobuf/PbFloorReqIdl.proto",
    "content": "// tbclient.PbFloor.PbFloorReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage PbFloorReqIdl {\n    message DataReq {\n        CommonReq common = 9;\n        int64 kz = 1;\n        int64 pid = 2;\n        int64 spid = 3;\n        int32 pn = 4;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_comments/protobuf/PbFloorReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13PbFloorReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\x8f\\x01\\n\\rPbFloorReqIdl\\x12$\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x16.PbFloorReqIdl.DataReq\\x1aX\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\t \\x01(\\x0b\\x32\\n.CommonReq\\x12\\n\\n\\x02kz\\x18\\x01 \\x01(\\x03\\x12\\x0b\\n\\x03pid\\x18\\x02 \\x01(\\x03\\x12\\x0c\\n\\x04spid\\x18\\x03 \\x01(\\x03\\x12\\n\\n\\x02pn\\x18\\x04 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"PbFloorReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PBFLOORREQIDL\"]._serialized_start = 41\n    _globals[\"_PBFLOORREQIDL\"]._serialized_end = 184\n    _globals[\"_PBFLOORREQIDL_DATAREQ\"]._serialized_start = 96\n    _globals[\"_PBFLOORREQIDL_DATAREQ\"]._serialized_end = 184\n"
  },
  {
    "path": "src/aiotieba/api/get_comments/protobuf/PbFloorResIdl.proto",
    "content": "// tbclient.PbFloor.PbFloorResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"Page.proto\";\nimport \"Post.proto\";\nimport \"ThreadInfo.proto\";\nimport \"SimpleForum.proto\";\nimport \"SubPostList.proto\";\n\nmessage PbFloorResIdl {\n    Error error = 1;\n    message DataRes {\n        Page page = 1;\n        Post post = 3;\n        repeated SubPostList subpost_list = 4;\n        ThreadInfo thread = 5;\n        SimpleForum forum = 6;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_comments/protobuf/PbFloorResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import Page_pb2 as Page__pb2\nfrom ..._protobuf import Post_pb2 as Post__pb2\nfrom ..._protobuf import SimpleForum_pb2 as SimpleForum__pb2\nfrom ..._protobuf import SubPostList_pb2 as SubPostList__pb2\nfrom ..._protobuf import ThreadInfo_pb2 as ThreadInfo__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13PbFloorResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nPage.proto\\x1a\\nPost.proto\\x1a\\x10ThreadInfo.proto\\x1a\\x11SimpleForum.proto\\x1a\\x11SubPostList.proto\"\\xe0\\x01\\n\\rPbFloorResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12$\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x16.PbFloorResIdl.DataRes\\x1a\\x91\\x01\\n\\x07\\x44\\x61taRes\\x12\\x13\\n\\x04page\\x18\\x01 \\x01(\\x0b\\x32\\x05.Page\\x12\\x13\\n\\x04post\\x18\\x03 \\x01(\\x0b\\x32\\x05.Post\\x12\"\\n\\x0csubpost_list\\x18\\x04 \\x03(\\x0b\\x32\\x0c.SubPostList\\x12\\x1b\\n\\x06thread\\x18\\x05 \\x01(\\x0b\\x32\\x0b.ThreadInfo\\x12\\x1b\\n\\x05\\x66orum\\x18\\x06 \\x01(\\x0b\\x32\\x0c.SimpleForumb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"PbFloorResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PBFLOORRESIDL\"]._serialized_start = 117\n    _globals[\"_PBFLOORRESIDL\"]._serialized_end = 341\n    _globals[\"_PBFLOORRESIDL_DATARES\"]._serialized_start = 196\n    _globals[\"_PBFLOORRESIDL_DATARES\"]._serialized_end = 341\n"
  },
  {
    "path": "src/aiotieba/api/get_dislike_forums/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import DislikeForum, DislikeForums\n"
  },
  {
    "path": "src/aiotieba/api/get_dislike_forums/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import Account, HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import DislikeForums\nfrom .protobuf import GetDislikeListReqIdl_pb2, GetDislikeListResIdl_pb2\n\nCMD = 309692\n\n\ndef pack_proto(account: Account, pn: int, rn: int) -> bytes:\n    req_proto = GetDislikeListReqIdl_pb2.GetDislikeListReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.pn = pn\n    req_proto.data.rn = rn\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> DislikeForums:\n    res_proto = GetDislikeListResIdl_pb2.GetDislikeListResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    dislike_forums = DislikeForums.from_proto(data_proto)\n\n    return dislike_forums\n\n\nasync def request_http(http_core: HttpCore, pn: int, rn: int) -> DislikeForums:\n    data = pack_proto(http_core.account, pn, rn)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/user/getDislikeList\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, pn: int, rn: int) -> DislikeForums:\n    data = pack_proto(ws_core.account, pn, rn)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_dislike_forums/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers, TypeMessage\n\n\n@dcs.dataclass\nclass Page_dislikef:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Page_dislikef:\n        current_page = data_proto.cur_page\n        has_more = bool(data_proto.has_more)\n        has_prev = current_page > 1\n        return Page_dislikef(current_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass DislikeForum:\n    \"\"\"\n    吧广场贴吧信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n\n        member_num (int): 吧会员数\n        post_num (int): 发帖量\n        thread_num (int): 主题帖数\n\n        is_followed (bool): 是否已关注\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n\n    member_num: int = 0\n    post_num: int = 0\n    thread_num: int = 0\n\n    is_followed: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> DislikeForum:\n        fid = data_proto.forum_id\n        fname = data_proto.forum_name\n        member_num = data_proto.member_count\n        post_num = data_proto.post_num\n        thread_num = data_proto.thread_num\n        return DislikeForum(fid, fname, member_num, post_num, thread_num)\n\n    def __eq__(self, obj: DislikeForum) -> bool:\n        return self.fid == obj.fid\n\n    def __hash__(self) -> int:\n        return self.fid\n\n\n@dcs.dataclass\nclass DislikeForums(TbErrorExt, Containers[DislikeForum]):\n    \"\"\"\n    首页推荐屏蔽的贴吧列表\n\n    Attributes:\n        objs (list[DislikeForum]): 首页推荐屏蔽的贴吧列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_dislikef): 页信息\n    \"\"\"\n\n    page: Page_dislikef = dcs.field(default_factory=Page_dislikef)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> DislikeForums:\n        objs = [DislikeForum.from_proto(p) for p in data_proto.forum_list]\n        page = Page_dislikef.from_proto(data_proto)\n        return DislikeForums(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_dislike_forums/protobuf/GetDislikeListReqIdl.proto",
    "content": "// tbclient.GetDislikeList.GetDislikeListReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage GetDislikeListReqIdl {\n    message DataReq {\n        CommonReq common = 1;\n        int32 pn = 3;\n        int32 rn = 4;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_dislike_forums/protobuf/GetDislikeListReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1aGetDislikeListReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\x82\\x01\\n\\x14GetDislikeListReqIdl\\x12+\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1d.GetDislikeListReqIdl.DataReq\\x1a=\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x01 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\n\\n\\x02pn\\x18\\x03 \\x01(\\x05\\x12\\n\\n\\x02rn\\x18\\x04 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetDislikeListReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETDISLIKELISTREQIDL\"]._serialized_start = 48\n    _globals[\"_GETDISLIKELISTREQIDL\"]._serialized_end = 178\n    _globals[\"_GETDISLIKELISTREQIDL_DATAREQ\"]._serialized_start = 117\n    _globals[\"_GETDISLIKELISTREQIDL_DATAREQ\"]._serialized_end = 178\n"
  },
  {
    "path": "src/aiotieba/api/get_dislike_forums/protobuf/GetDislikeListResIdl.proto",
    "content": "// tbclient.GetDislikeList.GetDislikeListResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"ForumList.proto\";\n\nmessage GetDislikeListResIdl {\n    Error error = 1;\n    message DataRes {\n        repeated ForumList forum_list = 1;\n        int32 has_more = 2;\n        int32 cur_page = 3;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_dislike_forums/protobuf/GetDislikeListResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import ForumList_pb2 as ForumList__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1aGetDislikeListResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\x0f\\x46orumList.proto\"\\xa9\\x01\\n\\x14GetDislikeListResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12+\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1d.GetDislikeListResIdl.DataRes\\x1aM\\n\\x07\\x44\\x61taRes\\x12\\x1e\\n\\nforum_list\\x18\\x01 \\x03(\\x0b\\x32\\n.ForumList\\x12\\x10\\n\\x08has_more\\x18\\x02 \\x01(\\x05\\x12\\x10\\n\\x08\\x63ur_page\\x18\\x03 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetDislikeListResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETDISLIKELISTRESIDL\"]._serialized_start = 61\n    _globals[\"_GETDISLIKELISTRESIDL\"]._serialized_end = 230\n    _globals[\"_GETDISLIKELISTRESIDL_DATARES\"]._serialized_start = 153\n    _globals[\"_GETDISLIKELISTRESIDL_DATARES\"]._serialized_end = 230\n"
  },
  {
    "path": "src/aiotieba/api/get_fans/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Fan, Fans\n"
  },
  {
    "path": "src/aiotieba/api/get_fans/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import Fans\n\n\ndef parse_body(body: bytes) -> Fans:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    fans = Fans.from_json(res_json)\n\n    return fans\n\n\nasync def request(http_core: HttpCore, user_id: int, pn: int) -> Fans:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"pn\", pn),\n        (\"uid\", user_id),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/fans/page\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=16 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_fans/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass Fan:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Fan:\n        user_id = int(data_map[\"id\"])\n        portrait = data_map[\"portrait\"]\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_map[\"name\"]\n        nick_name_new = data_map[\"name_show\"]\n        return Fan(user_id, portrait, user_name, nick_name_new)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: Fan) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass Page_fan:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Page_fan:\n        page_size = int(data_map[\"page_size\"])\n        current_page = int(data_map[\"current_page\"])\n        total_page = int(data_map[\"total_page\"])\n        total_count = int(data_map[\"total_count\"])\n        has_more = bool(int(data_map[\"has_more\"]))\n        has_prev = bool(int(data_map[\"has_prev\"]))\n        return Page_fan(page_size, current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Fans(TbErrorExt, Containers[Fan]):\n    \"\"\"\n    粉丝列表\n\n    Attributes:\n        objs (list[Fan]): 粉丝列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_fan): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_fan = dcs.field(default_factory=Page_fan)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Fans:\n        objs = [Fan.from_json(m) for m in data_map[\"user_list\"]]\n        page = Page_fan.from_json(data_map[\"page\"])\n        return Fans(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_fid/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/get_fid/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError, TiebaValueError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> int:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n    if not (fid := res_json[\"data\"][\"fid\"]):\n        raise TiebaValueError(\"fid is 0\")\n\n    return fid\n\n\nasync def request(http_core: HttpCore, fname: str) -> int:\n    params = [\n        (\"fname\", fname),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"http\", host=WEB_BASE_HOST, path=\"/f/commit/share/fnameShareApi\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_follow_forums/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import FollowForum, FollowForums\n"
  },
  {
    "path": "src/aiotieba/api/get_follow_forums/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import FollowForums\n\n\ndef parse_body(body: bytes) -> FollowForums:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    follow_forums = FollowForums.from_json(res_json)\n\n    return follow_forums\n\n\nasync def request(http_core: HttpCore, user_id: int, pn: int, rn: int) -> FollowForums:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"friend_uid\", user_id),\n        (\"page_no\", pn),\n        (\"page_size\", rn),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/forum/like\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=16 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_follow_forums/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass FollowForum:\n    \"\"\"\n    关注吧信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n\n        level (int): 用户等级\n        exp (int): 经验值\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    level: int = 0\n    exp: int = 0\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> FollowForum:\n        fid = int(data_map[\"id\"])\n        fname = data_map[\"name\"]\n        level = int(data_map[\"level_id\"])\n        exp = int(data_map[\"cur_score\"])\n        return FollowForum(fid, fname, level, exp)\n\n    def __eq__(self, obj: FollowForum) -> bool:\n        return self.fid == obj.fid\n\n    def __hash__(self) -> int:\n        return self.fid\n\n\n@dcs.dataclass\nclass FollowForums(TbErrorExt, Containers[FollowForum]):\n    \"\"\"\n    用户关注贴吧列表\n\n    Attributes:\n        objs (list[Forum]): 用户关注贴吧列表\n        err (Exception | None): 捕获的异常\n\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    has_more: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> FollowForums:\n        if forum_list := data_map.get(\"forum_list\", {}):\n            forum_dicts = forum_list.get(\"non-gconforum\", [])\n            objs = [FollowForum.from_json(m) for m in forum_dicts]\n            forum_dicts = forum_list.get(\"gconforum\", [])\n            objs += [FollowForum.from_json(m) for m in forum_dicts]\n            has_more = bool(int(data_map[\"has_more\"]))\n        else:\n            objs = []\n            has_more = False\n\n        return FollowForums(objs, has_more)\n"
  },
  {
    "path": "src/aiotieba/api/get_follows/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Follow, Follows\n"
  },
  {
    "path": "src/aiotieba/api/get_follows/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import Follows\n\n\ndef parse_body(body: bytes) -> Follows:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    follows = Follows.from_json(res_json)\n\n    return follows\n\n\nasync def request(http_core: HttpCore, user_id: int, pn: int) -> Follows:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"pn\", pn),\n        (\"uid\", user_id),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/follow/followList\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_follows/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass Follow:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Follow:\n        user_id = int(data_map[\"id\"])\n        portrait = data_map[\"portrait\"]\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_map[\"name\"]\n        nick_name_new = data_map[\"name_show\"]\n        return Follow(user_id, portrait, user_name, nick_name_new)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: Follow) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass Page_follow:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Page_follow:\n        current_page = int(data_map[\"pn\"])\n        total_count = int(data_map[\"total_follow_num\"])\n        has_more = bool(int(data_map[\"has_more\"]))\n        has_prev = current_page > 1\n        return Page_follow(current_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Follows(TbErrorExt, Containers[Follow]):\n    \"\"\"\n    粉丝列表\n\n    Attributes:\n        objs (list[Follow]): 粉丝列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_follow): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_follow = dcs.field(default_factory=Page_follow)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Follows:\n        objs = [Follow.from_json(m) for m in data_map[\"follow_list\"]]\n        page = Page_follow.from_json(data_map)\n        return Follows(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_forum/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Forum\n"
  },
  {
    "path": "src/aiotieba/api/get_forum/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import Forum\n\n\ndef parse_body(body: bytes) -> Forum:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    forum_dict = res_json[\"forum\"]\n    forum = Forum.from_json(forum_dict)\n\n    return forum\n\n\nasync def request(http_core: HttpCore, fname: str) -> Forum:\n    data = [(\"kw\", fname)]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/f/frs/frsBottom\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_forum/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass Forum(TbErrorExt):\n    \"\"\"\n    贴吧信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n\n        category (str): 一级分类\n        subcategory (str): 二级分类\n\n        small_avatar (str): 吧头像(小)\n        slogan (str): 吧标语\n        member_num (int): 吧会员数\n        post_num (int): 发帖量\n        thread_num (int): 主题帖数\n\n        has_bawu (bool): 是否有吧务\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n\n    category: str = \"\"\n    subcategory: str = \"\"\n\n    small_avatar: str = \"\"\n    slogan: str = \"\"\n    member_num: int = 0\n    post_num: int = 0\n    thread_num: int = 0\n\n    has_bawu: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Forum:\n        fid = data_map[\"id\"]\n        fname = data_map[\"name\"]\n        category = data_map[\"first_class\"]\n        subcategory = data_map[\"second_class\"]\n        small_avatar = data_map[\"avatar\"]\n        slogan = data_map[\"slogan\"]\n        member_num = data_map[\"member_num\"]\n        post_num = data_map[\"post_num\"]\n        thread_num = data_map[\"thread_num\"]\n        has_bawu = \"managers\" in data_map\n        return Forum(\n            fid, fname, category, subcategory, small_avatar, slogan, member_num, post_num, thread_num, has_bawu\n        )\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_detail/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import Forum_detail\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_detail/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import Forum_detail\nfrom .protobuf import GetForumDetailReqIdl_pb2, GetForumDetailResIdl_pb2\n\nCMD = 303021\n\n\ndef pack_proto(fid: int) -> bytes:\n    req_proto = GetForumDetailReqIdl_pb2.GetForumDetailReqIdl()\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.forum_id = fid\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> Forum_detail:\n    res_proto = GetForumDetailResIdl_pb2.GetForumDetailResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    forum = Forum_detail.from_proto(data_proto)\n\n    return forum\n\n\nasync def request_http(http_core: HttpCore, fid: int) -> Forum_detail:\n    data = pack_proto(fid)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/f/forum/getforumdetail\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=4 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, fid: int) -> Forum_detail:\n    data = pack_proto(fid)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_detail/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from .._classdef import TypeMessage\n\n\n@dcs.dataclass\nclass Forum_detail(TbErrorExt):\n    \"\"\"\n    贴吧信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n\n        category (str): 一级分类\n\n        small_avatar (str): 吧头像(小)\n        origin_avatar (str): 吧头像(原图)\n        slogan (str): 吧标语\n        member_num (int): 吧会员数\n        post_num (int): 发帖量\n\n        has_bawu (bool): 是否有吧务\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n\n    category: str = \"\"\n\n    small_avatar: str = \"\"\n    origin_avatar: str = \"\"\n    slogan: str = \"\"\n    member_num: int = 0\n    post_num: int = 0\n\n    has_bawu: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Forum_detail:\n        forum_proto = data_proto.forum_info\n        fid = forum_proto.forum_id\n        fname = forum_proto.forum_name\n        category = forum_proto.lv1_name\n        small_avatar = forum_proto.avatar\n        origin_avatar = forum_proto.avatar_origin\n        slogan = forum_proto.slogan\n        member_num = forum_proto.member_count\n        post_num = forum_proto.thread_count\n        has_bawu = data_proto.election_tab.new_strategy_text == \"已有吧主\"\n        return Forum_detail(fid, fname, category, small_avatar, origin_avatar, slogan, member_num, post_num, has_bawu)\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_detail/protobuf/GetForumDetailReqIdl.proto",
    "content": "// tbclient.GetForumDetail.GetForumDetailReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage GetForumDetailReqIdl {\n    message DataReq {\n        int64 forum_id = 1;\n        CommonReq common = 2;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_detail/protobuf/GetForumDetailReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1aGetForumDetailReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"|\\n\\x14GetForumDetailReqIdl\\x12+\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1d.GetForumDetailReqIdl.DataReq\\x1a\\x37\\n\\x07\\x44\\x61taReq\\x12\\x10\\n\\x08\\x66orum_id\\x18\\x01 \\x01(\\x03\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x02 \\x01(\\x0b\\x32\\n.CommonReqb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetForumDetailReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETFORUMDETAILREQIDL\"]._serialized_start = 47\n    _globals[\"_GETFORUMDETAILREQIDL\"]._serialized_end = 171\n    _globals[\"_GETFORUMDETAILREQIDL_DATAREQ\"]._serialized_start = 116\n    _globals[\"_GETFORUMDETAILREQIDL_DATAREQ\"]._serialized_end = 171\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_detail/protobuf/GetForumDetailResIdl.proto",
    "content": "// tbclient.GetForumDetail.GetForumDetailResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage GetForumDetailResIdl {\n    Error error = 1;\n    message DataRes {\n        message RecommendForumInfo {\n            string avatar = 1;\n            uint64 forum_id = 2;\n            string forum_name = 3;\n            uint32 member_count = 5;\n            uint32 thread_count = 6;\n            string slogan = 7;\n            string lv1_name = 18;\n            string avatar_origin = 20;\n        }\n        RecommendForumInfo forum_info = 1;\n        message ManagerElectionTab {\n            string new_strategy_text = 5;\n        }\n        ManagerElectionTab election_tab = 8;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_detail/protobuf/GetForumDetailResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1aGetForumDetailResIdl.proto\\x1a\\x0b\\x45rror.proto\"\\xd7\\x03\\n\\x14GetForumDetailResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12+\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1d.GetForumDetailResIdl.DataRes\\x1a\\xfa\\x02\\n\\x07\\x44\\x61taRes\\x12\\x44\\n\\nforum_info\\x18\\x01 \\x01(\\x0b\\x32\\x30.GetForumDetailResIdl.DataRes.RecommendForumInfo\\x12\\x46\\n\\x0c\\x65lection_tab\\x18\\x08 \\x01(\\x0b\\x32\\x30.GetForumDetailResIdl.DataRes.ManagerElectionTab\\x1a\\xaf\\x01\\n\\x12RecommendForumInfo\\x12\\x0e\\n\\x06\\x61vatar\\x18\\x01 \\x01(\\t\\x12\\x10\\n\\x08\\x66orum_id\\x18\\x02 \\x01(\\x04\\x12\\x12\\n\\nforum_name\\x18\\x03 \\x01(\\t\\x12\\x14\\n\\x0cmember_count\\x18\\x05 \\x01(\\r\\x12\\x14\\n\\x0cthread_count\\x18\\x06 \\x01(\\r\\x12\\x0e\\n\\x06slogan\\x18\\x07 \\x01(\\t\\x12\\x10\\n\\x08lv1_name\\x18\\x12 \\x01(\\t\\x12\\x15\\n\\ravatar_origin\\x18\\x14 \\x01(\\t\\x1a/\\n\\x12ManagerElectionTab\\x12\\x19\\n\\x11new_strategy_text\\x18\\x05 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetForumDetailResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETFORUMDETAILRESIDL\"]._serialized_start = 44\n    _globals[\"_GETFORUMDETAILRESIDL\"]._serialized_end = 515\n    _globals[\"_GETFORUMDETAILRESIDL_DATARES\"]._serialized_start = 137\n    _globals[\"_GETFORUMDETAILRESIDL_DATARES\"]._serialized_end = 515\n    _globals[\"_GETFORUMDETAILRESIDL_DATARES_RECOMMENDFORUMINFO\"]._serialized_start = 291\n    _globals[\"_GETFORUMDETAILRESIDL_DATARES_RECOMMENDFORUMINFO\"]._serialized_end = 466\n    _globals[\"_GETFORUMDETAILRESIDL_DATARES_MANAGERELECTIONTAB\"]._serialized_start = 468\n    _globals[\"_GETFORUMDETAILRESIDL_DATARES_MANAGERELECTIONTAB\"]._serialized_end = 515\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_level/__init__.py",
    "content": "from ._api import request_http\nfrom ._classdef import LevelInfo\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_level/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import Account, HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import LevelInfo\nfrom .protobuf import GetLevelInfoReqIdl_pb2, GetLevelInfoResIdl_pb2\n\nCMD = 301005\n\n\ndef pack_proto(account: Account, fid: int) -> bytes:\n    req_proto = GetLevelInfoReqIdl_pb2.GetLevelInfoReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.forum_id = fid\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> LevelInfo:\n    res_proto = GetLevelInfoResIdl_pb2.GetLevelInfoResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    level_info = LevelInfo.from_proto(data_proto)\n\n    return level_info\n\n\nasync def request_http(http_core: HttpCore, forum_id: int) -> LevelInfo:\n    data = pack_proto(http_core.account, forum_id)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/forum/getLevelInfo\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, forum_id: int) -> LevelInfo:\n    data = pack_proto(ws_core.account, forum_id)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_level/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from .._classdef import TypeMessage\n\n\n@dcs.dataclass\nclass LevelInfo:\n    \"\"\"\n    用户于某贴吧的等级信息\n\n    Attributes:\n        user_level (int): 等级数值\n        level_name (str): 等级名称\n        is_like (int): 是否已关注\n\n    \"\"\"\n\n    level_name: str = \"\"\n    user_level: int = 0\n    is_like: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> LevelInfo:\n        user_level = data_proto.user_level\n        level_name = data_proto.level_name\n        is_like = data_proto.is_like\n        return LevelInfo(level_name, user_level, is_like)\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_level/protobuf/GetLevelInfoReqIdl.proto",
    "content": "// tbclient.GetLevelInfo.GetLevelInfoReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage GetLevelInfoReqIdl {\n    message DataReq {\n        CommonReq common = 2;\n        int64 forum_id = 1;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_level/protobuf/GetLevelInfoReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x18GetLevelInfoReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"x\\n\\x12GetLevelInfoReqIdl\\x12)\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1b.GetLevelInfoReqIdl.DataReq\\x1a\\x37\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x02 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\x10\\n\\x08\\x66orum_id\\x18\\x01 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetLevelInfoReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETLEVELINFOREQIDL\"]._serialized_start = 45\n    _globals[\"_GETLEVELINFOREQIDL\"]._serialized_end = 165\n    _globals[\"_GETLEVELINFOREQIDL_DATAREQ\"]._serialized_start = 110\n    _globals[\"_GETLEVELINFOREQIDL_DATAREQ\"]._serialized_end = 165\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_level/protobuf/GetLevelInfoResIdl.proto",
    "content": "// tbclient.GetLevelInfo.GetLevelInfoResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage GetLevelInfoResIdl {\n    Error error = 2;\n    message DataRes {\n        int32 is_like = 2;\n        // repeated LevelInfo level_info = 1;\n        string level_name = 4;\n        int32 user_level = 3;\n    }\n    DataRes data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_forum_level/protobuf/GetLevelInfoResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x18GetLevelInfoResIdl.proto\\x1a\\x0b\\x45rror.proto\"\\x9a\\x01\\n\\x12GetLevelInfoResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x02 \\x01(\\x0b\\x32\\x06.Error\\x12)\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1b.GetLevelInfoResIdl.DataRes\\x1a\\x42\\n\\x07\\x44\\x61taRes\\x12\\x0f\\n\\x07is_like\\x18\\x02 \\x01(\\x05\\x12\\x12\\n\\nlevel_name\\x18\\x04 \\x01(\\t\\x12\\x12\\n\\nuser_level\\x18\\x03 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetLevelInfoResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETLEVELINFORESIDL\"]._serialized_start = 42\n    _globals[\"_GETLEVELINFORESIDL\"]._serialized_end = 196\n    _globals[\"_GETLEVELINFORESIDL_DATARES\"]._serialized_start = 130\n    _globals[\"_GETLEVELINFORESIDL_DATARES\"]._serialized_end = 196\n"
  },
  {
    "path": "src/aiotieba/api/get_group_msg/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request\nfrom ._classdef import UserInfo_ws, WsMessage, WsMsgGroup, WsMsgGroups\n"
  },
  {
    "path": "src/aiotieba/api/get_group_msg/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TiebaServerError\nfrom ._classdef import WsMsgGroups\nfrom .protobuf import GetGroupMsgReqIdl_pb2, GetGroupMsgResIdl_pb2\n\nif TYPE_CHECKING:\n    from ...core import Account, WsCore\n\nCMD = 202003\n\n\ndef pack_proto(account: Account, group_ids: list[int], msg_ids: list[int], get_type: int) -> bytes:\n    req_proto = GetGroupMsgReqIdl_pb2.GetGroupMsgReqIdl()\n    for group_id, msg_id in zip(group_ids, msg_ids, strict=False):\n        group_proto = req_proto.data.groupMids.add()\n        group_proto.groupId = group_id\n        group_proto.lastMsgId = msg_id\n    req_proto.data.gettype = str(get_type)\n    req_proto.cuid = f\"{account.cuid}|com.baidu.tieba_mini12.35.1.0\"\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> WsMsgGroups:\n    res_proto = GetGroupMsgResIdl_pb2.GetGroupMsgResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    groups = WsMsgGroups.from_proto(data_proto)\n\n    return groups\n\n\nasync def request(ws_core: WsCore, group_ids: list[int], get_type: int) -> WsMsgGroups:\n    msg_ids = [ws_core.mid_manager.get_msg_id(gid) for gid in group_ids]\n    data = pack_proto(ws_core.account, group_ids, msg_ids, get_type)\n\n    resp = await ws_core.send(data, CMD)\n    return parse_body(await resp.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_group_msg/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers, TypeMessage\n\n\n@dcs.dataclass\nclass UserInfo_ws:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_ws:\n        user_id = data_proto.userId\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.userName\n        return UserInfo_ws(user_id, portrait, user_name)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_ws) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def log_name(self) -> str:\n        return str(self)\n\n\n@dcs.dataclass\nclass WsMessage:\n    \"\"\"\n    websocket消息\n\n    Attributes:\n        msg_id (int): 消息id\n        msg_type (str): 消息类型\n        text (str): 文本内容\n        user (UserInfo_ws): 文本内容\n        create_time (int): 发送时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    msg_id: int = 0\n    msg_type: int = 0\n    text: str = \"\"\n    user: UserInfo_ws = dcs.field(default_factory=UserInfo_ws)\n    create_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> None:\n        msg_id = data_proto.msgId\n        msg_type = data_proto.msgType\n        text = data_proto.content\n        user = UserInfo_ws.from_proto(data_proto.userInfo)\n        create_time = data_proto.createTime\n        return WsMessage(msg_id, msg_type, text, user, create_time)\n\n\n@dcs.dataclass\nclass WsMsgGroup:\n    \"\"\"\n    websocket消息组\n\n    Attributes:\n        group_id (str): 消息组id\n        group_type (int): 消息组类别\n        messages (list[WsMessage]): 消息列表\n    \"\"\"\n\n    group_id: int = 0\n    group_type: int = 0\n    messages: list[WsMessage] = dcs.field(default_factory=list)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> WsMsgGroup:\n        group_id = data_proto.groupInfo.groupId\n        group_type = data_proto.groupInfo.groupType\n        messages = [WsMessage.from_proto(p) for p in data_proto.msgList]\n        return WsMsgGroup(group_id, group_type, messages)\n\n\n@dcs.dataclass\nclass WsMsgGroups(TbErrorExt, Containers[WsMsgGroup]):\n    \"\"\"\n    websocket消息组列表\n\n    Attributes:\n        objs (list[WsMsgGroup]): websocket消息组列表\n        err (Exception | None): 捕获的异常\n    \"\"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> WsMsgGroups:\n        objs = [WsMsgGroup.from_proto(p) for p in data_proto.groupInfo]\n        return WsMsgGroups(objs)\n"
  },
  {
    "path": "src/aiotieba/api/get_group_msg/protobuf/GetGroupMsgReqIdl.proto",
    "content": "// protobuf.GetGroupMsg.GetGroupMsgReqIdl\nsyntax = \"proto3\";\n\nmessage GetGroupMsgReqIdl {\n    string cuid = 1;\n    message DataReq {\n        message GroupLastId {\n            int64 groupId = 1;\n            int64 lastMsgId = 2;\n        }\n        repeated GroupLastId groupMids = 6;\n        string gettype = 7;\n    }\n    DataReq data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_group_msg/protobuf/GetGroupMsgReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x17GetGroupMsgReqIdl.proto\"\\xd6\\x01\\n\\x11GetGroupMsgReqIdl\\x12\\x0c\\n\\x04\\x63uid\\x18\\x01 \\x01(\\t\\x12(\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1a.GetGroupMsgReqIdl.DataReq\\x1a\\x88\\x01\\n\\x07\\x44\\x61taReq\\x12\\x39\\n\\tgroupMids\\x18\\x06 \\x03(\\x0b\\x32&.GetGroupMsgReqIdl.DataReq.GroupLastId\\x12\\x0f\\n\\x07gettype\\x18\\x07 \\x01(\\t\\x1a\\x31\\n\\x0bGroupLastId\\x12\\x0f\\n\\x07groupId\\x18\\x01 \\x01(\\x03\\x12\\x11\\n\\tlastMsgId\\x18\\x02 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetGroupMsgReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETGROUPMSGREQIDL\"]._serialized_start = 28\n    _globals[\"_GETGROUPMSGREQIDL\"]._serialized_end = 242\n    _globals[\"_GETGROUPMSGREQIDL_DATAREQ\"]._serialized_start = 106\n    _globals[\"_GETGROUPMSGREQIDL_DATAREQ\"]._serialized_end = 242\n    _globals[\"_GETGROUPMSGREQIDL_DATAREQ_GROUPLASTID\"]._serialized_start = 193\n    _globals[\"_GETGROUPMSGREQIDL_DATAREQ_GROUPLASTID\"]._serialized_end = 242\n"
  },
  {
    "path": "src/aiotieba/api/get_group_msg/protobuf/GetGroupMsgResIdl.proto",
    "content": "// protobuf.GetGroupMsg.GetGroupMsgResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage GetGroupMsgResIdl {\n    Error error = 1;\n    message DataRes {\n        message GroupMsg {\n            message GroupInfo {\n                int64 groupId = 1;\n                int32 groupType = 20;\n            }\n            GroupInfo groupInfo = 1;\n            message MsgInfo {\n                int64 msgId = 1;\n                int32 msgType = 3;\n                string content = 5;\n                int32 createTime = 8;\n                message UserInfo {\n                    int64 userId = 1;\n                    string userName = 2;\n                    string portrait = 4;\n                    // string userNameShow = 18;\n                }\n                UserInfo userInfo = 10;\n            }\n            repeated MsgInfo msgList = 2;\n        }\n        repeated GroupMsg groupInfo = 1;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_group_msg/protobuf/GetGroupMsgResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x17GetGroupMsgResIdl.proto\\x1a\\x0b\\x45rror.proto\"\\xaf\\x04\\n\\x11GetGroupMsgResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12(\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1a.GetGroupMsgResIdl.DataRes\\x1a\\xd8\\x03\\n\\x07\\x44\\x61taRes\\x12\\x36\\n\\tgroupInfo\\x18\\x01 \\x03(\\x0b\\x32#.GetGroupMsgResIdl.DataRes.GroupMsg\\x1a\\x94\\x03\\n\\x08GroupMsg\\x12@\\n\\tgroupInfo\\x18\\x01 \\x01(\\x0b\\x32-.GetGroupMsgResIdl.DataRes.GroupMsg.GroupInfo\\x12<\\n\\x07msgList\\x18\\x02 \\x03(\\x0b\\x32+.GetGroupMsgResIdl.DataRes.GroupMsg.MsgInfo\\x1a/\\n\\tGroupInfo\\x12\\x0f\\n\\x07groupId\\x18\\x01 \\x01(\\x03\\x12\\x11\\n\\tgroupType\\x18\\x14 \\x01(\\x05\\x1a\\xd6\\x01\\n\\x07MsgInfo\\x12\\r\\n\\x05msgId\\x18\\x01 \\x01(\\x03\\x12\\x0f\\n\\x07msgType\\x18\\x03 \\x01(\\x05\\x12\\x0f\\n\\x07\\x63ontent\\x18\\x05 \\x01(\\t\\x12\\x12\\n\\ncreateTime\\x18\\x08 \\x01(\\x05\\x12\\x46\\n\\x08userInfo\\x18\\n \\x01(\\x0b\\x32\\x34.GetGroupMsgResIdl.DataRes.GroupMsg.MsgInfo.UserInfo\\x1a>\\n\\x08UserInfo\\x12\\x0e\\n\\x06userId\\x18\\x01 \\x01(\\x03\\x12\\x10\\n\\x08userName\\x18\\x02 \\x01(\\t\\x12\\x10\\n\\x08portrait\\x18\\x04 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetGroupMsgResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETGROUPMSGRESIDL\"]._serialized_start = 41\n    _globals[\"_GETGROUPMSGRESIDL\"]._serialized_end = 600\n    _globals[\"_GETGROUPMSGRESIDL_DATARES\"]._serialized_start = 128\n    _globals[\"_GETGROUPMSGRESIDL_DATARES\"]._serialized_end = 600\n    _globals[\"_GETGROUPMSGRESIDL_DATARES_GROUPMSG\"]._serialized_start = 196\n    _globals[\"_GETGROUPMSGRESIDL_DATARES_GROUPMSG\"]._serialized_end = 600\n    _globals[\"_GETGROUPMSGRESIDL_DATARES_GROUPMSG_GROUPINFO\"]._serialized_start = 336\n    _globals[\"_GETGROUPMSGRESIDL_DATARES_GROUPMSG_GROUPINFO\"]._serialized_end = 383\n    _globals[\"_GETGROUPMSGRESIDL_DATARES_GROUPMSG_MSGINFO\"]._serialized_start = 386\n    _globals[\"_GETGROUPMSGRESIDL_DATARES_GROUPMSG_MSGINFO\"]._serialized_end = 600\n    _globals[\"_GETGROUPMSGRESIDL_DATARES_GROUPMSG_MSGINFO_USERINFO\"]._serialized_start = 538\n    _globals[\"_GETGROUPMSGRESIDL_DATARES_GROUPMSG_MSGINFO_USERINFO\"]._serialized_end = 600\n"
  },
  {
    "path": "src/aiotieba/api/get_images/__init__.py",
    "content": "from ._api import parse_body, request, request_bytes\nfrom ._classdef import Image, ImageBytes\n"
  },
  {
    "path": "src/aiotieba/api/get_images/_api.py",
    "content": "import aiohttp\nimport yarl\n\nfrom ...core import HttpCore\nfrom ...exception import ContentTypeError, HTTPStatusError\nfrom ._classdef import Image, ImageBytes\n\n\ndef _headers_checker(response: aiohttp.ClientResponse) -> None:\n    if response.status != 200:\n        raise HTTPStatusError(response.status, response.reason)\n\n    if not response.content_type.endswith((\"jpeg\", \"png\", \"bmp\"), 6):\n        raise ContentTypeError(f\"Expect jpeg, png or bmp, got {response.content_type}\")\n\n\ndef parse_body(body: bytes) -> Image:\n    import cv2 as cv\n    import numpy as np\n\n    image = cv.imdecode(np.frombuffer(body, np.uint8), cv.IMREAD_COLOR)\n    if image is None:\n        raise RuntimeError(\"Error in cv2.imdecode\")\n\n    return Image(image)\n\n\nasync def _request_bytes(http_core: HttpCore, url: yarl.URL) -> bytes:\n    request = http_core.pack_web_get_request(\n        url,\n        [],\n        extra_headers=[(aiohttp.hdrs.REFERER, \"tieba.baidu.com\")],\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=256 * 1024, headers_checker=_headers_checker)\n\n    return body\n\n\nasync def request_bytes(http_core: HttpCore, url: yarl.URL) -> ImageBytes:\n    body = await _request_bytes(http_core, url)\n    return ImageBytes(body)\n\n\nasync def request(http_core: HttpCore, url: yarl.URL) -> Image:\n    body = await _request_bytes(http_core, url)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_images/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    import numpy as np\n\n\ndef _null_factory() -> np.ndarray:\n    import numpy as np\n\n    return np.empty(0, dtype=np.uint8)\n\n\n@dcs.dataclass\nclass Image(TbErrorExt):\n    \"\"\"\n    图像\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        img (np.ndarray): 图像\n    \"\"\"\n\n    img: np.ndarray = dcs.field(default_factory=_null_factory)\n\n\n@dcs.dataclass\nclass ImageBytes(TbErrorExt):\n    \"\"\"\n    图像原始字节流\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        data (bytes): 图像原始字节流\n    \"\"\"\n\n    data: bytes = b\"\"\n"
  },
  {
    "path": "src/aiotieba/api/get_last_replyers/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import Thread_lp, Threads_lp, UserInfo_lp\n"
  },
  {
    "path": "src/aiotieba/api/get_last_replyers/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import Threads_lp\nfrom .protobuf import FrsPageReqIdl4lp_pb2, FrsPageResIdl4lp_pb2\n\nCMD = 301001\n\n\ndef pack_proto(fname: str, pn: int, rn: int, sort: int, is_good: bool) -> bytes:\n    req_proto = FrsPageReqIdl4lp_pb2.FrsPageReqIdl4lp()\n    req_proto.data.common._client_type = 2\n    req_proto.data.common._client_version = \"6.0.1\"\n    req_proto.data.kw = fname\n    req_proto.data.pn = 0 if pn == 1 else pn\n    req_proto.data.rn = rn\n    req_proto.data.rn_need = rn + 5\n    req_proto.data.is_good = int(is_good)\n    req_proto.data.sort_type = sort\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> Threads_lp:\n    res_proto = FrsPageResIdl4lp_pb2.FrsPageResIdl4lp()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    threads = Threads_lp.from_proto(data_proto)\n\n    return threads\n\n\nasync def request_http(http_core: HttpCore, fname: str, pn: int, rn: int, sort: int, is_good: bool) -> Threads_lp:\n    data = pack_proto(fname, pn, rn, sort, is_good)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/f/frs/page\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=256 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, fname: str, pn: int, rn: int, sort: int, is_good: bool) -> Threads_lp:\n    data = pack_proto(fname, pn, rn, sort, is_good)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_last_replyers/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers, TypeMessage\n\n\n@dcs.dataclass\nclass Page_lp:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Page_lp:\n        page_size = data_proto.page_size\n        current_page = data_proto.current_page\n        if current_page == 0 and page_size != 0:\n            current_page = 1\n        total_page = data_proto.total_page\n        total_count = data_proto.total_count\n        has_more = bool(data_proto.has_more)\n        has_prev = bool(data_proto.has_prev)\n        return Page_lp(page_size, current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass UserInfo_lp:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_old (str): 旧版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_old: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_lp:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_old = data_proto.name_show\n        return UserInfo_lp(user_id, portrait, user_name, nick_name_old)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_lp) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_old\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_old or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_old}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass LastReplyer:\n    \"\"\"\n    最后回复者的用户信息\n\n    Attributes:\n        user_id (int): user_id\n        user_name (str): 用户名\n        nick_name_old (str): 旧版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    user_name: str = \"\"\n    nick_name_old: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> LastReplyer:\n        user_id = data_proto.id\n        user_name = data_proto.name\n        nick_name_old = data_proto.name_show\n        return LastReplyer(user_id, user_name, nick_name_old)\n\n    def __str__(self) -> str:\n        return self.user_name or str(self.user_id)\n\n    def __eq__(self, obj: LastReplyer) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_old\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_old or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        return self.user_name or str(self.user_id)\n\n\n@dcs.dataclass\nclass Thread_lp:\n    \"\"\"\n    主题帖信息\n\n    Attributes:\n        text (str): 文本内容\n        title (str): 标题内容\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 主题帖tid\n        pid (int): 首楼回复pid\n        user (UserInfo_lp): 发布者的用户信息\n        author_id (int): 发布者的user_id\n        last_replyer (LastReplyer): 最后回复者的用户信息\n\n        is_good (bool): 是否精品帖\n        is_top (bool): 是否置顶帖\n\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n        last_time (int): 最后回复时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    title: str = \"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_lp = dcs.field(default_factory=UserInfo_lp)\n    last_replyer: LastReplyer = dcs.field(default_factory=LastReplyer)\n\n    is_good: bool = False\n    is_top: bool = False\n\n    create_time: int = 0\n    last_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> None:\n        title = data_proto.title\n        tid = data_proto.id\n        pid = data_proto.first_post_id\n        user = UserInfo_lp.from_proto(data_proto.author)\n        last_replyer = LastReplyer.from_proto(data_proto.last_replyer)\n        is_good = bool(data_proto.is_good)\n        is_top = bool(data_proto.is_top)\n        create_time = data_proto.create_time\n        last_time = data_proto.last_time_int\n        return Thread_lp(title, 0, \"\", tid, pid, user, last_replyer, is_good, is_top, create_time, last_time)\n\n    def __eq__(self, obj: Thread_lp) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @property\n    def text(self) -> str:\n        return self.title\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n\n@dcs.dataclass\nclass Forum_lp:\n    \"\"\"\n    吧信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Forum_lp:\n        forum_proto = data_proto.forum\n        fid = forum_proto.id\n        fname = forum_proto.name\n        return Forum_lp(fid, fname)\n\n\n@dcs.dataclass\nclass Threads_lp(TbErrorExt, Containers[Thread_lp]):\n    \"\"\"\n    主题帖列表\n\n    Attributes:\n        objs (list[Thread_lp]): 主题帖列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_lp): 页信息\n        has_more (bool): 是否还有下一页\n\n        forum (Forum_lp): 所在吧信息\n    \"\"\"\n\n    page: Page_lp = dcs.field(default_factory=Page_lp)\n    forum: Forum_lp = dcs.field(default_factory=Forum_lp)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Threads_lp:\n        page = Page_lp.from_proto(data_proto.page)\n        forum = Forum_lp.from_proto(data_proto)\n\n        objs = [Thread_lp.from_proto(p) for p in data_proto.thread_list]\n        for thread in objs:\n            thread.fname = forum.fname\n            thread.fid = forum.fid\n\n        return Threads_lp(objs, page, forum)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_last_replyers/protobuf/FrsPageReqIdl4lp.proto",
    "content": "// tbclient.FrsPage.FrsPageReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage FrsPageReqIdl4lp {\n    message DataReq {\n        CommonReq common = 39;\n        string kw = 1;\n        int32 rn = 2;\n        int32 rn_need = 3;\n        int32 is_good = 4;\n        int32 cid = 5;\n        int32 pn = 15;\n        int32 sort_type = 47;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_last_replyers/protobuf/FrsPageReqIdl4lp_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b\"\\n\\x16\\x46rsPageReqIdl4lp.proto\\x1a\\x0f\\x43ommonReq.proto\\\"\\xc9\\x01\\n\\x10\\x46rsPageReqIdl4lp\\x12'\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x19.FrsPageReqIdl4lp.DataReq\\x1a\\x8b\\x01\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18' \\x01(\\x0b\\x32\\n.CommonReq\\x12\\n\\n\\x02kw\\x18\\x01 \\x01(\\t\\x12\\n\\n\\x02rn\\x18\\x02 \\x01(\\x05\\x12\\x0f\\n\\x07rn_need\\x18\\x03 \\x01(\\x05\\x12\\x0f\\n\\x07is_good\\x18\\x04 \\x01(\\x05\\x12\\x0b\\n\\x03\\x63id\\x18\\x05 \\x01(\\x05\\x12\\n\\n\\x02pn\\x18\\x0f \\x01(\\x05\\x12\\x11\\n\\tsort_type\\x18/ \\x01(\\x05\\x62\\x06proto3\"\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"FrsPageReqIdl4lp_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_FRSPAGEREQIDL4LP\"]._serialized_start = 44\n    _globals[\"_FRSPAGEREQIDL4LP\"]._serialized_end = 245\n    _globals[\"_FRSPAGEREQIDL4LP_DATAREQ\"]._serialized_start = 106\n    _globals[\"_FRSPAGEREQIDL4LP_DATAREQ\"]._serialized_end = 245\n"
  },
  {
    "path": "src/aiotieba/api/get_last_replyers/protobuf/FrsPageResIdl4lp.proto",
    "content": "// tbclient.FrsPage.FrsPageResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"Page.proto\";\nimport \"ThreadInfo.proto\";\n\nmessage FrsPageResIdl4lp {\n    Error error = 1;\n    message DataRes {\n        message ForumInfo {\n            int64 id = 1;\n            string name = 2;\n        }\n        ForumInfo forum = 2;\n        Page page = 4;\n        repeated ThreadInfo thread_list = 7;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_last_replyers/protobuf/FrsPageResIdl4lp_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import Page_pb2 as Page__pb2\nfrom ..._protobuf import ThreadInfo_pb2 as ThreadInfo__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b\"\\n\\x16\\x46rsPageResIdl4lp.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nPage.proto\\x1a\\x10ThreadInfo.proto\\\"\\xf0\\x01\\n\\x10\\x46rsPageResIdl4lp\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12'\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x19.FrsPageResIdl4lp.DataRes\\x1a\\x9b\\x01\\n\\x07\\x44\\x61taRes\\x12\\x32\\n\\x05\\x66orum\\x18\\x02 \\x01(\\x0b\\x32#.FrsPageResIdl4lp.DataRes.ForumInfo\\x12\\x13\\n\\x04page\\x18\\x04 \\x01(\\x0b\\x32\\x05.Page\\x12 \\n\\x0bthread_list\\x18\\x07 \\x03(\\x0b\\x32\\x0b.ThreadInfo\\x1a%\\n\\tForumInfo\\x12\\n\\n\\x02id\\x18\\x01 \\x01(\\x03\\x12\\x0c\\n\\x04name\\x18\\x02 \\x01(\\tb\\x06proto3\"\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"FrsPageResIdl4lp_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_FRSPAGERESIDL4LP\"]._serialized_start = 70\n    _globals[\"_FRSPAGERESIDL4LP\"]._serialized_end = 310\n    _globals[\"_FRSPAGERESIDL4LP_DATARES\"]._serialized_start = 155\n    _globals[\"_FRSPAGERESIDL4LP_DATARES\"]._serialized_end = 310\n    _globals[\"_FRSPAGERESIDL4LP_DATARES_FORUMINFO\"]._serialized_start = 273\n    _globals[\"_FRSPAGERESIDL4LP_DATARES_FORUMINFO\"]._serialized_end = 310\n"
  },
  {
    "path": "src/aiotieba/api/get_member_users/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import MemberUser, MemberUsers\n"
  },
  {
    "path": "src/aiotieba/api/get_member_users/_api.py",
    "content": "import bs4\nimport yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ._classdef import MemberUsers\n\n\ndef parse_body(body: bytes) -> MemberUsers:\n    soup = bs4.BeautifulSoup(body, \"lxml\")\n    member_users = MemberUsers.from_xml(soup)\n\n    return member_users\n\n\nasync def request(http_core: HttpCore, fname: str, pn: int) -> MemberUsers:\n    params = [\n        (\"word\", fname),\n        (\"pn\", pn),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/bawu2/platform/listMemberInfo\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=32 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_member_users/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    import bs4\n\n\n@dcs.dataclass\nclass MemberUser:\n    \"\"\"\n    最新关注用户信息\n\n    Attributes:\n        user_name (str): 用户名\n        portrait (str): portrait\n        level (int): 等级\n    \"\"\"\n\n    user_name: str = \"\"\n    portrait: str = \"\"\n    level: int = 0\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> MemberUser:\n        user_item = data_tag.a\n        user_name = user_item[\"title\"]\n        portrait = user_item[\"href\"][14:]\n        level_item = data_tag.span\n        level = int(level_item[\"class\"][1][12:])\n        return MemberUser(user_name, portrait, level)\n\n\n@dcs.dataclass\nclass Page_member:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n        total_page (int): 总页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n    total_page: int = 0\n\n    has_more: bool = False\n    has_prev: int = False\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> Page_member:\n        current_page = int(data_tag.text)\n        total_page_item = data_tag.parent.next_sibling\n        total_page = int(total_page_item.text[1:-1])\n        has_more = current_page < total_page\n        has_prev = current_page > 1\n        return Page_member(current_page, total_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass MemberUsers(TbErrorExt, Containers[MemberUser]):\n    \"\"\"\n    最新关注用户列表\n\n    Attributes:\n        objs (list[MemberUser]): 最新关注用户列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_member): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_member = dcs.field(default_factory=Page_member)\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> MemberUsers:\n        objs = [MemberUser.from_xml(t) for t in data_soup(\"div\", class_=\"name_wrap\")]\n        page = Page_member.from_xml(data_soup.find(\"div\", class_=\"tbui_pagination\").find(\"li\", class_=\"active\"))\n        return MemberUsers(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_posts/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import Comment_p, Post, Posts, Thread_p, UserInfo_p, UserInfo_pt\n"
  },
  {
    "path": "src/aiotieba/api/get_posts/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, STABLE_VERSION\nfrom ...core import Account, HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import Posts\nfrom .protobuf import PbPageReqIdl_pb2, PbPageResIdl_pb2\n\nCMD = 302001\n\n\ndef pack_proto(\n    account: Account,\n    tid: int,\n    pn: int,\n    rn: int,\n    sort: int,\n    only_thread_author: bool,\n    with_comments: bool,\n    comment_sort_by_agree: bool,\n    comment_rn: int,\n) -> bytes:\n    req_proto = PbPageReqIdl_pb2.PbPageReqIdl()\n    req_proto.data.common._client_type = 2\n    req_proto.data.common._client_version = STABLE_VERSION\n    req_proto.data.kz = tid\n    req_proto.data.pn = pn\n    req_proto.data.rn = rn if rn > 1 else 2\n    req_proto.data.r = sort\n    req_proto.data.lz = int(only_thread_author)\n    if with_comments:\n        req_proto.data.common.BDUSS = account.BDUSS\n        req_proto.data.with_floor = int(with_comments)\n        req_proto.data.floor_sort_type = int(comment_sort_by_agree)\n        req_proto.data.floor_rn = comment_rn\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> Posts:\n    res_proto = PbPageResIdl_pb2.PbPageResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    posts = Posts.from_proto(data_proto)\n\n    return posts\n\n\nasync def request_http(\n    http_core: HttpCore,\n    tid: int,\n    pn: int,\n    rn: int,\n    sort: int,\n    only_thread_author: bool,\n    with_comments: bool,\n    comment_sort_by_agree: bool,\n    comment_rn: int,\n) -> Posts:\n    data = pack_proto(\n        http_core.account,\n        tid,\n        pn,\n        rn,\n        sort,\n        only_thread_author,\n        with_comments,\n        comment_sort_by_agree,\n        comment_rn,\n    )\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/pb/page\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=128 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(\n    ws_core: WsCore,\n    tid: int,\n    pn: int,\n    rn: int,\n    sort: int,\n    only_thread_author: bool,\n    with_comments: bool,\n    comment_sort_by_agree: bool,\n    comment_rn: int,\n) -> Posts:\n    data = pack_proto(\n        ws_core.account,\n        tid,\n        pn,\n        rn,\n        sort,\n        only_thread_author,\n        with_comments,\n        comment_sort_by_agree,\n        comment_rn,\n    )\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_posts/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\n\nfrom ...enums import Gender, PrivLike, PrivReply, ThreadType\nfrom ...exception import TbErrorExt\nfrom ...helper import deprecated\nfrom ...logging import get_logger as LOG\nfrom .._classdef import Containers, TypeMessage, VoteInfo\nfrom .._classdef.contents import (\n    _IMAGEHASH_EXP,\n    FragAt,\n    FragEmoji,\n    FragLink,\n    FragText,\n    FragTiebaPlus,\n    FragUnknown,\n    FragVideo,\n    FragVoice,\n    TypeFragment,\n    TypeFragText,\n)\n\nFragText_p = FragText_pt = FragText_pc = FragText\nFragEmoji_p = FragEmoji_pt = FragEmoji_pc = FragEmoji\nFragAt_p = FragAt_pt = FragAt_pc = FragAt\nFragLink_p = FragLink_pt = FragLink_pc = FragLink\nFragTiebaPlus_p = FragTiebaPlus_pt = FragTiebaPlus_pc = FragTiebaPlus\nFragVideo_pt = FragVideo\nFragVoice_p = FragVoice_pt = FragVoice_pc = FragVoice\n\n\n@dcs.dataclass\nclass FragImage_p:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 小图链接\n        big_src (str): 大图链接\n        origin_src (str): 原图链接\n        origin_size (int): 原图大小\n        show_width (int): 图像在客户端预览显示的宽度\n        show_height (int): 图像在客户端预览显示的高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    big_src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    origin_size: int = 0\n    show_width: int = 0\n    show_height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragImage_p:\n        src = data_proto.cdn_src\n        big_src = data_proto.big_cdn_src\n        origin_src = data_proto.origin_src\n        origin_size = data_proto.origin_size\n\n        show_width, _, show_height = data_proto.bsize.partition(\",\")\n        show_width = int(show_width)\n        show_height = int(show_height)\n\n        if hash_obj := _IMAGEHASH_EXP.search(src):\n            hash_ = hash_obj.group(1)\n        else:\n            hash_ = \"\"\n\n        return FragImage_p(src, big_src, origin_src, origin_size, show_width, show_height, hash_)\n\n\n@dcs.dataclass\nclass FragVideo_p:\n    \"\"\"\n    视频碎片\n\n    Attributes:\n        src (str): 视频链接\n        cover_src (str): 封面链接\n        duration (int): 视频长度 以秒为单位\n        width (int): 视频宽度\n        height (int): 视频高度\n        view_num (int): 浏览次数\n    \"\"\"\n\n    src: str = \"\"\n    cover_src: str = \"\"\n    duration: int = 0\n    width: int = 0\n    height: int = 0\n    view_num: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragVideo_p:\n        src = data_proto.link\n        cover_src = data_proto.src\n        duration = data_proto.during_time\n        width = data_proto.width\n        height = data_proto.height\n        view_num = data_proto.count\n        return FragVideo_p(src, cover_src, duration, width, height, view_num)\n\n    def __bool__(self) -> bool:\n        return bool(self.width)\n\n\n@dcs.dataclass\nclass Contents_p(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_p]): 表情碎片列表\n        imgs (list[FragImage_p]): 图像碎片列表\n        ats (list[FragAt_p]): @碎片列表\n        links (list[FragLink_p]): 链接碎片列表\n        tiebapluses (list[FragTiebaPlus_p]): 贴吧plus碎片列表\n        video (FragVideo_p): 视频碎片\n        voice (FragVoice_p): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_p] = dcs.field(default_factory=list, repr=False)\n    imgs: list[FragImage_p] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_p] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_p] = dcs.field(default_factory=list, repr=False)\n    tiebapluses: list[FragTiebaPlus_p] = dcs.field(default_factory=list, repr=False)\n    video: FragVideo_p = dcs.field(default_factory=FragVideo_p, repr=False)\n    voice: FragVoice_p = dcs.field(default_factory=FragVoice_p, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_p:\n        content_protos = data_proto.content\n\n        texts = []\n        emojis = []\n        imgs = []\n        ats = []\n        links = []\n        tiebapluses = []\n        video = FragVideo_p()\n        voice = FragVoice_p()\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条 40梗百科\n                if _type in [0, 9, 18, 27, 40]:\n                    frag = FragText_p.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_p.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                # 20:tid=5470214675\n                elif _type in [3, 20]:\n                    frag = FragImage_p.from_proto(proto)\n                    imgs.append(frag)\n                    yield frag\n                elif _type == 4:\n                    frag = FragAt_p.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_p.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 10:  # voice\n                    frag = FragVoice_p.from_proto(proto)\n                    nonlocal voice\n                    voice = frag\n                    yield frag\n                elif _type == 5:  # video\n                    frag = FragVideo_p.from_proto(proto)\n                    nonlocal video\n                    video = frag\n                    yield frag\n                # 35|36:tid=7769728331 / 37:tid=7760184147\n                elif _type in [35, 36, 37]:\n                    frag = FragTiebaPlus_p.from_proto(proto)\n                    tiebapluses.append(frag)\n                    texts.append(frag)\n                    yield frag\n                # outdated tiebaplus / vote\n                elif _type in [34, 52]:\n                    continue\n                else:\n                    yield FragUnknown.from_proto(proto)\n\n        objs = list(_frags())\n\n        return Contents_p(objs, texts, emojis, imgs, ats, links, tiebapluses, video, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass Contents_pc(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_pc]): 表情碎片列表\n        ats (list[FragAt_pc]): @碎片列表\n        links (list[FragLink_pc]): 链接碎片列表\n        tiebapluses (list[FragTiebaPlus_pc]): 贴吧plus碎片列表\n        voice (FragVoice_pc): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_pc] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_pc] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_pc] = dcs.field(default_factory=list, repr=False)\n    tiebapluses: list[FragTiebaPlus_pc] = dcs.field(default_factory=list, repr=False)\n    voice: FragVoice_pc = dcs.field(default_factory=FragVoice_pc, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_pc:\n        content_protos = data_proto.content\n\n        texts = []\n        emojis = []\n        ats = []\n        links = []\n        tiebapluses = []\n        voice = FragVoice_pc()\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条\n                if _type in [0, 9, 18, 27]:\n                    frag = FragText_pc.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_pc.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                elif _type == 4:\n                    frag = FragAt_pc.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_pc.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 10:  # voice\n                    frag = FragVoice_pc.from_proto(proto)\n                    nonlocal voice\n                    voice = frag\n                    yield frag\n                # 35|36:tid=7769728331 / 37:tid=7760184147\n                elif _type in [35, 36, 37]:\n                    frag = FragTiebaPlus_pc.from_proto(proto)\n                    tiebapluses.append(frag)\n                    texts.append(frag)\n                    yield frag\n                # outdated tiebaplus\n                elif _type == 34:\n                    continue\n                else:\n                    yield FragUnknown.from_proto(proto)\n\n        objs = list(_frags())\n\n        return Contents_pc(objs, texts, emojis, ats, links, tiebapluses, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass UserInfo_p:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        level (int): 等级\n        glevel (int): 贴吧成长等级\n        gender (int): 性别\n        ip (str): ip归属地\n        icons (list[str]): 印记信息\n\n        is_bawu (bool): 是否吧务\n        is_vip (bool): 是否超级会员\n        is_god (bool): 是否大神\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    level: int = 0\n    glevel: int = 0\n    gender: Gender = Gender.UNKNOWN\n    ip: str = \"\"\n    icons: list[str] = dcs.field(default_factory=list)\n\n    is_bawu: bool = False\n    is_vip: bool = False\n    is_god: bool = False\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_p:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        level = data_proto.level_id\n        glevel = data_proto.user_growth.level_id\n        gender = Gender(data_proto.gender)\n        ip = data_proto.ip_address\n        icons = [name for i in data_proto.iconinfo if (name := i.name)]\n        is_bawu = bool(data_proto.is_bawu)\n        is_vip = bool(data_proto.new_tshow_icon)\n        is_god = bool(data_proto.new_god_data.status)\n        priv_like = PrivLike(priv_like) if (priv_like := data_proto.priv_sets.like) else PrivLike.PUBLIC\n        priv_reply = PrivReply(priv_reply) if (priv_reply := data_proto.priv_sets.reply) else PrivReply.ALL\n        return UserInfo_p(\n            user_id,\n            portrait,\n            user_name,\n            nick_name_new,\n            level,\n            glevel,\n            gender,\n            ip,\n            icons,\n            is_bawu,\n            is_vip,\n            is_god,\n            priv_like,\n            priv_reply,\n        )\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_p) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass Comment_p:\n    \"\"\"\n    楼中楼信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_pc): 正文内容碎片列表\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 所在主题帖id\n        ppid (int): 所在楼层id\n        pid (int): 楼中楼id\n        user (UserInfo_p): 发布者的用户信息\n        author_id (int): 发布者的user_id\n        reply_to_id (int): 被回复者的user_id\n\n        floor (int): 所在楼层数\n        agree (int): 点赞数\n        disagree (int): 点踩数\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n        is_thread_author (bool): 是否楼主\n    \"\"\"\n\n    contents: Contents_pc = dcs.field(default_factory=Contents_pc)\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    ppid: int = 0\n    pid: int = 0\n    user: UserInfo_p = dcs.field(default_factory=UserInfo_p)\n    author_id: int = 0\n    reply_to_id: int = 0\n\n    floor: int = 0\n    agree: int = 0\n    disagree: int = 0\n    create_time: int = 0\n    is_thread_author: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Comment_p:\n        contents = Contents_pc.from_proto(data_proto)\n\n        reply_to_id = 0\n        if contents:\n            first_frag = contents[0]\n            if (\n                isinstance(first_frag, FragText_p)\n                and first_frag.text == \"回复 \"\n                and (reply_to_id := data_proto.content[1].uid)\n            ):\n                reply_to_id = reply_to_id\n                if isinstance(contents[1], FragAt_p):\n                    del contents.ats[0]\n                contents.objs = contents.objs[2:]\n                contents.texts = contents.texts[2:]\n                if contents.texts:\n                    first_text_frag = contents.texts[0]\n                    first_text_frag.text = first_text_frag.text.removeprefix(\" :\")\n\n        contents = contents\n\n        pid = data_proto.id\n        author_id = data_proto.author_id\n        agree = data_proto.agree.agree_num\n        disagree = data_proto.agree.disagree_num\n        create_time = data_proto.time\n\n        return Comment_p(\n            contents, 0, \"\", 0, 0, pid, None, author_id, reply_to_id, 0, agree, disagree, create_time, False\n        )\n\n    def __eq__(self, obj: Comment_p) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @property\n    def text(self) -> str:\n        return self.contents.text\n\n\n@dcs.dataclass\nclass Post:\n    \"\"\"\n    楼层信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_p): 正文内容碎片列表\n        sign (str): 小尾巴文本内容\n        comments (list[Comment_p]): 楼中楼列表\n        is_aimeme (bool): 是否是AI生成的表情包\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 所在主题帖id\n        pid (int): 回复id\n        user (UserInfo_p): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        floor (int): 楼层数\n        reply_num (int): 楼中楼数\n        agree (int): 点赞数\n        disagree (int): 点踩数\n        create_time (int): 创建时间\n        is_thread_author (bool): 是否楼主\n    \"\"\"\n\n    contents: Contents_p = dcs.field(default_factory=Contents_p)\n    sign: str = \"\"\n    comments: list[Comment_p] = dcs.field(default_factory=list)\n    is_aimeme: bool = False\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_p = dcs.field(default_factory=UserInfo_p)\n    author_id: int = 0\n\n    floor: int = 0\n    reply_num: int = 0\n    agree: int = 0\n    disagree: int = 0\n    create_time: int = 0\n    is_thread_author: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Post:\n        contents = Contents_p.from_proto(data_proto)\n        sign = \"\".join(p.text for p in data_proto.signature.content if p.type == 0)\n        comments = [Comment_p.from_proto(p) for p in data_proto.sub_post_list.sub_post_list]\n        is_aimeme = bool(data_proto.sprite_meme_info.meme_id)\n        pid = data_proto.id\n        author_id = data_proto.author_id\n        floor = data_proto.floor\n        reply_num = data_proto.sub_post_number\n        agree = data_proto.agree.agree_num\n        disagree = data_proto.agree.disagree_num\n        create_time = data_proto.time\n        return Post(\n            contents,\n            sign,\n            comments,\n            is_aimeme,\n            0,\n            \"\",\n            0,\n            pid,\n            None,\n            author_id,\n            floor,\n            reply_num,\n            agree,\n            disagree,\n            create_time,\n            False,\n        )\n\n    def __eq__(self, obj: Post) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @cached_property\n    def text(self) -> str:\n        if self.sign:\n            text = f\"{self.contents.text}\\n{self.sign}\"\n        else:\n            text = self.contents.text\n        return text\n\n\n@dcs.dataclass\nclass Page_p:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Page_p:\n        page_size = data_proto.page_size\n        current_page = data_proto.current_page\n        total_page = data_proto.total_page\n        total_count = data_proto.total_count\n        has_more = bool(data_proto.has_more)\n        has_prev = bool(data_proto.has_prev)\n        return Page_p(page_size, current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Forum_p:\n    \"\"\"\n    吧信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n\n        category (str): 一级分类\n        subcategory (str): 二级分类\n\n        member_num (int): 吧会员数\n        post_num (int): 发帖量\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n\n    category: str = \"\"\n    subcategory: str = \"\"\n\n    member_num: int = 0\n    post_num: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Forum_p:\n        fid = data_proto.id\n        fname = data_proto.name\n        category = data_proto.first_class\n        subcategory = data_proto.second_class\n        member_num = data_proto.member_num\n        post_num = data_proto.post_num\n        return Forum_p(fid, fname, category, subcategory, member_num, post_num)\n\n\n@dcs.dataclass\nclass FragImage_pt:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 小图链接 宽580px\n        big_src (str): 大图链接 宽720px\n        origin_src (str): 原图链接\n        show_width (int): 图像在客户端预览显示的宽度\n        show_height (int): 图像在客户端预览显示的高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    big_src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    show_width: int = 0\n    show_height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragImage_pt:\n        src = data_proto.water_pic\n        big_src = data_proto.small_pic\n        origin_src = data_proto.big_pic\n\n        show_width = data_proto.width\n        show_height = data_proto.height\n\n        if hash_obj := _IMAGEHASH_EXP.search(src):\n            hash_ = hash_obj.group(1)\n        else:\n            hash_ = \"\"\n\n        return FragImage_pt(src, big_src, origin_src, show_width, show_height, hash_)\n\n\n@dcs.dataclass\nclass Contents_pt(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_pt]): 表情碎片列表\n        imgs (list[FragImage_pt]): 图像碎片列表\n        ats (list[FragAt_pt]): @碎片列表\n        links (list[FragLink_pt]): 链接碎片列表\n        tiebapluses (list[FragTiebaPlus_pt]): 贴吧plus碎片列表\n        video (FragVideo_pt): 视频碎片\n        voice (FragVoice_pt): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_pt] = dcs.field(default_factory=list, repr=False)\n    imgs: list[FragImage_pt] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_pt] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_pt] = dcs.field(default_factory=list, repr=False)\n    tiebapluses: list[FragTiebaPlus_pt] = dcs.field(default_factory=list, repr=False)\n    video: FragVideo_pt = dcs.field(default_factory=FragVideo_pt, repr=False)\n    voice: FragVoice_pt = dcs.field(default_factory=FragVoice_pt, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_pt:\n        content_protos = data_proto.content\n\n        texts = []\n        emojis = []\n        imgs = [FragImage_pt.from_proto(p) for p in data_proto.media]\n        ats = []\n        links = []\n        tiebapluses = []\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条\n                if _type in [0, 9, 18, 27]:\n                    frag = FragText_pt.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_pt.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                elif _type == 4:\n                    frag = FragAt_pt.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_pt.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                # 35|36:tid=7769728331 / 37:tid=7760184147\n                elif _type in [35, 36, 37]:\n                    frag = FragTiebaPlus_pt.from_proto(proto)\n                    tiebapluses.append(frag)\n                    texts.append(frag)\n                    yield frag\n                # outdated tiebaplus\n                elif _type == 34:\n                    continue\n                else:\n                    yield FragUnknown.from_proto(proto)\n\n        objs = list(_frags())\n\n        if ats:\n            del ats[0]\n            del objs[0]\n        objs += imgs\n\n        if data_proto.video_info.video_width:\n            video = FragVideo_pt.from_proto(data_proto.video_info)\n            objs.append(video)\n        else:\n            video = FragVideo_pt()\n\n        if data_proto.voice_info:\n            voice = FragVoice_pt.from_proto(data_proto.voice_info[0])\n            objs.append(voice)\n        else:\n            voice = FragVoice_pt()\n\n        return Contents_pt(objs, texts, emojis, imgs, ats, links, tiebapluses, video, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass UserInfo_pt:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        level (int): 等级\n        glevel (int): 贴吧成长等级\n        ip (str): ip归属地\n        icons (list[str]): 印记信息\n\n        is_bawu (bool): 是否吧务\n        is_vip (bool): 是否超级会员\n        is_god (bool): 是否大神\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    level: int = 0\n    glevel: int = 0\n    ip: str = \"\"\n    icons: list[str] = dcs.field(default_factory=list)\n\n    is_bawu: bool = False\n    is_vip: bool = False\n    is_god: bool = False\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_pt:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        level = data_proto.level_id\n        glevel = data_proto.user_growth.level_id\n        ip = data_proto.ip_address\n        icons = [name for i in data_proto.iconinfo if (name := i.name)]\n        is_bawu = bool(data_proto.is_bawu)\n        is_vip = bool(data_proto.new_tshow_icon)\n        is_god = bool(data_proto.new_god_data.status)\n        priv_like = PrivLike(priv_like) if (priv_like := data_proto.priv_sets.like) else PrivLike.PUBLIC\n        priv_reply = PrivReply(priv_reply) if (priv_reply := data_proto.priv_sets.reply) else PrivReply.ALL\n        return UserInfo_pt(\n            user_id,\n            portrait,\n            user_name,\n            nick_name_new,\n            level,\n            glevel,\n            ip,\n            icons,\n            is_bawu,\n            is_vip,\n            is_god,\n            priv_like,\n            priv_reply,\n        )\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_pt) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass ShareThread_pt:\n    \"\"\"\n    被分享的主题帖信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_pt): 正文内容碎片列表\n        title (str): 标题内容\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 主题帖tid\n        author_id (int): 发布者的user_id\n\n        vote_info (VoteInfo): 投票内容\n    \"\"\"\n\n    contents: Contents_pt = dcs.field(default_factory=Contents_pt)\n    title: str = \"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    author_id: int = 0\n\n    vote_info: VoteInfo = dcs.field(default_factory=VoteInfo)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> ShareThread_pt:\n        contents = Contents_pt.from_proto(data_proto)\n        title = data_proto.title\n        fid = data_proto.fid\n        fname = data_proto.fname\n        tid = int(tid) if (tid := data_proto.tid) else 0\n        author_id = data_proto.content[0].uid if data_proto.content else 0\n        vote_info = VoteInfo.from_proto(data_proto.poll_info)\n        return ShareThread_pt(contents, title, fid, fname, tid, author_id, vote_info)\n\n    def __eq__(self, obj: ShareThread_pt) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @cached_property\n    def text(self) -> str:\n        if self.title:\n            text = f\"{self.title}\\n{self.contents.text}\"\n        else:\n            text = self.contents.text\n        return text\n\n\n@dcs.dataclass\nclass Thread_p:\n    \"\"\"\n    主题帖信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_pt): 正文内容碎片列表\n        title (str): 标题内容\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 主题帖tid\n        pid (int): 首楼回复pid\n        user (UserInfo_pt): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        type (ThreadType): 帖子类型\n        is_share (bool): 是否分享帖\n        is_help (bool): 是否为求助帖\n\n        vote_info (VoteInfo): 投票信息\n        share_origin (ShareThread_pt): 转发来的原帖内容\n        view_num (int): 浏览量\n        reply_num (int): 回复数\n        share_num (int): 分享数\n        agree (int): 点赞数\n        disagree (int): 点踩数\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    contents: Contents_pt = dcs.field(default_factory=Contents_pt)\n    title: str = \"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_pt = dcs.field(default_factory=UserInfo_pt)\n\n    type: ThreadType = ThreadType.UNKNOWN\n    is_share: bool = False\n\n    vote_info: VoteInfo = dcs.field(default_factory=VoteInfo)\n    share_origin: ShareThread_pt = dcs.field(default_factory=ShareThread_pt)\n    view_num: int = 0\n    reply_num: int = 0\n    share_num: int = 0\n    agree: int = 0\n    disagree: int = 0\n    create_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Thread_p:\n        thread_proto = data_proto.thread\n        title = thread_proto.title\n        tid = thread_proto.id\n        pid = thread_proto.post_id\n        user = UserInfo_pt.from_proto(thread_proto.author)\n\n        type_ = ThreadType(thread_proto.thread_type)\n        if type_ == ThreadType.UNKNOWN:\n            LOG().debug(\"Unknown thread type. tid=%d, type=%s\", tid, data_proto.thread_type)\n\n        is_share = bool(thread_proto.is_share_thread)\n        view_num = data_proto.thread_freq_num\n        reply_num = thread_proto.reply_num\n        share_num = thread_proto.share_num\n        agree = thread_proto.agree.agree_num\n        disagree = thread_proto.agree.disagree_num\n        create_time = thread_proto.create_time\n\n        if not is_share:\n            real_thread_proto = thread_proto.origin_thread_info\n            contents = Contents_pt.from_proto(real_thread_proto)\n            vote_info = VoteInfo.from_proto(real_thread_proto.poll_info)\n            share_origin = ShareThread_pt()\n        else:\n            contents = Contents_pt()\n            vote_info = VoteInfo()\n            share_origin = ShareThread_pt.from_proto(thread_proto.origin_thread_info)\n\n        return Thread_p(\n            contents,\n            title,\n            0,\n            \"\",\n            tid,\n            pid,\n            user,\n            type_,\n            is_share,\n            vote_info,\n            share_origin,\n            view_num,\n            reply_num,\n            share_num,\n            agree,\n            disagree,\n            create_time,\n        )\n\n    def __eq__(self, obj: Thread_p) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @property\n    def text(self) -> str:\n        if self.title:\n            text = f\"{self.title}\\n{self.contents.text}\"\n        else:\n            text = self.contents.text\n        return text\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n    @property\n    @deprecated(\"使用 thread.type == ThreadType.HELP 作为替代\")\n    def is_help(self) -> bool:\n        return self.type == ThreadType.HELP\n\n\n@dcs.dataclass\nclass Posts(TbErrorExt, Containers[Post]):\n    \"\"\"\n    回复列表\n\n    Attributes:\n        objs (list[Post]): 回复列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_p): 页信息\n        has_more (bool): 是否还有下一页\n\n        forum (Forum_p): 所在吧信息\n        thread (Thread_p): 所在主题帖信息\n    \"\"\"\n\n    page: Page_p = dcs.field(default_factory=Page_p)\n    forum: Forum_p = dcs.field(default_factory=Forum_p)\n    thread: Thread_p = dcs.field(default_factory=Thread_p)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Posts:\n        page = Page_p.from_proto(data_proto.page)\n        forum = Forum_p.from_proto(data_proto.forum)\n        thread = Thread_p.from_proto(data_proto)\n\n        thread.fid = forum.fid\n        thread.fname = forum.fname\n\n        objs = [Post.from_proto(p) for p in data_proto.post_list if not p.chat_content.bot_uk]\n        users = {p.id: UserInfo_p.from_proto(p) for p in data_proto.user_list}\n        for post in objs:\n            post.fid = forum.fid\n            post.fname = forum.fname\n            post.tid = thread.tid\n            post.user = users[post.author_id]\n            post.is_thread_author = thread.author_id == post.author_id\n            for comment in post.comments:\n                comment.fid = post.fid\n                comment.fname = post.fname\n                comment.tid = post.tid\n                comment.ppid = post.pid\n                comment.floor = post.floor\n                comment.user = users[comment.author_id]\n                comment.is_thread_author = thread.author_id == comment.author_id\n\n        return Posts(objs, page, forum, thread)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_posts/protobuf/PbPageReqIdl.proto",
    "content": "// tbclient.PbPage.PbPageReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage PbPageReqIdl {\n    message DataReq {\n        CommonReq common = 25;\n        int64 kz = 4;\n        int32 lz = 5;\n        int32 r = 6;\n        int64 pid = 7;\n        int32 with_floor = 8;\n        int32 floor_rn = 9;\n        int32 rn = 13;\n        int32 pn = 18;\n        int32 floor_sort_type = 74;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_posts/protobuf/PbPageReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x12PbPageReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\xe2\\x01\\n\\x0cPbPageReqIdl\\x12#\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x15.PbPageReqIdl.DataReq\\x1a\\xac\\x01\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x19 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\n\\n\\x02kz\\x18\\x04 \\x01(\\x03\\x12\\n\\n\\x02lz\\x18\\x05 \\x01(\\x05\\x12\\t\\n\\x01r\\x18\\x06 \\x01(\\x05\\x12\\x0b\\n\\x03pid\\x18\\x07 \\x01(\\x03\\x12\\x12\\n\\nwith_floor\\x18\\x08 \\x01(\\x05\\x12\\x10\\n\\x08\\x66loor_rn\\x18\\t \\x01(\\x05\\x12\\n\\n\\x02rn\\x18\\r \\x01(\\x05\\x12\\n\\n\\x02pn\\x18\\x12 \\x01(\\x05\\x12\\x17\\n\\x0f\\x66loor_sort_type\\x18J \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"PbPageReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PBPAGEREQIDL\"]._serialized_start = 40\n    _globals[\"_PBPAGEREQIDL\"]._serialized_end = 266\n    _globals[\"_PBPAGEREQIDL_DATAREQ\"]._serialized_start = 94\n    _globals[\"_PBPAGEREQIDL_DATAREQ\"]._serialized_end = 266\n"
  },
  {
    "path": "src/aiotieba/api/get_posts/protobuf/PbPageResIdl.proto",
    "content": "// tbclient.PbPage.PbPageResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"SimpleForum.proto\";\nimport \"Page.proto\";\nimport \"Post.proto\";\nimport \"ThreadInfo.proto\";\nimport \"User.proto\";\n\nmessage PbPageResIdl {\n    Error error = 1;\n    message DataRes {\n        SimpleForum forum = 2;\n        Page page = 3;\n        repeated Post post_list = 6;\n        ThreadInfo thread = 8;\n        repeated User user_list = 13;\n        int64 thread_freq_num = 37;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_posts/protobuf/PbPageResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import Page_pb2 as Page__pb2\nfrom ..._protobuf import Post_pb2 as Post__pb2\nfrom ..._protobuf import SimpleForum_pb2 as SimpleForum__pb2\nfrom ..._protobuf import ThreadInfo_pb2 as ThreadInfo__pb2\nfrom ..._protobuf import User_pb2 as User__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x12PbPageResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\x11SimpleForum.proto\\x1a\\nPage.proto\\x1a\\nPost.proto\\x1a\\x10ThreadInfo.proto\\x1a\\nUser.proto\"\\xf2\\x01\\n\\x0cPbPageResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12#\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x15.PbPageResIdl.DataRes\\x1a\\xa5\\x01\\n\\x07\\x44\\x61taRes\\x12\\x1b\\n\\x05\\x66orum\\x18\\x02 \\x01(\\x0b\\x32\\x0c.SimpleForum\\x12\\x13\\n\\x04page\\x18\\x03 \\x01(\\x0b\\x32\\x05.Page\\x12\\x18\\n\\tpost_list\\x18\\x06 \\x03(\\x0b\\x32\\x05.Post\\x12\\x1b\\n\\x06thread\\x18\\x08 \\x01(\\x0b\\x32\\x0b.ThreadInfo\\x12\\x18\\n\\tuser_list\\x18\\r \\x03(\\x0b\\x32\\x05.User\\x12\\x17\\n\\x0fthread_freq_num\\x18% \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"PbPageResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PBPAGERESIDL\"]._serialized_start = 109\n    _globals[\"_PBPAGERESIDL\"]._serialized_end = 351\n    _globals[\"_PBPAGERESIDL_DATARES\"]._serialized_start = 186\n    _globals[\"_PBPAGERESIDL_DATARES\"]._serialized_end = 351\n"
  },
  {
    "path": "src/aiotieba/api/get_rank_forums/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import RankForum, RankForums\n"
  },
  {
    "path": "src/aiotieba/api/get_rank_forums/_api.py",
    "content": "import bs4\nimport yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...enums import RankForumType\nfrom ._classdef import RankForums\n\n\ndef parse_body(body: bytes) -> RankForums:\n    soup = bs4.BeautifulSoup(body, \"lxml\")\n    rank_forums = RankForums.from_xml(soup)\n\n    return rank_forums\n\n\nasync def request(http_core: HttpCore, fname: str, pn: int, rank_type: RankForumType) -> RankForums:\n    params = [\n        (\"kw\", fname),\n        (\"type\", int(rank_type)),\n        (\"pn\", pn),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/sign/index\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_rank_forums/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    import bs4\n\n\n@dcs.dataclass\nclass RankForum:\n    \"\"\"\n    吧签到排名\n\n    Attributes:\n        fname (str): 吧名\n\n        sign_num (int): 签到用户数\n        member_num (int): 总用户数\n\n        has_bawu (bool): 是否有吧务\n    \"\"\"\n\n    fname: str = \"\"\n\n    sign_num: int = 0\n    member_num: int = 0\n\n    has_bawu: bool = False\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> RankForum:\n        rank_idx_item = data_tag.td\n        fname_item = rank_idx_item.find_next_sibling(\"td\")\n        fname = fname_item.text\n        sign_num_item = fname_item.find_next_sibling(\"td\")\n        sign_num = int(sign_num_item.text)\n        member_num_item = sign_num_item.find_next_sibling(\"td\")\n        member_num = int(member_num_item.text)\n        manager_item = member_num_item.find_next_sibling(\"td\", class_=\"clearfix\")\n        manager_status_item = manager_item.div\n        has_bawu = manager_status_item[\"class\"][0] != \"no_bawu\"\n        return RankForum(fname, sign_num, member_num, has_bawu)\n\n\n@dcs.dataclass\nclass Page_rankforum:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n        total_page (int): 总页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n    total_page: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> Page_rankforum:\n        pages_item = data_soup.find(\"div\", class_=\"pagination\")\n        current_page_item = pages_item.span\n        current_page = int(current_page_item.text)\n        total_page_item = pages_item.find_all(\"a\")[-1]\n        total_page_url: str = total_page_item[\"href\"]\n        total_page_str = total_page_url[total_page_url.rfind(\"pn=\") + 3 :]\n        total_page = int(total_page_str)\n        has_more = current_page < total_page\n        has_prev = current_page > 1\n\n        return Page_rankforum(current_page, total_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass RankForums(TbErrorExt, Containers[RankForum]):\n    \"\"\"\n    吧签到排名表\n\n    Attributes:\n        objs (list[RankForum]): 吧签到排名表\n        err (Exception | None): 捕获的异常\n\n        page (Page_rankforum): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_rankforum = dcs.field(default_factory=Page_rankforum)\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> RankForums:\n        dbgtbody = data_soup.find(\"table\")\n        objs = [RankForum.from_xml(t) for t in dbgtbody.find_all(\"tr\", class_=\"j_rank_row\")]\n        page = Page_rankforum.from_xml(data_soup)\n        return RankForums(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_rank_users/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import RankUser, RankUsers\n"
  },
  {
    "path": "src/aiotieba/api/get_rank_users/_api.py",
    "content": "import bs4\nimport yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ._classdef import RankUsers\n\n\ndef parse_body(body: bytes) -> RankUsers:\n    soup = bs4.BeautifulSoup(body, \"lxml\")\n    rank_users = RankUsers.from_xml(soup)\n\n    return rank_users\n\n\nasync def request(http_core: HttpCore, fname: str, pn: int) -> RankUsers:\n    params = [\n        (\"kw\", fname),\n        (\"pn\", pn),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/f/like/furank\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_rank_users/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom ...helper import parse_json\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n    import bs4\n\n\n@dcs.dataclass\nclass RankUser:\n    \"\"\"\n    等级排行榜用户信息\n\n    Attributes:\n        user_name (str): 用户名\n        level (int): 等级\n        exp (int): 经验值\n        is_vip (bool): 是否超级会员\n    \"\"\"\n\n    user_name: str = \"\"\n    level: int = 0\n    exp: int = 0\n    is_vip: bool = False\n\n    @staticmethod\n    def from_xml(data_tag: bs4.element.Tag) -> RankUser:\n        user_name_item = data_tag.td.next_sibling\n        user_name = user_name_item.text\n        is_vip = \"drl_item_vip\" in user_name_item.div[\"class\"]\n        level_item = user_name_item.next_sibling\n        # e.g. get level 16 from \"bg_lv16\" by slicing [5:]\n        level = int(level_item.div[\"class\"][0][5:])\n        exp_item = level_item.next_sibling\n        exp = int(exp_item.text)\n        return RankUser(user_name, level, exp, is_vip)\n\n\n@dcs.dataclass\nclass Page_rank:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n        total_page (int): 总页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n    total_page: int = 0\n\n    has_more: bool = False\n    has_prev: int = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Page_rank:\n        current_page = data_map[\"cur_page\"]\n        total_page = data_map[\"total_num\"]\n        has_more = current_page < total_page\n        has_prev = current_page > 1\n        return Page_rank(current_page, total_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass RankUsers(TbErrorExt, Containers[RankUser]):\n    \"\"\"\n    等级排行榜用户列表\n\n    Attributes:\n        objs (list[RankUser]): 等级排行榜用户列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_rank): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_rank = dcs.field(default_factory=Page_rank)\n\n    @staticmethod\n    def from_xml(data_soup: bs4.BeautifulSoup) -> RankUsers:\n        objs = [RankUser.from_xml(t) for t in data_soup(\"tr\", class_=[\"drl_list_item\", \"drl_list_item_self\"])]\n        page_item = data_soup.find(\"ul\", class_=\"p_rank_pager\")\n        page_dict = parse_json(page_item[\"data-field\"])\n        page = Page_rank.from_json(page_dict)\n        return RankUsers(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_recom_status/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import RecomStatus\n"
  },
  {
    "path": "src/aiotieba/api/get_recom_status/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import RecomStatus\n\n\ndef parse_body(body: bytes) -> RecomStatus:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    status = RecomStatus.from_json(res_json)\n\n    return status\n\n\nasync def request(http_core: HttpCore, fid: int) -> RecomStatus:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"forum_id\", fid),\n        (\"pn\", 1),\n        (\"rn\", 0),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/bawu/getRecomThreadList\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_recom_status/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass RecomStatus(TbErrorExt):\n    \"\"\"\n    大吧主推荐功能的月度配额状态\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        total_recom_num (int): 本月总推荐配额\n        used_recom_num (int): 本月已使用的推荐配额\n    \"\"\"\n\n    total_recom_num: int = 0\n    used_recom_num: int = 0\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> RecomStatus:\n        total_recom_num = int(data_map[\"total_recommend_num\"])\n        used_recom_num = int(data_map[\"used_recommend_num\"])\n        return RecomStatus(total_recom_num, used_recom_num)\n"
  },
  {
    "path": "src/aiotieba/api/get_recover_info/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import RecoverInfo\n"
  },
  {
    "path": "src/aiotieba/api/get_recover_info/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import RecoverInfo\n\n\ndef parse_body(body: bytes) -> RecoverInfo:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n    data_map = res_json[\"data\"]\n    rec_info = RecoverInfo.from_json(data_map)\n\n    return rec_info\n\n\nasync def request(http_core: HttpCore, fid: int, tid: int, pid: int) -> RecoverInfo:\n    params = [\n        (\"forum_id\", fid),\n        (\"thread_id\", tid),\n        (\"post_id\", pid),\n        (\"type\", 1),\n        (\"sub_type\", 2 if pid else 1),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/bawu/getRecoverInfo\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=16 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_recover_info/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\nfrom .._classdef.contents import _IMAGEHASH_EXP, FragUnknown, TypeFragment, TypeFragText\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass FragText_ri:\n    \"\"\"\n    纯文本碎片\n\n    Attributes:\n        text (str): 文本内容\n    \"\"\"\n\n    text: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> FragText_ri:\n        text = data_map[\"value\"]\n        return FragText_ri(text)\n\n\n@dcs.dataclass\nclass FragImage_ri:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 小图链接 宽720px\n        show_width (int): 图像在客户端预览显示的宽度\n        show_height (int): 图像在客户端预览显示的高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    show_width: int = 0\n    show_height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> FragImage_ri:\n        src = data_map[\"url\"]\n        show_width = int(data_map[\"width\"])\n        show_height = int(data_map[\"height\"])\n\n        hash_ = _IMAGEHASH_EXP.search(src).group(1)\n\n        return FragImage_ri(src, show_width, show_height, hash_)\n\n\n@dcs.dataclass\nclass Contents_ri(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        imgs (list[FragImage_t]): 图像碎片列表\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    imgs: list[FragImage_ri] = dcs.field(default_factory=list, repr=False)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Contents_ri:\n        content_maps = data_map[\"content_detail\"]\n\n        texts = []\n        imgs = [FragImage_ri.from_json(m) for m in data_map[\"all_pics\"]]\n\n        def _frags():\n            for cmap in content_maps:\n                _type = cmap[\"type\"]\n                # 1纯文本\n                if _type == 1:\n                    frag = FragText_ri.from_json(cmap)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 3:\n                    continue\n                else:\n                    yield FragUnknown.from_proto(cmap)\n\n        objs = list(_frags())\n        objs += imgs\n\n        return Contents_ri(objs, texts, imgs)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass UserInfo_ri:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_ri:\n        portrait = data_map[\"portrait\"]\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_map[\"user_name\"]\n        nick_name_new = data_map[\"show_nickname\"]\n        return UserInfo_ri(portrait, user_name, nick_name_new)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait\n\n    def __eq__(self, obj: UserInfo_ri) -> bool:\n        return self.portrait == obj.portrait\n\n    def __hash__(self) -> int:\n        return hash(self.portrait)\n\n    def __bool__(self) -> bool:\n        return bool(self.portrait)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        return self.user_name or f\"{self.nick_name_new}/{self.portrait}\"\n\n\n@dcs.dataclass\nclass RecoverInfo(TbErrorExt):\n    \"\"\"\n    待恢复帖子信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_ri): 正文内容碎片列表\n        title (str): 标题内容\n\n        tid (int): 所在主题帖id\n        pid (int): 回复id\n        user (UserInfo_ri): 发布者的用户信息\n    \"\"\"\n\n    contents: Contents_ri = dcs.field(default_factory=Contents_ri)\n    title: str = \"\"\n\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_ri = dcs.field(default_factory=UserInfo_ri)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> RecoverInfo:\n        thread_info = data_map[\"thread_info\"]\n\n        contents = Contents_ri.from_json(thread_info)\n        title = thread_info[\"title\"]\n\n        tid = thread_info[\"thread_id\"]\n        pid = thread_info[\"post_id\"]\n        user = UserInfo_ri.from_json(data_map[\"user_info\"])\n\n        return RecoverInfo(contents, title, tid, pid, user)\n\n    def __eq__(self, obj: RecoverInfo) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @cached_property\n    def text(self) -> str:\n        if self.title:\n            text = f\"{self.title}\\n{self.contents.text}\"\n        else:\n            text = self.contents.text\n        return text\n"
  },
  {
    "path": "src/aiotieba/api/get_recovers/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Recover, Recovers\n"
  },
  {
    "path": "src/aiotieba/api/get_recovers/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import Recovers\n\nif TYPE_CHECKING:\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> Recovers:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n    recovers = Recovers.from_json(res_json)\n\n    return recovers\n\n\nasync def request(http_core: HttpCore, fid: int, user_id: int | None, pn: int, rn: int) -> Recovers:\n    params = [\n        (\"rn\", rn),\n        (\"forum_id\", fid),\n        (\"pn\", pn),\n        (\"type\", 1),\n        (\"sub_type\", 1),\n    ]\n    if user_id:\n        params.append((\"uid\", user_id))\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/manage/getRecoverList\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=16 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_recovers/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass UserInfo_rec:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_name: str = \"\"\n    portrait: str = \"\"\n    nick_name_new: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_rec:\n        portrait = data_map[\"portrait\"]\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_map[\"user_name\"]\n        nick_name_new = data_map[\"user_nickname\"]\n        return UserInfo_rec(user_name, portrait, nick_name_new)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait\n\n    def __eq__(self, obj: UserInfo_rec) -> bool:\n        return self.portrait == obj.portrait\n\n    def __hash__(self) -> int:\n        return hash(self.portrait)\n\n    def __bool__(self) -> bool:\n        return bool(self.portrait)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        return self.user_name or f\"{self.nick_name_new}/{self.portrait}\"\n\n\n@dcs.dataclass\nclass Recover:\n    \"\"\"\n    待恢复帖子信息\n\n    Attributes:\n        text (str): 文本内容\n        tid (int): 所在主题帖id\n        pid (int): 回复id 若为主题帖则该字段为0\n        user (UserInfo_rec): 发布者的用户信息\n        op_show_name (str): 操作人显示名称\n        op_time (int): 操作时间 10位时间戳 以秒为单位\n\n        is_floor (bool): 是否为楼中楼\n        is_hide (bool): 是否为屏蔽\n    \"\"\"\n\n    text: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_rec = dcs.field(default_factory=UserInfo_rec)\n    op_show_name: str = \"\"\n    op_time: int = 0\n\n    is_floor: bool = False\n    is_hide: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Recover:\n        thread_info = data_map[\"thread_info\"]\n        tid = int(thread_info[\"tid\"])\n        if post_info := data_map[\"post_info\"]:\n            text = post_info[\"abstract\"]\n            pid = int(post_info[\"pid\"])\n            user = UserInfo_rec.from_json(post_info)\n        else:\n            text = thread_info[\"abstract\"]\n            pid = 0\n            user = UserInfo_rec.from_json(thread_info)\n        is_floor = bool(data_map[\"is_foor\"])  # 百度的Code Review主要起到一个装饰的作用\n        is_hide = bool(int(data_map[\"is_frs_mask\"]))\n        op_show_name = data_map[\"op_info\"][\"name\"]\n        op_time = int(data_map[\"op_info\"][\"time\"])\n        return Recover(text, tid, pid, user, op_show_name, op_time, is_floor, is_hide)\n\n\n@dcs.dataclass\nclass Page_recover:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Page_recover:\n        page_size = data_map[\"rn\"]\n        current_page = data_map[\"pn\"]\n        has_more = data_map[\"has_more\"]\n        has_prev = current_page > 1\n        return Page_recover(page_size, current_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Recovers(TbErrorExt, Containers[Recover]):\n    \"\"\"\n    待恢复帖子列表\n\n    Attributes:\n        objs (list[Recover]): 待恢复帖子列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_recover): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_recover = dcs.field(default_factory=Page_recover)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> None:\n        objs = [Recover.from_json(t) for t in data_map[\"data\"][\"thread_list\"]]\n        page = Page_recover.from_json(data_map[\"data\"][\"page\"])\n        return Recovers(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_replys/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import Reply, Replys\n"
  },
  {
    "path": "src/aiotieba/api/get_replys/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import Account, HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import Replys\nfrom .protobuf import ReplyMeReqIdl_pb2, ReplyMeResIdl_pb2\n\nCMD = 303007\n\n\ndef pack_proto(account: Account, pn: int) -> bytes:\n    req_proto = ReplyMeReqIdl_pb2.ReplyMeReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.pn = str(pn)\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(proto: bytes) -> Replys:\n    res_proto = ReplyMeResIdl_pb2.ReplyMeResIdl()\n    res_proto.ParseFromString(proto)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    replys = Replys.from_proto(data_proto)\n\n    return replys\n\n\nasync def request_http(http_core: HttpCore, pn: int) -> Replys:\n    data = pack_proto(http_core.account, pn)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/feed/replyme\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=16 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, pn: int) -> Replys:\n    data = pack_proto(ws_core.account, pn)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_replys/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\n\nfrom ...enums import PrivLike, PrivReply\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers, TypeMessage\n\n\n@dcs.dataclass\nclass UserInfo_reply:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_reply:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        priv_like = PrivLike(priv_like) if (priv_like := data_proto.priv_sets.like) else PrivLike.PUBLIC\n        priv_reply = PrivReply(priv_reply) if (priv_reply := data_proto.priv_sets.reply) else PrivReply.ALL\n        return UserInfo_reply(user_id, portrait, user_name, nick_name_new, priv_like, priv_reply)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_reply) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass UserInfo_reply_p:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_reply_p:\n        user_id = data_proto.id\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        return UserInfo_reply_p(user_id, user_name, nick_name_new)\n\n    def __str__(self) -> str:\n        return self.user_name or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_reply_p) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        return self.user_name or f\"{self.nick_name_new}/{self.user_id}\"\n\n\n@dcs.dataclass\nclass UserInfo_reply_t:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        nick_name_new (str): 新版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    nick_name_new: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_reply_t:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        nick_name_new = data_proto.name_show\n        return UserInfo_reply_t(user_id, portrait, nick_name_new)\n\n    def __str__(self) -> str:\n        return self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_reply_t) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new\n\n    @cached_property\n    def log_name(self) -> str:\n        return str(self.user_id) if not self.portrait else f\"{self.nick_name_new}/{self.portrait}\"\n\n\n@dcs.dataclass\nclass Reply:\n    \"\"\"\n    回复信息\n    Attributes:\n        text (str): 文本内容\n\n        fname (str): 所在贴吧名\n        tid (int): 所在主题帖id\n        ppid (int): 所在楼层pid\n        pid (int): 回复id\n        user (UserInfo_reply): 发布者的用户信息\n        author_id (int): 发布者的user_id\n        post_user (UserInfo_reply_p): 楼层用户信息\n        thread_user (UserInfo_reply_t): 楼主用户信息\n\n        is_comment (bool): 是否楼中楼\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    text: str = \"\"\n\n    fname: str = \"\"\n    tid: int = 0\n    ppid: int = 0\n    pid: int = 0\n    user: UserInfo_reply = dcs.field(default_factory=UserInfo_reply)\n    post_user: UserInfo_reply_p = dcs.field(default_factory=UserInfo_reply_p)\n    thread_user: UserInfo_reply_t = dcs.field(default_factory=UserInfo_reply_t)\n\n    is_comment: bool = False\n    create_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Reply:\n        text = data_proto.content\n        fname = data_proto.fname\n        tid = data_proto.thread_id\n        ppid = data_proto.quote_pid\n        pid = data_proto.post_id\n        user = UserInfo_reply.from_proto(data_proto.replyer)\n        post_user = UserInfo_reply_p.from_proto(data_proto.quote_user)\n        thread_user = UserInfo_reply_t.from_proto(data_proto.thread_author_user)\n        is_comment = bool(data_proto.is_floor)\n        create_time = data_proto.time\n        return Reply(text, fname, tid, ppid, pid, user, post_user, thread_user, is_comment, create_time)\n\n    def __eq__(self, obj: Reply) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n\n@dcs.dataclass\nclass Page_reply:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Page_reply:\n        current_page = data_proto.current_page\n        has_more = bool(data_proto.has_more)\n        has_prev = bool(data_proto.has_prev)\n        return Page_reply(current_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass Replys(TbErrorExt, Containers[Reply]):\n    \"\"\"\n    收到回复列表\n\n    Attributes:\n        objs (list[Reply]): 收到回复列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_reply): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_reply = dcs.field(default_factory=Page_reply)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Replys:\n        objs = [Reply.from_proto(p) for p in data_proto.reply_list]\n        page = Page_reply.from_proto(data_proto.page)\n        return Replys(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_replys/protobuf/ReplyMeReqIdl.proto",
    "content": "// tbclient.ReplyMe.ReplyMeReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage ReplyMeReqIdl {\n    message DataReq {\n        string pn = 1;\n        CommonReq common = 3;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_replys/protobuf/ReplyMeReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13ReplyMeReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"h\\n\\rReplyMeReqIdl\\x12$\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x16.ReplyMeReqIdl.DataReq\\x1a\\x31\\n\\x07\\x44\\x61taReq\\x12\\n\\n\\x02pn\\x18\\x01 \\x01(\\t\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x03 \\x01(\\x0b\\x32\\n.CommonReqb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"ReplyMeReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_REPLYMEREQIDL\"]._serialized_start = 40\n    _globals[\"_REPLYMEREQIDL\"]._serialized_end = 144\n    _globals[\"_REPLYMEREQIDL_DATAREQ\"]._serialized_start = 95\n    _globals[\"_REPLYMEREQIDL_DATAREQ\"]._serialized_end = 144\n"
  },
  {
    "path": "src/aiotieba/api/get_replys/protobuf/ReplyMeResIdl.proto",
    "content": "// tbclient.ReplyMe.ReplyMeResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"Page.proto\";\nimport \"User.proto\";\n\nmessage ReplyMeResIdl {\n    Error error = 1;\n    message DataRes {\n        Page page = 1;\n        message ReplyList {\n            uint64 thread_id = 1;\n            uint64 post_id = 2;\n            uint32 time = 3;\n            string fname = 5;\n            string content = 6;\n            uint32 is_floor = 7;\n            string quote_content = 8;\n            User replyer = 9;\n            uint64 quote_pid = 14;\n            User quote_user = 15;\n            User thread_author_user = 25;\n        }\n        repeated ReplyList reply_list = 2;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_replys/protobuf/ReplyMeResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import Page_pb2 as Page__pb2\nfrom ..._protobuf import User_pb2 as User__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13ReplyMeResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nPage.proto\\x1a\\nUser.proto\"\\x95\\x03\\n\\rReplyMeResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12$\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x16.ReplyMeResIdl.DataRes\\x1a\\xc6\\x02\\n\\x07\\x44\\x61taRes\\x12\\x13\\n\\x04page\\x18\\x01 \\x01(\\x0b\\x32\\x05.Page\\x12\\x34\\n\\nreply_list\\x18\\x02 \\x03(\\x0b\\x32 .ReplyMeResIdl.DataRes.ReplyList\\x1a\\xef\\x01\\n\\tReplyList\\x12\\x11\\n\\tthread_id\\x18\\x01 \\x01(\\x04\\x12\\x0f\\n\\x07post_id\\x18\\x02 \\x01(\\x04\\x12\\x0c\\n\\x04time\\x18\\x03 \\x01(\\r\\x12\\r\\n\\x05\\x66name\\x18\\x05 \\x01(\\t\\x12\\x0f\\n\\x07\\x63ontent\\x18\\x06 \\x01(\\t\\x12\\x10\\n\\x08is_floor\\x18\\x07 \\x01(\\r\\x12\\x15\\n\\rquote_content\\x18\\x08 \\x01(\\t\\x12\\x16\\n\\x07replyer\\x18\\t \\x01(\\x0b\\x32\\x05.User\\x12\\x11\\n\\tquote_pid\\x18\\x0e \\x01(\\x04\\x12\\x19\\n\\nquote_user\\x18\\x0f \\x01(\\x0b\\x32\\x05.User\\x12!\\n\\x12thread_author_user\\x18\\x19 \\x01(\\x0b\\x32\\x05.Userb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"ReplyMeResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_REPLYMERESIDL\"]._serialized_start = 61\n    _globals[\"_REPLYMERESIDL\"]._serialized_end = 466\n    _globals[\"_REPLYMERESIDL_DATARES\"]._serialized_start = 140\n    _globals[\"_REPLYMERESIDL_DATARES\"]._serialized_end = 466\n    _globals[\"_REPLYMERESIDL_DATARES_REPLYLIST\"]._serialized_start = 227\n    _globals[\"_REPLYMERESIDL_DATARES_REPLYLIST\"]._serialized_end = 466\n"
  },
  {
    "path": "src/aiotieba/api/get_roomlist_by_fid/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import RoomList\n"
  },
  {
    "path": "src/aiotieba/api/get_roomlist_by_fid/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, CHAT_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import RoomList\n\n\ndef parse_body(body: bytes) -> RoomList:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n    roomlist = RoomList.from_json(res_json)\n    return roomlist\n\n\nasync def request(http_core: HttpCore, fid: int) -> RoomList:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", CHAT_VERSION),\n        (\"call_from\", \"frs\"),\n        (\"fid\", fid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/chat/getRoomListByFid\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_roomlist_by_fid/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\n\n\n@dcs.dataclass\nclass RoomList:\n    \"\"\"\n    某吧的聊天室列表\n\n    Attributes:\n        room_list (list[dict]): 每个聊天室的json内容\n    \"\"\"\n\n    room_list: list\n\n    @staticmethod\n    def from_json(resjson: dict) -> RoomList:  # TODO: 解析json并参数化而不是直接返回\n        room_list = []\n        for x in resjson[\"data\"][\"list\"]:\n            room_list.extend(x[\"room_list\"])\n        return RoomList(room_list)\n"
  },
  {
    "path": "src/aiotieba/api/get_self_follow_forums/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import SelfFollowForum, SelfFollowForums\n"
  },
  {
    "path": "src/aiotieba/api/get_self_follow_forums/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import SelfFollowForums\n\n\ndef parse_body(body: bytes) -> SelfFollowForums:\n    res_json = parse_json(body)\n    if code := res_json[\"error_code\"]:\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    self_follow_forums = SelfFollowForums.from_json(res_json)\n\n    return self_follow_forums\n\n\nasync def request(http_core: HttpCore, pn: int, rn: int) -> SelfFollowForums:\n    data = [\n        (\"tbs\", http_core.account.tbs),\n        (\"sort_type\", 3),\n        (\"call_from\", 3),\n        (\"page_no\", pn),\n        (\"res_num\", rn),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/c/f/forum/forumGuide\"),\n        data,\n        extra_headers=[(\"Subapp-Type\", \"hybrid\")],\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=64 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_self_follow_forums/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass SelfFollowForum:\n    \"\"\"\n    吧基本信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n        level (int): 用户等级\n        is_signed (bool): 是否已签到\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    level: int = 0\n    is_signed: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> SelfFollowForum:\n        fid = data_map[\"forum_id\"]\n        fname = data_map[\"forum_name\"]\n        level = data_map[\"level_id\"]\n        is_signed = data_map[\"is_sign\"]\n        return SelfFollowForum(fid, fname, level, is_signed)\n\n\n@dcs.dataclass\nclass SelfFollowForums(TbErrorExt, Containers[SelfFollowForum]):\n    \"\"\"\n    本账号关注贴吧列表\n\n    Attributes:\n        objs (list[SelfFollowForum]): 本账号关注贴吧列表\n        err (Exception | None): 捕获的异常\n\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    has_more: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> SelfFollowForums:\n        objs = [SelfFollowForum.from_json(m) for m in data_map[\"like_forum\"]]\n        has_more = data_map[\"like_forum_has_more\"]\n        return SelfFollowForums(objs, has_more)\n"
  },
  {
    "path": "src/aiotieba/api/get_self_follow_forums_v1/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import SelfFollowForumsV1, SelfFollowForumV1\n"
  },
  {
    "path": "src/aiotieba/api/get_self_follow_forums_v1/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import SelfFollowForumsV1\n\n\ndef parse_body(body: bytes) -> SelfFollowForumsV1:\n    res_json = parse_json(body)\n    if code := res_json[\"errno\"]:\n        raise TiebaServerError(code, res_json[\"errmsg\"])\n\n    data_map = res_json[\"data\"][\"like_forum\"]\n    self_follow_forums = SelfFollowForumsV1.from_json(data_map)\n\n    return self_follow_forums\n\n\nasync def request(http_core: HttpCore, pn: int, rn: int) -> SelfFollowForumsV1:\n    params = [\n        (\"pn\", pn),\n        (\"rn\", rn),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mg/o/getForumHome\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=128 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_self_follow_forums_v1/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass SelfFollowForumV1:\n    \"\"\"\n    吧基本信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n        level (int): 用户等级\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    level: int = 0\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> SelfFollowForumV1:\n        fid = data_map[\"forum_id\"]\n        fname = data_map[\"forum_name\"]\n        level = data_map[\"level_id\"]\n        return SelfFollowForumV1(fid, fname, level)\n\n\n@dcs.dataclass\nclass Page_sforumV1:\n    \"\"\"\n    页信息\n\n    Attributes:\n        current_page (int): 当前页码\n        total_page (int): 总页码\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    current_page: int = 0\n    total_page: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Page_sforumV1:\n        current_page = data_map[\"cur_page\"]\n        total_page = data_map[\"total_page\"]\n        has_more = current_page < total_page\n        has_prev = current_page > 1\n        return Page_sforumV1(current_page, total_page, has_more, has_prev)\n\n\n@dcs.dataclass\nclass SelfFollowForumsV1(TbErrorExt, Containers[SelfFollowForumV1]):\n    \"\"\"\n    本账号关注贴吧列表\n\n    Attributes:\n        objs (list[SelfFollowForum]): 本账号关注贴吧列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_sforum): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_sforumV1 = dcs.field(default_factory=Page_sforumV1)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> SelfFollowForumsV1:\n        objs = [SelfFollowForumV1.from_json(m) for m in data_map[\"list\"]]\n        page = Page_sforumV1.from_json(data_map[\"page\"])\n        return SelfFollowForumsV1(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_selfinfo_initNickname/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/get_selfinfo_initNickname/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import UserInfo_selfinit\n\n\ndef parse_body(body: bytes) -> UserInfo_selfinit:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    user_dict = res_json[\"user_info\"]\n    user = UserInfo_selfinit.from_json(user_dict)\n\n    return user\n\n\nasync def request(http_core: HttpCore) -> UserInfo_selfinit:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/s/initNickname\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_selfinfo_initNickname/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass UserInfo_selfinit:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_name (str): 用户名\n        nick_name_old (str): 旧版昵称\n        tieba_uid (int): 用户个人主页uid\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_name: str = \"\"\n    nick_name_old: str = \"\"\n    tieba_uid: int = 0\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_selfinit:\n        user_name = data_map[\"user_name\"]\n        nick_name_old = data_map[\"name_show\"]\n        tieba_uid = data_map[\"tieba_uid\"]\n        return UserInfo_selfinit(user_name, nick_name_old, tieba_uid)\n\n    def __str__(self) -> str:\n        return self.user_name\n\n    def __eq__(self, obj: UserInfo_selfinit) -> bool:\n        return self.tieba_uid == obj.tieba_uid\n\n    def __hash__(self) -> int:\n        return self.tieba_uid\n\n    def __bool__(self) -> bool:\n        return bool(self.tieba_uid)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_old\n\n    @cached_property\n    def log_name(self) -> str:\n        return self.user_name or f\"{self.nick_name_old}/{self.tieba_uid}\"\n"
  },
  {
    "path": "src/aiotieba/api/get_selfinfo_moindex/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import UserInfo_moindex\n"
  },
  {
    "path": "src/aiotieba/api/get_selfinfo_moindex/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import UserInfo_moindex\n\n\ndef parse_body(body: bytes) -> UserInfo_moindex:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n    user_dict = res_json[\"data\"]\n    user = UserInfo_moindex.from_json(user_dict)\n\n    return user\n\n\nasync def request(http_core: HttpCore) -> UserInfo_moindex:\n    params = [(\"need_user\", 1)]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/newmoindex\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=4 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_selfinfo_moindex/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...enums import Gender\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass UserInfo_moindex:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n\n        gender (Gender): 性别\n        post_num (int): 发帖数\n        fan_num (int): 粉丝数\n        follow_num (int): 关注数\n        forum_num (int): 关注贴吧数\n        sign (str): 个性签名\n\n        is_vip (bool): 是否超级会员\n\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n\n    gender: Gender = Gender.UNKNOWN\n    post_num: int = 0\n    fan_num: int = 0\n    follow_num: int = 0\n    forum_num: int = 0\n    sign: str = \"\"\n\n    is_vip: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_moindex:\n        user_id = data_map[\"id\"]\n        portrait = data_map[\"portrait\"]\n        user_name = data_map[\"name\"]\n\n        gender = Gender(data_map[\"user_sex\"])\n        post_num = data_map[\"post_num\"]\n        fan_num = data_map[\"fans_num\"]\n        follow_num = data_map[\"concern_num\"]\n        forum_num = data_map[\"like_forum_num\"]\n        sign = data_map[\"intro\"]\n\n        if vip_dict := data_map[\"vipInfo\"]:\n            is_vip = int(vip_dict[\"v_status\"]) == 3\n        else:\n            is_vip = False\n\n        return UserInfo_moindex(\n            user_id, portrait, user_name, gender, post_num, fan_num, follow_num, forum_num, sign, is_vip\n        )\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait\n\n    def __eq__(self, obj: UserInfo_moindex) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return self.user_id\n\n    @cached_property\n    def log_name(self) -> str:\n        return self.user_name or self.portrait\n"
  },
  {
    "path": "src/aiotieba/api/get_square_forums/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import SquareForum, SquareForums\n"
  },
  {
    "path": "src/aiotieba/api/get_square_forums/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import Account, HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import SquareForums\nfrom .protobuf import GetForumSquareReqIdl_pb2, GetForumSquareResIdl_pb2\n\nCMD = 309653\n\n\ndef pack_proto(account: Account, cname: str, pn: int, rn: int) -> bytes:\n    req_proto = GetForumSquareReqIdl_pb2.GetForumSquareReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.class_name = cname\n    req_proto.data.pn = pn\n    req_proto.data.rn = rn\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> SquareForums:\n    res_proto = GetForumSquareResIdl_pb2.GetForumSquareResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    square_forums = SquareForums.from_proto(data_proto)\n\n    return square_forums\n\n\nasync def request_http(http_core: HttpCore, cname: str, pn: int, rn: int) -> SquareForums:\n    data = pack_proto(http_core.account, cname, pn, rn)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/forum/getForumSquare\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=16 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, cname: str, pn: int, rn: int) -> SquareForums:\n    data = pack_proto(ws_core.account, cname, pn, rn)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_square_forums/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers, TypeMessage\n\n\n@dcs.dataclass\nclass SquareForum:\n    \"\"\"\n    吧广场贴吧信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n\n        member_num (int): 吧会员数\n        post_num (int): 发帖量\n\n        is_followed (bool): 是否已关注\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n\n    member_num: int = 0\n    post_num: int = 0\n\n    is_followed: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> SquareForum:\n        fid = data_proto.forum_id\n        fname = data_proto.forum_name\n        member_num = data_proto.member_count\n        post_num = data_proto.thread_count\n        is_followed = bool(data_proto.is_like)\n        return SquareForum(fid, fname, member_num, post_num, is_followed)\n\n    def __eq__(self, obj: SquareForum) -> bool:\n        return self.fid == obj.fid\n\n    def __hash__(self) -> int:\n        return self.fid\n\n\n@dcs.dataclass\nclass Page_square:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Page_square:\n        page_size = data_proto.page_size\n        current_page = data_proto.current_page\n        total_page = data_proto.total_page\n        total_count = data_proto.total_count\n        has_more = bool(data_proto.has_more)\n        has_prev = bool(data_proto.has_prev)\n        return Page_square(page_size, current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass SquareForums(TbErrorExt, Containers[SquareForum]):\n    \"\"\"\n    吧广场列表\n\n    Attributes:\n        objs (list[SquareForum]): 吧广场列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_square): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_square = dcs.field(default_factory=Page_square)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage | None = None) -> None:\n        objs = [SquareForum.from_proto(p) for p in data_proto.forum_info]\n        page = Page_square.from_proto(data_proto.page)\n        return SquareForums(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_square_forums/protobuf/GetForumSquareReqIdl.proto",
    "content": "// tbclient.GetForumSquare.GetForumSquareReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage GetForumSquareReqIdl {\n    message DataReq {\n        CommonReq common = 1;\n        string class_name = 2;\n        int32 pn = 3;\n        int32 rn = 4;\n        int64 user_id = 5;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_square_forums/protobuf/GetForumSquareReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1aGetForumSquareReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\xa7\\x01\\n\\x14GetForumSquareReqIdl\\x12+\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1d.GetForumSquareReqIdl.DataReq\\x1a\\x62\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x01 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\x12\\n\\nclass_name\\x18\\x02 \\x01(\\t\\x12\\n\\n\\x02pn\\x18\\x03 \\x01(\\x05\\x12\\n\\n\\x02rn\\x18\\x04 \\x01(\\x05\\x12\\x0f\\n\\x07user_id\\x18\\x05 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetForumSquareReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETFORUMSQUAREREQIDL\"]._serialized_start = 48\n    _globals[\"_GETFORUMSQUAREREQIDL\"]._serialized_end = 215\n    _globals[\"_GETFORUMSQUAREREQIDL_DATAREQ\"]._serialized_start = 117\n    _globals[\"_GETFORUMSQUAREREQIDL_DATAREQ\"]._serialized_end = 215\n"
  },
  {
    "path": "src/aiotieba/api/get_square_forums/protobuf/GetForumSquareResIdl.proto",
    "content": "// tbclient.GetForumSquare.GetForumSquareResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"Page.proto\";\n\nmessage GetForumSquareResIdl {\n    Error error = 1;\n    message DataRes {\n        message RecommendForumInfo {\n            uint64 forum_id = 2;\n            string forum_name = 3;\n            uint32 is_like = 4;\n            uint32 member_count = 5;\n            uint32 thread_count = 6;\n        }\n        repeated RecommendForumInfo forum_info = 2;\n        Page page = 3;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_square_forums/protobuf/GetForumSquareResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import Page_pb2 as Page__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1aGetForumSquareResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nPage.proto\"\\xba\\x02\\n\\x14GetForumSquareResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12+\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1d.GetForumSquareResIdl.DataRes\\x1a\\xdd\\x01\\n\\x07\\x44\\x61taRes\\x12\\x44\\n\\nforum_info\\x18\\x02 \\x03(\\x0b\\x32\\x30.GetForumSquareResIdl.DataRes.RecommendForumInfo\\x12\\x13\\n\\x04page\\x18\\x03 \\x01(\\x0b\\x32\\x05.Page\\x1aw\\n\\x12RecommendForumInfo\\x12\\x10\\n\\x08\\x66orum_id\\x18\\x02 \\x01(\\x04\\x12\\x12\\n\\nforum_name\\x18\\x03 \\x01(\\t\\x12\\x0f\\n\\x07is_like\\x18\\x04 \\x01(\\r\\x12\\x14\\n\\x0cmember_count\\x18\\x05 \\x01(\\r\\x12\\x14\\n\\x0cthread_count\\x18\\x06 \\x01(\\rb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetForumSquareResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETFORUMSQUARERESIDL\"]._serialized_start = 56\n    _globals[\"_GETFORUMSQUARERESIDL\"]._serialized_end = 370\n    _globals[\"_GETFORUMSQUARERESIDL_DATARES\"]._serialized_start = 149\n    _globals[\"_GETFORUMSQUARERESIDL_DATARES\"]._serialized_end = 370\n    _globals[\"_GETFORUMSQUARERESIDL_DATARES_RECOMMENDFORUMINFO\"]._serialized_start = 251\n    _globals[\"_GETFORUMSQUARERESIDL_DATARES_RECOMMENDFORUMINFO\"]._serialized_end = 370\n"
  },
  {
    "path": "src/aiotieba/api/get_statistics/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Statistics\n"
  },
  {
    "path": "src/aiotieba/api/get_statistics/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import Statistics\n\n\ndef parse_body(body: bytes) -> Statistics:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    data_map = res_json[\"data\"]\n    stat = Statistics.from_json(data_map)\n\n    return stat\n\n\nasync def request(http_core: HttpCore, fid: int) -> Statistics:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"forum_id\", fid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/forum/getforumdata\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=4 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_statistics/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from collections.abc import Sequence\n\n\n@dcs.dataclass\nclass Statistics:\n    \"\"\"\n    吧务后台统计信息\n    时间从旧到新\n\n    Attributes:\n        view (list[int]): 浏览量\n        thread (list[int]): 主题帖数\n        new_member (list[int]): 新增吧会员数\n        post (list[int]): 回复数\n        sign_ratio (list[int]): 签到率\n        avg_time (list[int]): 人均浏览时长\n        avg_times (list[int]): 人均进吧次数\n        recommend (list[int]): 首页推荐数\n    \"\"\"\n\n    view: list[int] = dcs.field(default_factory=list)\n    thread: list[int] = dcs.field(default_factory=list)\n    new_member: list[int] = dcs.field(default_factory=list)\n    post: list[int] = dcs.field(default_factory=list)\n    sign_ratio: list[int] = dcs.field(default_factory=list)\n    avg_time: list[int] = dcs.field(default_factory=list)\n    avg_times: list[int] = dcs.field(default_factory=list)\n    recommend: list[int] = dcs.field(default_factory=list)\n\n    @staticmethod\n    def from_json(data_seq: Sequence) -> Statistics:\n        def extract(i: int) -> list[int]:\n            seq: list = data_seq[i][\"group\"][1][\"values\"]\n            seq = [int(item[\"value\"]) for item in seq]\n            return seq\n\n        view = extract(0)\n        thread = extract(1)\n        new_member = extract(2)\n        post = extract(3)\n        sign_ratio = extract(4)\n        avg_time = extract(5)\n        avg_times = extract(6)\n        recommend = extract(7)\n\n        return Statistics(view, thread, new_member, post, sign_ratio, avg_time, avg_times, recommend)\n"
  },
  {
    "path": "src/aiotieba/api/get_tab_map/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import TabMap\n"
  },
  {
    "path": "src/aiotieba/api/get_tab_map/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import Account, HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import TabMap\nfrom .protobuf import SearchPostForumReqIdl_pb2, SearchPostForumResIdl_pb2\n\nCMD = 309466\n\n\ndef pack_proto(account: Account, fname: str) -> bytes:\n    req_proto = SearchPostForumReqIdl_pb2.SearchPostForumReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.fname = fname\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> TabMap:\n    res_proto = SearchPostForumResIdl_pb2.SearchPostForumResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    tab_map = TabMap.from_proto(data_proto)\n\n    return tab_map\n\n\nasync def request_http(http_core: HttpCore, fname: str) -> TabMap:\n    data = pack_proto(http_core.account, fname)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(\n            scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/forum/searchPostForum\", query_string=f\"cmd={CMD}\"\n        ),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=4 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, fname: str) -> TabMap:\n    data = pack_proto(ws_core.account, fname)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_tab_map/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from .._classdef import TypeMessage\n\n\n@dcs.dataclass\nclass TabMap(TbErrorExt):\n    \"\"\"\n    分区名到分区id的映射\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        map (dict[str, int]): 分区名到分区id的映射\n    \"\"\"\n\n    map: dict[str, int] = dcs.field(default_factory=dict)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> TabMap:\n        map_ = {tab_proto.tab_name: tab_proto.tab_id for tab_proto in data_proto.exact_match.tab_info}\n        return TabMap(map_)\n\n    def __getitem__(self, key: str) -> int:\n        return self.map[key]\n"
  },
  {
    "path": "src/aiotieba/api/get_tab_map/protobuf/SearchPostForumReqIdl.proto",
    "content": "// tbclient.SearchPostForum.SearchPostForumReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage SearchPostForumReqIdl {\n    message DataReq {\n        CommonReq common = 1;\n        string fname = 2;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_tab_map/protobuf/SearchPostForumReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1bSearchPostForumReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"{\\n\\x15SearchPostForumReqIdl\\x12,\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1e.SearchPostForumReqIdl.DataReq\\x1a\\x34\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x01 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\r\\n\\x05\\x66name\\x18\\x02 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"SearchPostForumReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_SEARCHPOSTFORUMREQIDL\"]._serialized_start = 48\n    _globals[\"_SEARCHPOSTFORUMREQIDL\"]._serialized_end = 171\n    _globals[\"_SEARCHPOSTFORUMREQIDL_DATAREQ\"]._serialized_start = 119\n    _globals[\"_SEARCHPOSTFORUMREQIDL_DATAREQ\"]._serialized_end = 171\n"
  },
  {
    "path": "src/aiotieba/api/get_tab_map/protobuf/SearchPostForumResIdl.proto",
    "content": "// tbclient.SearchPostForum.SearchPostForumResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"FrsTabInfo.proto\";\n\nmessage SearchPostForumResIdl {\n    Error error = 1;\n    message DataRes {\n        message SearchForum {\n            int64 forum_id = 1;\n            string forum_name = 2;\n            repeated FrsTabInfo tab_info = 9;\n        }\n        SearchForum exact_match = 1;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_tab_map/protobuf/SearchPostForumResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import FrsTabInfo_pb2 as FrsTabInfo__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1bSearchPostForumResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\x10\\x46rsTabInfo.proto\"\\xfd\\x01\\n\\x15SearchPostForumResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12,\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1e.SearchPostForumResIdl.DataRes\\x1a\\x9e\\x01\\n\\x07\\x44\\x61taRes\\x12?\\n\\x0b\\x65xact_match\\x18\\x01 \\x01(\\x0b\\x32*.SearchPostForumResIdl.DataRes.SearchForum\\x1aR\\n\\x0bSearchForum\\x12\\x10\\n\\x08\\x66orum_id\\x18\\x01 \\x01(\\x03\\x12\\x12\\n\\nforum_name\\x18\\x02 \\x01(\\t\\x12\\x1d\\n\\x08tab_info\\x18\\t \\x03(\\x0b\\x32\\x0b.FrsTabInfob\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"SearchPostForumResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_SEARCHPOSTFORUMRESIDL\"]._serialized_start = 63\n    _globals[\"_SEARCHPOSTFORUMRESIDL\"]._serialized_end = 316\n    _globals[\"_SEARCHPOSTFORUMRESIDL_DATARES\"]._serialized_start = 158\n    _globals[\"_SEARCHPOSTFORUMRESIDL_DATARES\"]._serialized_end = 316\n    _globals[\"_SEARCHPOSTFORUMRESIDL_DATARES_SEARCHFORUM\"]._serialized_start = 234\n    _globals[\"_SEARCHPOSTFORUMRESIDL_DATARES_SEARCHFORUM\"]._serialized_end = 316\n"
  },
  {
    "path": "src/aiotieba/api/get_threads/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import ShareThread, Thread, Threads, UserInfo_t\n"
  },
  {
    "path": "src/aiotieba/api/get_threads/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import Threads\nfrom .protobuf import FrsPageReqIdl_pb2, FrsPageResIdl_pb2\n\nCMD = 301001\n\n\ndef pack_proto(fname: str, pn: int, rn: int, sort: int, is_good: bool, version: str) -> bytes:\n    req_proto = FrsPageReqIdl_pb2.FrsPageReqIdl()\n    req_proto.data.common._client_type = 2\n    req_proto.data.common._client_version = version\n    req_proto.data.kw = fname\n    req_proto.data.pn = 0 if pn == 1 else pn\n    req_proto.data.rn = rn\n    req_proto.data.rn_need = rn + 5\n    req_proto.data.is_good = int(is_good)\n    req_proto.data.sort_type = sort\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> Threads:\n    res_proto = FrsPageResIdl_pb2.FrsPageResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    threads = Threads.from_proto(data_proto)\n\n    return threads\n\n\nasync def request_http(\n    http_core: HttpCore, fname: str, pn: int, rn: int, sort: int, is_good: bool, version: str\n) -> Threads:\n    data = pack_proto(fname, pn, rn, sort, is_good, version)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/f/frs/page\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=256 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, fname: str, pn: int, rn: int, sort: int, is_good: bool, version: str) -> Threads:\n    data = pack_proto(fname, pn, rn, sort, is_good, version)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_threads/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\n\nfrom ...enums import Gender, PrivLike, PrivReply, ThreadType\nfrom ...exception import TbErrorExt\nfrom ...helper import deprecated\nfrom ...logging import get_logger as LOG\nfrom .._classdef import Containers, TypeMessage, VoteInfo\nfrom .._classdef.contents import (\n    _IMAGEHASH_EXP,\n    FragAt,\n    FragEmoji,\n    FragImage,\n    FragLink,\n    FragText,\n    FragTiebaPlus,\n    FragUnknown,\n    FragVideo,\n    FragVoice,\n    TypeFragment,\n    TypeFragText,\n)\n\nFragText_t = FragText_st = FragText\nFragEmoji_t = FragEmoji_st = FragEmoji\nFragImage_t = FragImage\nFragAt_t = FragAt_st = FragAt\nFragLink_t = FragLink_st = FragLink\nFragTiebaPlus_t = FragTiebaPlus_st = FragTiebaPlus\nFragVideo_t = FragVideo_st = FragVideo\nFragVoice_t = FragVoice_st = FragVoice\n\n\n@dcs.dataclass\nclass FragImage_feed:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 小图链接 宽720px\n        big_src (str): 大图链接 宽960px\n        origin_src (str): 原图链接\n        width (int): 图像宽度\n        height (int): 图像高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    big_src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    width: int = 0\n    height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragImage_feed:\n        src = data_proto.small_pic_url\n        big_src = data_proto.big_pic_url\n        origin_src = data_proto.origin_pic_url\n        width = data_proto.width\n        height = data_proto.height\n\n        hash_ = _IMAGEHASH_EXP.search(origin_src).group(1)\n\n        return FragImage_feed(src, big_src, origin_src, width, height, hash_)\n\n\n@dcs.dataclass\nclass FragEmoji_feed:\n    \"\"\"\n    表情碎片\n\n    Attributes:\n        id (str): 表情图片id\n        desc (str): 表情描述\n    \"\"\"\n\n    id: str = \"\"\n    desc: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragEmoji_feed:\n        id_ = data_proto.name\n        desc = data_proto.c\n        return FragEmoji_feed(id_, desc)\n\n\n@dcs.dataclass\nclass Contents_t(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_t | FragEmoji_feed]): 表情碎片列表\n        imgs (list[FragImage_t | FragImage_feed]): 图像碎片列表\n        ats (list[FragAt_t]): @碎片列表\n        links (list[FragLink_t]): 链接碎片列表\n        tiebapluses (list[FragTiebaPlus_t]): 贴吧plus碎片列表\n        video (FragVideo_t): 视频碎片\n        voice (FragVoice_t): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_t | FragEmoji_feed] = dcs.field(default_factory=list, repr=False)\n    imgs: list[FragImage_t | FragImage_feed] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_t] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_t] = dcs.field(default_factory=list, repr=False)\n    tiebapluses: list[FragTiebaPlus_t] = dcs.field(default_factory=list, repr=False)\n    video: FragVideo_t = dcs.field(default_factory=FragVideo_t, repr=False)\n    voice: FragVoice_t = dcs.field(default_factory=FragVoice_t, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_t:\n        content_protos = data_proto.first_post_content\n\n        texts = []\n        emojis = []\n        imgs = []\n        ats = []\n        links = []\n        tiebapluses = []\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条\n                if _type in [0, 9, 18, 27]:\n                    frag = FragText_t.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_t.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                # 20:tid=5470214675\n                elif _type in [3, 20]:\n                    frag = FragImage_t.from_proto(proto)\n                    imgs.append(frag)\n                    yield frag\n                elif _type == 4:\n                    frag = FragAt_t.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_t.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 10:  # voice\n                    continue\n                elif _type == 5:  # video\n                    continue\n                # 35|36:tid=7769728331 / 37:tid=7760184147\n                elif _type in [35, 36, 37]:\n                    frag = FragTiebaPlus_t.from_proto(proto)\n                    tiebapluses.append(frag)\n                    texts.append(frag)\n                    yield frag\n                # outdated tiebaplus\n                elif _type == 34:\n                    continue\n                else:\n                    yield FragUnknown.from_proto(frag)\n\n        objs = list(_frags())\n\n        if data_proto.video_info.video_width:\n            video = FragVideo_t.from_proto(data_proto.video_info)\n            objs.append(video)\n        else:\n            video = FragVideo_t()\n\n        if data_proto.voice_info:\n            voice = FragVoice_t.from_proto(data_proto.voice_info[0])\n            objs.append(voice)\n        else:\n            voice = FragVoice_t()\n\n        return Contents_t(objs, texts, emojis, imgs, ats, links, tiebapluses, video, voice)\n\n    @staticmethod\n    def from_feed(data_proto: TypeMessage) -> Contents_t:\n        texts = []\n        emojis = []\n        imgs = []\n\n        def _frags():\n            for component in data_proto.components:\n                _type = component.component\n                if _type == \"feed_abstract\":\n                    for proto in component.feed_abstract.data:\n                        if proto.type == 1:\n                            frag = FragText_t.from_proto(proto.text_info)\n                            texts.append(frag)\n                            yield frag\n                        elif proto.type == 3:\n                            frag = FragEmoji_feed.from_proto(proto.emoji_info)\n                            emojis.append(frag)\n                            yield frag\n                        else:\n                            yield FragUnknown.from_proto(frag)\n                elif _type == \"feed_pic\":\n                    for proto in component.feed_pic.pics:\n                        frag = FragImage_feed.from_proto(proto)\n                        imgs.append(frag)\n                        yield frag\n                elif _type in [\"feed_head\", \"feed_title\", \"feed_social\", \"feed_poll\"]:\n                    continue\n                else:\n                    LOG().debug(\"Unknown component type. type=%s\", _type)\n\n        objs = list(_frags())\n\n        video = FragVideo_t()\n        voice = FragVoice_t()\n\n        return Contents_t(objs, texts, emojis, imgs, [], [], [], video, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass Page_t:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Page_t:\n        page_size = data_proto.page_size\n        current_page = data_proto.current_page\n        if current_page == 0 and page_size != 0:\n            current_page = 1\n        total_page = data_proto.total_page\n        total_count = data_proto.total_count\n        has_more = bool(data_proto.has_more)\n        has_prev = bool(data_proto.has_prev)\n        return Page_t(page_size, current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass UserInfo_t:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        level (int): 等级\n        glevel (int): 贴吧成长等级\n        gender (Gender): 性别\n        icons (list[str]): 印记信息\n\n        is_bawu (bool): 是否吧务\n        is_vip (bool): 是否超级会员\n        is_god (bool): 是否大神\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    level: int = 0\n    glevel: int = 0\n    gender: Gender = Gender.UNKNOWN\n    icons: list[str] = dcs.field(default_factory=list)\n\n    is_bawu: bool = False\n    is_vip: bool = False\n    is_god: bool = False\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_t:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        level = data_proto.level_id\n        glevel = data_proto.user_growth.level_id\n        gender = Gender(data_proto.gender)\n        icons = [name for i in data_proto.iconinfo if (name := i.name)]\n        is_bawu = bool(data_proto.is_bawu)\n        is_vip = bool(data_proto.new_tshow_icon)\n        is_god = bool(data_proto.new_god_data.status)\n        priv_like = PrivLike(priv_like) if (priv_like := data_proto.priv_sets.like) else PrivLike.PUBLIC\n        priv_reply = PrivReply(priv_reply) if (priv_reply := data_proto.priv_sets.reply) else PrivReply.ALL\n        return UserInfo_t(\n            user_id,\n            portrait,\n            user_name,\n            nick_name_new,\n            level,\n            glevel,\n            gender,\n            icons,\n            is_bawu,\n            is_vip,\n            is_god,\n            priv_like,\n            priv_reply,\n        )\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_t) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass FragImage_st:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 小图链接 宽580px\n        big_src (str): 大图链接 宽720px\n        origin_src (str): 原图链接\n        show_width (int): 图像在客户端预览显示的宽度\n        show_height (int): 图像在客户端预览显示的高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    big_src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    show_width: int = 0\n    show_height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragImage_st:\n        src = data_proto.water_pic\n        big_src = data_proto.small_pic\n        origin_src = data_proto.big_pic\n\n        show_width = data_proto.width\n        show_height = data_proto.height\n\n        if hash_obj := _IMAGEHASH_EXP.search(src):\n            hash_ = hash_obj.group(1)\n        else:\n            hash_ = \"\"\n\n        return FragImage_st(src, big_src, origin_src, show_width, show_height, hash_)\n\n\n@dcs.dataclass\nclass Contents_st(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_st]): 表情碎片列表\n        imgs (list[FragImage_st]): 图像碎片列表\n        ats (list[FragAt_st]): @碎片列表\n        links (list[FragLink_st]): 链接碎片列表\n        tiebapluses (list[FragTiebaPlus_st]): 贴吧plus碎片列表\n        video (FragVideo_st): 视频碎片\n        voice (FragVoice_st): 视频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_st] = dcs.field(default_factory=list, repr=False)\n    imgs: list[FragImage_st] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_st] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_st] = dcs.field(default_factory=list, repr=False)\n    tiebapluses: list[FragTiebaPlus_st] = dcs.field(default_factory=list, repr=False)\n    video: FragVideo_st = dcs.field(default_factory=FragVideo_st, repr=False)\n    voice: FragVoice_st = dcs.field(default_factory=FragVoice_st, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_st:\n        content_protos = data_proto.content\n\n        texts = []\n        emojis = []\n        imgs = [FragImage_st.from_proto(p) for p in data_proto.media]\n        ats = []\n        links = []\n        tiebapluses = []\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条\n                if _type in [0, 9, 18, 27]:\n                    frag = FragText_st.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_st.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                elif _type == 4:\n                    frag = FragAt_st.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_st.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 5:  # video\n                    continue\n                # 35|36:tid=7769728331 / 37:tid=7760184147\n                elif _type in [35, 36, 37]:\n                    frag = FragTiebaPlus_st.from_proto(proto)\n                    tiebapluses.append(frag)\n                    texts.append(frag)\n                    yield frag\n                # outdated tiebaplus\n                elif _type == 34:\n                    continue\n                else:\n                    yield FragUnknown.from_proto(frag)\n\n        objs = list(_frags())\n\n        if ats:\n            del ats[0]\n            del objs[0]\n        objs += imgs\n\n        if data_proto.video_info.video_width:\n            video = FragVideo_st.from_proto(data_proto.video_info)\n            objs.append(video)\n        else:\n            video = FragVideo_st()\n\n        if data_proto.voice_info:\n            voice = FragVoice_st.from_proto(data_proto.voice_info[0])\n            objs.append(voice)\n        else:\n            voice = FragVoice_st()\n\n        return Contents_st(objs, texts, emojis, imgs, ats, links, tiebapluses, video, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass ShareThread:\n    \"\"\"\n    被分享的主题帖信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_st): 正文内容碎片列表\n        title (str): 标题内容\n\n        author_id (int): 发布者的user_id\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 主题帖tid\n        pid (int): 首楼的回复id\n\n        vote_info (VoteInfo): 投票内容\n    \"\"\"\n\n    contents: Contents_st = dcs.field(default_factory=Contents_st)\n    title: str = \"\"\n\n    author_id: int = 0\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n\n    vote_info: VoteInfo = dcs.field(default_factory=VoteInfo)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> ShareThread:\n        contents = Contents_st.from_proto(data_proto)\n        author_id = data_proto.content[0].uid if data_proto.content else 0\n        title = data_proto.title\n        fid = data_proto.fid\n        fname = data_proto.fname\n        tid = int(tid) if (tid := data_proto.tid) else 0\n        pid = data_proto.pid\n        vote_info = VoteInfo.from_proto(data_proto.poll_info)\n        return ShareThread(contents, title, author_id, fid, fname, tid, pid, vote_info)\n\n    def __eq__(self, obj: ShareThread) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @cached_property\n    def text(self) -> str:\n        if self.title:\n            text = f\"{self.title}\\n{self.contents.text}\"\n        else:\n            text = self.contents.text\n        return text\n\n\n@dcs.dataclass\nclass Thread:\n    \"\"\"\n    主题帖信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_t): 正文内容碎片列表\n        title (str): 标题内容\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 主题帖tid\n        pid (int): 首楼回复pid\n        user (UserInfo_t): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        type (ThreadType): 帖子类型\n        tab_id (int): 帖子所在分区id\n        is_good (bool): 是否精品帖\n        is_top (bool): 是否置顶帖\n        is_share (bool): 是否分享帖\n        is_hide (bool): 是否被屏蔽\n        is_livepost (bool): 是否为置顶话题\n        is_help (bool): 是否为求助帖\n\n        vote_info (VoteInfo): 投票信息\n        share_origin (ShareThread): 转发来的原帖内容\n        view_num (int): 浏览量\n        reply_num (int): 回复数\n        share_num (int): 分享数\n        agree (int): 点赞数\n        disagree (int): 点踩数\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n        last_time (int): 最后回复时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    contents: Contents_t = dcs.field(default_factory=Contents_t)\n    title: str = \"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_t = dcs.field(default_factory=UserInfo_t)\n    author_id: int = 0\n\n    type: ThreadType = ThreadType.UNKNOWN\n    tab_id: int = 0\n    is_good: bool = False\n    is_top: bool = False\n    is_share: bool = False\n    is_hide: bool = False\n    is_livepost: bool = False\n\n    vote_info: VoteInfo = dcs.field(default_factory=VoteInfo)\n    share_origin: ShareThread = dcs.field(default_factory=ShareThread)\n    view_num: int = 0\n    reply_num: int = 0\n    share_num: int = 0\n    agree: int = 0\n    disagree: int = 0\n    create_time: int = 0\n    last_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> None:\n        contents = Contents_t.from_proto(data_proto)\n        title = data_proto.title\n        tid = data_proto.id\n        pid = data_proto.first_post_id\n        author_id = data_proto.author_id\n\n        type_ = ThreadType(data_proto.thread_type)\n        if type_ == ThreadType.UNKNOWN:\n            LOG().debug(\"Unknown thread type. tid=%d, type=%s\", tid, data_proto.thread_type)\n\n        tab_id = data_proto.tab_id\n        is_good = bool(data_proto.is_good)\n        is_top = bool(data_proto.is_top)\n        is_share = bool(data_proto.is_share_thread)\n        is_hide = bool(data_proto.is_frs_mask)\n        is_livepost = bool(data_proto.is_livepost)\n        vote_info = VoteInfo.from_proto(data_proto.poll_info)\n        if is_share:\n            if data_proto.origin_thread_info.pid:\n                share_origin = ShareThread.from_proto(data_proto.origin_thread_info)\n            else:\n                is_share = False\n                share_origin = ShareThread()\n        else:\n            share_origin = ShareThread()\n        view_num = data_proto.view_num\n        reply_num = data_proto.reply_num\n        share_num = data_proto.share_num\n        agree = data_proto.agree.agree_num\n        disagree = data_proto.agree.disagree_num\n        create_time = data_proto.create_time\n        last_time = data_proto.last_time_int\n        return Thread(\n            contents,\n            title,\n            0,\n            \"\",\n            tid,\n            pid,\n            None,\n            author_id,\n            type_,\n            tab_id,\n            is_good,\n            is_top,\n            is_share,\n            is_hide,\n            is_livepost,\n            vote_info,\n            share_origin,\n            view_num,\n            reply_num,\n            share_num,\n            agree,\n            disagree,\n            create_time,\n            last_time,\n        )\n\n    @staticmethod\n    def from_feed(data_proto: TypeMessage) -> None:\n        contents = Contents_t.from_feed(data_proto)\n\n        business_info_map = {it.key: it.value for it in data_proto.business_info}\n\n        title = business_info_map[\"title\"]\n        tid = int(business_info_map[\"thread_id\"])\n        pid = 0\n\n        author_id = int(business_info_map[\"user_id\"])\n        type_ = ThreadType(int(business_info_map[\"thread_type\"]))\n        tab_id = int(business_info_map[\"inner_tab_id\"])\n        is_good = False\n        is_top = False\n        is_share = False\n        is_hide = False\n        is_livepost = False\n\n        vote_info = None\n        for component in data_proto.components:\n            _type = component.component\n            if _type != \"feed_poll\":  # TODO: 不确定，需要找个抽奖帖测试\n                continue\n            vote_info = VoteInfo.from_proto(component.feed_poll)\n        if vote_info is None:\n            vote_info = VoteInfo()\n\n        share_origin = ShareThread()  # TODO: 找个转发帖测试\n\n        view_num = int(business_info_map[\"view_num\"])\n        reply_num = 0\n        share_num = 0\n        agree = 0\n        disagree = 0\n        create_time = int(business_info_map[\"create_time\"])\n        last_time = 0\n        return Thread(\n            contents,\n            title,\n            0,\n            \"\",\n            tid,\n            pid,\n            None,\n            author_id,\n            type_,\n            tab_id,\n            is_good,\n            is_top,\n            is_share,\n            is_hide,\n            is_livepost,\n            vote_info,\n            share_origin,\n            view_num,\n            reply_num,\n            share_num,\n            agree,\n            disagree,\n            create_time,\n            last_time,\n        )\n\n    def __eq__(self, obj: Thread) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @cached_property\n    def text(self) -> str:\n        if self.title:\n            text = f\"{self.title}\\n{self.contents.text}\"\n        else:\n            text = self.contents.text\n        return text\n\n    @property\n    @deprecated(\"使用 thread.type == ThreadType.HELP 作为替代\")\n    def is_help(self) -> bool:\n        return self.type == ThreadType.HELP\n\n\n@dcs.dataclass\nclass Forum_t:\n    \"\"\"\n    吧信息\n\n    Attributes:\n        fid (int): 贴吧id\n        fname (str): 贴吧名\n\n        category (str): 一级分类\n        subcategory (str): 二级分类\n\n        member_num (int): 吧会员数\n        post_num (int): 发帖量\n        thread_num (int): 主题帖数\n\n        has_bawu (bool): 是否有吧务\n        has_rule (bool): 是否有吧规\n    \"\"\"\n\n    fid: int = 0\n    fname: str = \"\"\n\n    category: str = \"\"\n    subcategory: str = \"\"\n\n    member_num: int = 0\n    post_num: int = 0\n    thread_num: int = 0\n\n    has_bawu: bool = False\n    has_rule: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Forum_t:\n        forum_proto = data_proto.forum\n        fid = forum_proto.id\n        fname = forum_proto.name\n        category = forum_proto.first_class\n        subcategory = forum_proto.second_class\n        member_num = forum_proto.member_num\n        post_num = forum_proto.post_num\n        thread_num = forum_proto.thread_num\n        has_bawu = bool(forum_proto.managers)\n        has_rule = bool(data_proto.forum_rule.has_forum_rule)\n        return Forum_t(fid, fname, category, subcategory, member_num, post_num, thread_num, has_bawu, has_rule)\n\n\n@dcs.dataclass\nclass Threads(TbErrorExt, Containers[Thread]):\n    \"\"\"\n    主题帖列表\n\n    Attributes:\n        objs (list[Thread]): 主题帖列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_t): 页信息\n        has_more (bool): 是否还有下一页\n\n        forum (Forum_t): 所在吧信息\n        tab_map (dict[str, int]): 分区名到分区id的映射表\n    \"\"\"\n\n    page: Page_t = dcs.field(default_factory=Page_t)\n    forum: Forum_t = dcs.field(default_factory=Forum_t)\n    tab_map: dict[str, int] = dcs.field(default_factory=dict)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Threads:\n        page = Page_t.from_proto(data_proto.page)\n        forum = Forum_t.from_proto(data_proto)\n        tab_map = {p.tab_name: p.tab_id for p in data_proto.nav_tab_info.tab}\n\n        objs = [Thread.from_proto(p) for p in data_proto.thread_list]\n        users = {p.id: UserInfo_t.from_proto(p) for p in data_proto.user_list}\n        for thread in objs:\n            thread.fname = forum.fname\n            thread.fid = forum.fid\n            thread.user = users[thread.author_id]\n\n        return Threads(objs, page, forum, tab_map)\n\n    @staticmethod\n    def from_feed(data_proto: TypeMessage) -> Threads:\n        # 从12.65版本开始部分热门吧的主题帖列表采用feed形式推送\n        page = Page_t.from_proto(data_proto.page)\n        forum = Forum_t.from_proto(data_proto)\n        tab_map = {p.tab_name: p.tab_id for p in data_proto.nav_tab_info.tab}\n\n        objs = [Thread.from_feed(p.feed) for p in data_proto.page_data.feed_list if p.layout == \"feed\"]\n        for thread in objs:\n            thread.fname = forum.fname\n            thread.fid = forum.fid\n\n        return Threads(objs, page, forum, tab_map)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/get_threads/protobuf/FrsPageReqIdl.proto",
    "content": "// tbclient.FrsPage.FrsPageReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage FrsPageReqIdl {\n    message DataReq {\n        CommonReq common = 39;\n        string kw = 1;\n        int32 rn = 2;\n        int32 rn_need = 3;\n        int32 is_good = 4;\n        int32 cid = 5;\n        int32 pn = 15;\n        int32 sort_type = 47;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_threads/protobuf/FrsPageReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b\"\\n\\x13\\x46rsPageReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\\\"\\xc3\\x01\\n\\rFrsPageReqIdl\\x12$\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x16.FrsPageReqIdl.DataReq\\x1a\\x8b\\x01\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18' \\x01(\\x0b\\x32\\n.CommonReq\\x12\\n\\n\\x02kw\\x18\\x01 \\x01(\\t\\x12\\n\\n\\x02rn\\x18\\x02 \\x01(\\x05\\x12\\x0f\\n\\x07rn_need\\x18\\x03 \\x01(\\x05\\x12\\x0f\\n\\x07is_good\\x18\\x04 \\x01(\\x05\\x12\\x0b\\n\\x03\\x63id\\x18\\x05 \\x01(\\x05\\x12\\n\\n\\x02pn\\x18\\x0f \\x01(\\x05\\x12\\x11\\n\\tsort_type\\x18/ \\x01(\\x05\\x62\\x06proto3\"\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"FrsPageReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_FRSPAGEREQIDL\"]._serialized_start = 41\n    _globals[\"_FRSPAGEREQIDL\"]._serialized_end = 236\n    _globals[\"_FRSPAGEREQIDL_DATAREQ\"]._serialized_start = 97\n    _globals[\"_FRSPAGEREQIDL_DATAREQ\"]._serialized_end = 236\n"
  },
  {
    "path": "src/aiotieba/api/get_threads/protobuf/FrsPageResIdl.proto",
    "content": "// tbclient.FrsPage.FrsPageResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"Page.proto\";\nimport \"ThreadInfo.proto\";\nimport \"User.proto\";\nimport \"FrsTabInfo.proto\";\nimport \"PollInfo.proto\";\n\nmessage PageData {\n    message LayoutFactory {\n        string layout = 1;\n\n        message FeedLayout {\n            message ComponentFactory {\n                string component = 1;\n\n                message FeedContentResource {\n                    int32 type = 1;\n\n                    message FeedContentText {\n                        string text = 1;\n                    }\n                    FeedContentText text_info = 8;\n\n                    message FeedContentEmoji {\n                        string name = 1;\n                        string c = 2;\n                    }\n                    FeedContentEmoji emoji_info = 10;\n                }\n\n                message PicInfo {\n                    string small_pic_url = 1;\n                    string big_pic_url = 2;\n                    string origin_pic_url = 3;\n                    uint32 width = 4;\n                    uint32 height = 5;\n                }\n\n                message TitleComponent {\n                    repeated FeedContentResource data = 1;\n                }\n                TitleComponent feed_title = 3;\n\n                message AbstractComponent {\n                    repeated FeedContentResource data = 1;\n                }\n                AbstractComponent feed_abstract = 4;\n\n                message FeedPicComponent {\n                    repeated PicInfo pics = 1;\n                }\n                FeedPicComponent feed_pic = 7;\n\n                PollInfo feed_poll = 22;\n            }\n            repeated ComponentFactory components = 1;\n\n            message FeedKV {\n                string key = 1;\n                string value = 2;\n            }\n            repeated FeedKV business_info = 5;\n        }\n        FeedLayout feed = 2;\n    }\n    repeated LayoutFactory feed_list = 2;\n}\n\nmessage FrsPageResIdl {\n    Error error = 1;\n\n    message DataRes {\n        message ForumInfo {\n            int64 id = 1;\n            string name = 2;\n            string first_class = 3;\n            string second_class = 4;\n            int32 member_num = 9;\n            int32 thread_num = 10;\n            int32 post_num = 11;\n            message Manager {}\n            repeated Manager managers = 17;\n        }\n        ForumInfo forum = 2;\n\n        Page page = 4;\n        repeated ThreadInfo thread_list = 7;\n        repeated User user_list = 17;\n\n        message NavTabInfo {\n            repeated FrsTabInfo tab = 1;\n        }\n        NavTabInfo nav_tab_info = 37;\n\n        message ForumRuleStatus {\n            int32 has_forum_rule = 4;\n        }\n        ForumRuleStatus forum_rule = 105;\n\n        PageData page_data = 126;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_threads/protobuf/FrsPageResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import FrsTabInfo_pb2 as FrsTabInfo__pb2\nfrom ..._protobuf import Page_pb2 as Page__pb2\nfrom ..._protobuf import PollInfo_pb2 as PollInfo__pb2\nfrom ..._protobuf import ThreadInfo_pb2 as ThreadInfo__pb2\nfrom ..._protobuf import User_pb2 as User__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13\\x46rsPageResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nPage.proto\\x1a\\x10ThreadInfo.proto\\x1a\\nUser.proto\\x1a\\x10\\x46rsTabInfo.proto\\x1a\\x0ePollInfo.proto\"\\x8e\\x0b\\n\\x08PageData\\x12*\\n\\tfeed_list\\x18\\x02 \\x03(\\x0b\\x32\\x17.PageData.LayoutFactory\\x1a\\xd5\\n\\n\\rLayoutFactory\\x12\\x0e\\n\\x06layout\\x18\\x01 \\x01(\\t\\x12\\x30\\n\\x04\\x66\\x65\\x65\\x64\\x18\\x02 \\x01(\\x0b\\x32\".PageData.LayoutFactory.FeedLayout\\x1a\\x81\\n\\n\\nFeedLayout\\x12G\\n\\ncomponents\\x18\\x01 \\x03(\\x0b\\x32\\x33.PageData.LayoutFactory.FeedLayout.ComponentFactory\\x12@\\n\\rbusiness_info\\x18\\x05 \\x03(\\x0b\\x32).PageData.LayoutFactory.FeedLayout.FeedKV\\x1a\\xc1\\x08\\n\\x10\\x43omponentFactory\\x12\\x11\\n\\tcomponent\\x18\\x01 \\x01(\\t\\x12V\\n\\nfeed_title\\x18\\x03 \\x01(\\x0b\\x32\\x42.PageData.LayoutFactory.FeedLayout.ComponentFactory.TitleComponent\\x12\\\\\\n\\rfeed_abstract\\x18\\x04 \\x01(\\x0b\\x32\\x45.PageData.LayoutFactory.FeedLayout.ComponentFactory.AbstractComponent\\x12V\\n\\x08\\x66\\x65\\x65\\x64_pic\\x18\\x07 \\x01(\\x0b\\x32\\x44.PageData.LayoutFactory.FeedLayout.ComponentFactory.FeedPicComponent\\x12\\x1c\\n\\tfeed_poll\\x18\\x16 \\x01(\\x0b\\x32\\t.PollInfo\\x1a\\xcb\\x02\\n\\x13\\x46\\x65\\x65\\x64\\x43ontentResource\\x12\\x0c\\n\\x04type\\x18\\x01 \\x01(\\x05\\x12j\\n\\ttext_info\\x18\\x08 \\x01(\\x0b\\x32W.PageData.LayoutFactory.FeedLayout.ComponentFactory.FeedContentResource.FeedContentText\\x12l\\n\\nemoji_info\\x18\\n \\x01(\\x0b\\x32X.PageData.LayoutFactory.FeedLayout.ComponentFactory.FeedContentResource.FeedContentEmoji\\x1a\\x1f\\n\\x0f\\x46\\x65\\x65\\x64\\x43ontentText\\x12\\x0c\\n\\x04text\\x18\\x01 \\x01(\\t\\x1a+\\n\\x10\\x46\\x65\\x65\\x64\\x43ontentEmoji\\x12\\x0c\\n\\x04name\\x18\\x01 \\x01(\\t\\x12\\t\\n\\x01\\x63\\x18\\x02 \\x01(\\t\\x1al\\n\\x07PicInfo\\x12\\x15\\n\\rsmall_pic_url\\x18\\x01 \\x01(\\t\\x12\\x13\\n\\x0b\\x62ig_pic_url\\x18\\x02 \\x01(\\t\\x12\\x16\\n\\x0eorigin_pic_url\\x18\\x03 \\x01(\\t\\x12\\r\\n\\x05width\\x18\\x04 \\x01(\\r\\x12\\x0e\\n\\x06height\\x18\\x05 \\x01(\\r\\x1ag\\n\\x0eTitleComponent\\x12U\\n\\x04\\x64\\x61ta\\x18\\x01 \\x03(\\x0b\\x32G.PageData.LayoutFactory.FeedLayout.ComponentFactory.FeedContentResource\\x1aj\\n\\x11\\x41\\x62stractComponent\\x12U\\n\\x04\\x64\\x61ta\\x18\\x01 \\x03(\\x0b\\x32G.PageData.LayoutFactory.FeedLayout.ComponentFactory.FeedContentResource\\x1a]\\n\\x10\\x46\\x65\\x65\\x64PicComponent\\x12I\\n\\x04pics\\x18\\x01 \\x03(\\x0b\\x32;.PageData.LayoutFactory.FeedLayout.ComponentFactory.PicInfo\\x1a$\\n\\x06\\x46\\x65\\x65\\x64KV\\x12\\x0b\\n\\x03key\\x18\\x01 \\x01(\\t\\x12\\r\\n\\x05value\\x18\\x02 \\x01(\\t\"\\x94\\x05\\n\\rFrsPageResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12$\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x16.FrsPageResIdl.DataRes\\x1a\\xc5\\x04\\n\\x07\\x44\\x61taRes\\x12/\\n\\x05\\x66orum\\x18\\x02 \\x01(\\x0b\\x32 .FrsPageResIdl.DataRes.ForumInfo\\x12\\x13\\n\\x04page\\x18\\x04 \\x01(\\x0b\\x32\\x05.Page\\x12 \\n\\x0bthread_list\\x18\\x07 \\x03(\\x0b\\x32\\x0b.ThreadInfo\\x12\\x18\\n\\tuser_list\\x18\\x11 \\x03(\\x0b\\x32\\x05.User\\x12\\x37\\n\\x0cnav_tab_info\\x18% \\x01(\\x0b\\x32!.FrsPageResIdl.DataRes.NavTabInfo\\x12:\\n\\nforum_rule\\x18i \\x01(\\x0b\\x32&.FrsPageResIdl.DataRes.ForumRuleStatus\\x12\\x1c\\n\\tpage_data\\x18~ \\x01(\\x0b\\x32\\t.PageData\\x1a\\xd1\\x01\\n\\tForumInfo\\x12\\n\\n\\x02id\\x18\\x01 \\x01(\\x03\\x12\\x0c\\n\\x04name\\x18\\x02 \\x01(\\t\\x12\\x13\\n\\x0b\\x66irst_class\\x18\\x03 \\x01(\\t\\x12\\x14\\n\\x0csecond_class\\x18\\x04 \\x01(\\t\\x12\\x12\\n\\nmember_num\\x18\\t \\x01(\\x05\\x12\\x12\\n\\nthread_num\\x18\\n \\x01(\\x05\\x12\\x10\\n\\x08post_num\\x18\\x0b \\x01(\\x05\\x12:\\n\\x08managers\\x18\\x11 \\x03(\\x0b\\x32(.FrsPageResIdl.DataRes.ForumInfo.Manager\\x1a\\t\\n\\x07Manager\\x1a&\\n\\nNavTabInfo\\x12\\x18\\n\\x03tab\\x18\\x01 \\x03(\\x0b\\x32\\x0b.FrsTabInfo\\x1a)\\n\\x0f\\x46orumRuleStatus\\x12\\x16\\n\\x0ehas_forum_rule\\x18\\x04 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"FrsPageResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PAGEDATA\"]._serialized_start = 113\n    _globals[\"_PAGEDATA\"]._serialized_end = 1535\n    _globals[\"_PAGEDATA_LAYOUTFACTORY\"]._serialized_start = 170\n    _globals[\"_PAGEDATA_LAYOUTFACTORY\"]._serialized_end = 1535\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT\"]._serialized_start = 254\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT\"]._serialized_end = 1535\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY\"]._serialized_start = 408\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY\"]._serialized_end = 1497\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_FEEDCONTENTRESOURCE\"]._serialized_start = 748\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_FEEDCONTENTRESOURCE\"]._serialized_end = 1079\n    _globals[\n        \"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_FEEDCONTENTRESOURCE_FEEDCONTENTTEXT\"\n    ]._serialized_start = 1003\n    _globals[\n        \"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_FEEDCONTENTRESOURCE_FEEDCONTENTTEXT\"\n    ]._serialized_end = 1034\n    _globals[\n        \"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_FEEDCONTENTRESOURCE_FEEDCONTENTEMOJI\"\n    ]._serialized_start = 1036\n    _globals[\n        \"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_FEEDCONTENTRESOURCE_FEEDCONTENTEMOJI\"\n    ]._serialized_end = 1079\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_PICINFO\"]._serialized_start = 1081\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_PICINFO\"]._serialized_end = 1189\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_TITLECOMPONENT\"]._serialized_start = 1191\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_TITLECOMPONENT\"]._serialized_end = 1294\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_ABSTRACTCOMPONENT\"]._serialized_start = 1296\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_ABSTRACTCOMPONENT\"]._serialized_end = 1402\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_FEEDPICCOMPONENT\"]._serialized_start = 1404\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_COMPONENTFACTORY_FEEDPICCOMPONENT\"]._serialized_end = 1497\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_FEEDKV\"]._serialized_start = 1499\n    _globals[\"_PAGEDATA_LAYOUTFACTORY_FEEDLAYOUT_FEEDKV\"]._serialized_end = 1535\n    _globals[\"_FRSPAGERESIDL\"]._serialized_start = 1538\n    _globals[\"_FRSPAGERESIDL\"]._serialized_end = 2198\n    _globals[\"_FRSPAGERESIDL_DATARES\"]._serialized_start = 1617\n    _globals[\"_FRSPAGERESIDL_DATARES\"]._serialized_end = 2198\n    _globals[\"_FRSPAGERESIDL_DATARES_FORUMINFO\"]._serialized_start = 1906\n    _globals[\"_FRSPAGERESIDL_DATARES_FORUMINFO\"]._serialized_end = 2115\n    _globals[\"_FRSPAGERESIDL_DATARES_FORUMINFO_MANAGER\"]._serialized_start = 2106\n    _globals[\"_FRSPAGERESIDL_DATARES_FORUMINFO_MANAGER\"]._serialized_end = 2115\n    _globals[\"_FRSPAGERESIDL_DATARES_NAVTABINFO\"]._serialized_start = 2117\n    _globals[\"_FRSPAGERESIDL_DATARES_NAVTABINFO\"]._serialized_end = 2155\n    _globals[\"_FRSPAGERESIDL_DATARES_FORUMRULESTATUS\"]._serialized_start = 2157\n    _globals[\"_FRSPAGERESIDL_DATARES_FORUMRULESTATUS\"]._serialized_end = 2198\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getUserInfo_web/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import UserInfo_guinfo_web\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getUserInfo_web/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import UserInfo_guinfo_web\n\n\ndef parse_body(body: bytes) -> UserInfo_guinfo_web:\n    res_json = parse_json(body)\n    if code := res_json[\"errno\"]:\n        raise TiebaServerError(code, res_json[\"errmsg\"])\n\n    user_dict = res_json[\"chatUser\"]\n    user = UserInfo_guinfo_web.from_json(user_dict)\n\n    return user\n\n\nasync def request(http_core: HttpCore, user_id: int) -> UserInfo_guinfo_web:\n    params = [(\"chatUid\", user_id)]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"http\", host=WEB_BASE_HOST, path=\"/im/pcmsg/query/getUserInfo\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getUserInfo_web/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass UserInfo_guinfo_web(TbErrorExt):\n    \"\"\"\n    用户信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_guinfo_web:\n        user_id = data_map[\"uid\"]\n        portrait = data_map[\"portrait\"]\n        user_name = user_name if (user_name := data_map[\"uname\"]) != user_id else \"\"\n        nick_name_new = data_map[\"show_nickname\"]\n        return UserInfo_guinfo_web(user_id, portrait, user_name, nick_name_new)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_guinfo_web) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getuserinfo_app/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import UserInfo_guinfo_app\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getuserinfo_app/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import UserInfo_guinfo_app\nfrom .protobuf import GetUserInfoReqIdl_pb2, GetUserInfoResIdl_pb2\n\nCMD = 303024\n\n\ndef pack_proto(user_id: int) -> bytes:\n    req_proto = GetUserInfoReqIdl_pb2.GetUserInfoReqIdl()\n    req_proto.data.user_id = user_id\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> UserInfo_guinfo_app:\n    res_proto = GetUserInfoResIdl_pb2.GetUserInfoResIdl()\n    res_proto.ParseFromString(body)\n\n    if error_code := res_proto.error.errorno:\n        raise TiebaServerError(error_code, res_proto.error.errmsg)\n\n    user_proto = res_proto.data.user\n    user = UserInfo_guinfo_app.from_proto(user_proto)\n\n    return user\n\n\nasync def request_http(http_core: HttpCore, user_id: int) -> UserInfo_guinfo_app:\n    data = pack_proto(user_id)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/u/user/getuserinfo\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, user_id: int) -> UserInfo_guinfo_app:\n    data = pack_proto(user_id)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getuserinfo_app/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...enums import Gender\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from .._classdef import TypeMessage\n\n\n@dcs.dataclass\nclass UserInfo_guinfo_app(TbErrorExt):\n    \"\"\"\n    用户信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_old (str): 旧版昵称\n\n        gender (Gender): 性别\n\n        is_vip (bool): 是否超级会员\n        is_god (bool): 是否大神\n\n        nick_name (str): 用户昵称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_old: str = \"\"\n\n    gender: Gender = Gender.UNKNOWN\n\n    is_vip: bool = False\n    is_god: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_guinfo_app:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_old = data_proto.name_show\n        gender = Gender(data_proto.sex)\n        is_vip = bool(data_proto.vipInfo.v_status)\n        is_god = bool(data_proto.new_god_data.status)\n        return UserInfo_guinfo_app(user_id, portrait, user_name, nick_name_old, gender, is_vip, is_god)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_guinfo_app) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_old\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_old}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getuserinfo_app/protobuf/GetUserInfoReqIdl.proto",
    "content": "// tbclient.GetUserInfo.GetUserInfoReqIdl\nsyntax = \"proto3\";\n\nmessage GetUserInfoReqIdl {\n    message DataReq {\n        int64 user_id = 2;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getuserinfo_app/protobuf/GetUserInfoReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x17GetUserInfoReqIdl.proto\"Y\\n\\x11GetUserInfoReqIdl\\x12(\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1a.GetUserInfoReqIdl.DataReq\\x1a\\x1a\\n\\x07\\x44\\x61taReq\\x12\\x0f\\n\\x07user_id\\x18\\x02 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetUserInfoReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETUSERINFOREQIDL\"]._serialized_start = 27\n    _globals[\"_GETUSERINFOREQIDL\"]._serialized_end = 116\n    _globals[\"_GETUSERINFOREQIDL_DATAREQ\"]._serialized_start = 90\n    _globals[\"_GETUSERINFOREQIDL_DATAREQ\"]._serialized_end = 116\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getuserinfo_app/protobuf/GetUserInfoResIdl.proto",
    "content": "// tbclient.GetUserInfo.GetUserInfoResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"User.proto\";\n\nmessage GetUserInfoResIdl {\n    Error error = 1;\n    message DataRes {\n        User user = 1;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_getuserinfo_app/protobuf/GetUserInfoResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import User_pb2 as User__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x17GetUserInfoResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nUser.proto\"t\\n\\x11GetUserInfoResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12(\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1a.GetUserInfoResIdl.DataRes\\x1a\\x1e\\n\\x07\\x44\\x61taRes\\x12\\x13\\n\\x04user\\x18\\x01 \\x01(\\x0b\\x32\\x05.Userb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetUserInfoResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETUSERINFORESIDL\"]._serialized_start = 52\n    _globals[\"_GETUSERINFORESIDL\"]._serialized_end = 168\n    _globals[\"_GETUSERINFORESIDL_DATARES\"]._serialized_start = 138\n    _globals[\"_GETUSERINFORESIDL_DATARES\"]._serialized_end = 168\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_panel/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import UserInfo_panel\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_panel/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import is_portrait, parse_json\nfrom ._classdef import UserInfo_panel\n\n\ndef parse_body(body: bytes) -> UserInfo_panel:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n    data_map = res_json[\"data\"]\n    user = UserInfo_panel.from_json(data_map)\n\n    return user\n\n\nasync def request(http_core: HttpCore, name_or_portrait: str) -> UserInfo_panel:\n    if is_portrait(name_or_portrait):\n        params = [(\"id\", name_or_portrait)]\n    else:\n        params = [(\"un\", name_or_portrait)]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"http\", host=WEB_BASE_HOST, path=\"/home/get/panel\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_panel/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...enums import Gender\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\ndef _tbnum2int(tb_num: str) -> int:\n    if isinstance(tb_num, str):\n        return int(float(tb_num.removesuffix(\"万\")) * 1e4)\n    else:\n        return tb_num\n\n\n@dcs.dataclass\nclass UserInfo_panel(TbErrorExt):\n    \"\"\"\n    用户信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n        nick_name_old (str): 旧版昵称\n\n        gender (Gender): 性别\n        age (float): 吧龄\n        post_num (int): 发帖数\n        fan_num (int): 粉丝数\n\n        is_vip (bool): 是否超级会员\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n    nick_name_old: str = \"\"\n\n    gender: Gender = Gender.UNKNOWN\n    age: int = 0.0\n    post_num: int = 0\n    fan_num: int = 0\n\n    is_vip: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_panel:\n        portrait = data_map[\"portrait\"]\n        user_name = data_map[\"name\"]\n        nick_name_new = data_map[\"show_nickname\"]\n        nick_name_old = data_map[\"name_show\"]\n\n        sex = data_map[\"sex\"]\n        if sex == \"male\":\n            gender = Gender.MALE\n        elif sex == \"female\":\n            gender = Gender.FEMALE\n        else:\n            gender = Gender.UNKNOWN\n\n        if (tb_age := data_map[\"tb_age\"]) != \"-\":\n            age = float(tb_age)\n        else:\n            age = 0.0\n\n        post_num = _tbnum2int(data_map[\"post_num\"])\n        fan_num = _tbnum2int(data_map[\"followed_count\"])\n\n        if vip_dict := data_map[\"vipInfo\"]:\n            is_vip = int(vip_dict[\"v_status\"]) == 3\n        else:\n            is_vip = False\n\n        return UserInfo_panel(portrait, user_name, nick_name_new, nick_name_old, gender, age, post_num, fan_num, is_vip)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait\n\n    def __eq__(self, obj: UserInfo_panel) -> bool:\n        return self.portrait == obj.portrait\n\n    def __hash__(self) -> int:\n        return hash(self.portrait)\n\n    def __bool__(self) -> bool:\n        return hash(self.portrait)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        return self.user_name or f\"{self.nick_name_new}/{self.portrait}\"\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_user_json/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import UserInfo_json\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_user_json/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaValueError\nfrom ...helper import parse_json\nfrom ._classdef import UserInfo_json\n\n\ndef parse_body(body: bytes) -> UserInfo_json:\n    if not body:\n        raise TiebaValueError(\"Empty body\")\n\n    text = body.decode(\"utf-8\", errors=\"ignore\")\n    res_json = parse_json(text)\n\n    user_dict = res_json[\"creator\"]\n    user = UserInfo_json.from_json(user_dict)\n\n    return user\n\n\nasync def request(http_core: HttpCore, user_name: str) -> UserInfo_json:\n    params = [\n        (\"un\", user_name),\n        (\"ie\", \"utf-8\"),\n    ]\n\n    request = http_core.pack_web_get_request(\n        yarl.URL.build(scheme=\"http\", host=WEB_BASE_HOST, path=\"/i/sys/user_json\"), params\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_uinfo_user_json/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass UserInfo_json(TbErrorExt):\n    \"\"\"\n    用户信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_json:\n        user_id = data_map[\"id\"]\n        portrait = data_map[\"portrait\"]\n        user_name = \"\"\n        return UserInfo_json(user_id, portrait, user_name)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_json) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def log_name(self) -> str:\n        return str(self)\n"
  },
  {
    "path": "src/aiotieba/api/get_unblock_appeals/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import Appeal, Appeals\n"
  },
  {
    "path": "src/aiotieba/api/get_unblock_appeals/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import Appeals\n\n\ndef parse_body(body: bytes) -> Appeals:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n    appeals = Appeals.from_json(res_json)\n\n    return appeals\n\n\nasync def request(http_core: HttpCore, fid: int, pn: int, rn: int) -> Appeals:\n    data = [\n        (\"fn\", \"-\"),\n        (\"fid\", fid),\n        (\"pn\", pn),\n        (\"rn\", rn),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/getBawuAppealList\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=32 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_unblock_appeals/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass Appeal:\n    \"\"\"\n    申诉请求信息\n\n    Attributes:\n        user_id (int): 申诉用户id\n        portrait (str): 申诉用户portrait\n        user_name (str): 申诉用户名\n        nick_name (str): 申诉用户昵称\n\n        appeal_id (int): 申诉id\n        appeal_reason (str): 申诉理由\n        appeal_time (int): 申诉时间 10位时间戳 以秒为单位\n\n        punish_reason (str): 封禁理由\n        punish_time (int): 封禁开始时间 10位时间戳 以秒为单位\n        punish_day (int): 封禁天数\n        op_name (str): 操作人用户名\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name: str = \"\"\n\n    appeal_id: int = 0\n    appeal_reason: str = \"\"\n    appeal_time: int = 0\n\n    punish_reason: str = \"\"\n    punish_time: int = 0\n    punish_day: int = 0\n    op_name: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Appeal:\n        user_map = data_map[\"user\"]\n        user_id = user_map[\"id\"]\n        portrait = user_map[\"portrait\"]\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = user_map[\"name\"]\n        nick_name = user_map[\"name_show\"]\n        appeal_id = int(data_map[\"appeal_id\"])\n        appeal_reason = data_map[\"appeal_reason\"]\n        appeal_time = int(data_map[\"appeal_time\"])\n        punish_reason = data_map[\"punish_reason\"]\n        punish_time = int(data_map[\"punish_start_time\"])\n        punish_day = data_map[\"punish_day_num\"]\n        op_name = data_map[\"operate_man\"]\n        return Appeal(\n            user_id,\n            portrait,\n            user_name,\n            nick_name,\n            appeal_id,\n            appeal_reason,\n            appeal_time,\n            punish_reason,\n            punish_time,\n            punish_day,\n            op_name,\n        )\n\n\n@dcs.dataclass\nclass Appeals(TbErrorExt, Containers[Appeal]):\n    \"\"\"\n    申诉请求列表\n\n    Attributes:\n        objs (list[Appeal]): 申诉请求列表\n        err (Exception | None): 捕获的异常\n\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    has_more: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Appeals:\n        objs = [Appeal.from_json(m) for m in data_map[\"data\"].get(\"appeal_list\", [])]\n        has_more = data_map[\"data\"].get(\"has_more\", False)\n        return Appeals(objs, has_more)\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/__init__.py",
    "content": "from . import get_posts, get_posts_form, get_threads\nfrom ._classdef import UserPost, UserPosts, UserPostss, UserThread, UserThreads\nfrom ._const import CMD\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\nfrom typing import TYPE_CHECKING\n\nfrom ...enums import ThreadType\nfrom ...exception import TbErrorExt\nfrom ...helper import deprecated\nfrom ...logging import get_logger as LOG\nfrom .._classdef import Containers, TypeMessage, VoteInfo\nfrom .._classdef.contents import (\n    _IMAGEHASH_EXP,\n    FragAt,\n    FragEmoji,\n    FragLink,\n    FragText,\n    FragUnknown,\n    FragVideo,\n    FragVoice,\n    TypeFragment,\n    TypeFragText,\n)\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\nFragText_up = FragText_ut = FragText\nFragEmoji_ut = FragEmoji\nFragAt_ut = FragAt\nFragLink_up = FragLink_ut = FragLink\nFragVideo_ut = FragVideo\nFragVoice_ut = FragVoice\n\n\n@dcs.dataclass\nclass FragVoice_up:\n    \"\"\"\n    音频碎片\n\n    Attributes:\n        md5 (str): 音频md5\n        duration (int): 音频长度 以秒为单位\n    \"\"\"\n\n    md5: str = \"\"\n    duration: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragVoice_up:\n        md5 = data_proto.voice_md5\n        duration = int(data_proto.during_time) / 1000\n        return FragVoice_up(md5, duration)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> FragVoice_up:\n        md5 = data_map[\"voice_md5\"]\n        duration = int(data_map[\"during_time\"]) / 1000\n        return FragVoice_up(md5, duration)\n\n    def __bool__(self) -> bool:\n        return bool(self.md5)\n\n\n@dcs.dataclass\nclass Contents_up(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        links (list[FragLink_up]): 链接碎片列表\n        voice (FragVoice_up): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_up] = dcs.field(default_factory=list, repr=False)\n    voice: FragVoice_up = dcs.field(default_factory=FragVoice_up, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_up:\n        content_protos = data_proto.post_content\n\n        texts = []\n        links = []\n        voice = FragVoice_up()\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                if _type in [0, 4]:\n                    frag = FragText_up.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_up.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 10:  # voice\n                    nonlocal voice\n                    voice = FragVoice_up.from_proto(proto)\n                    continue\n                else:\n                    yield FragUnknown.from_proto(frag)\n\n        objs = list(_frags())\n\n        return Contents_up(objs, texts, links, voice)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Contents_up:\n        content_maps = data_map[\"post_content\"]\n\n        texts = []\n        links = []\n        voice = FragVoice_up()\n\n        def _frags():\n            for content_map in content_maps:\n                _type = int(content_map[\"type\"])\n                if _type in [0, 4]:\n                    frag = FragText_up.from_json(content_map)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_up.from_json(content_map)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 10:  # voice\n                    nonlocal voice\n                    voice = FragVoice_up.from_json(content_map)\n                    continue\n                else:\n                    yield FragUnknown.from_json(content_map)\n\n        objs = list(_frags())\n\n        return Contents_up(objs, texts, links, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass UserInfo_u:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_u:\n        user_id = data_proto.user_id\n        portrait = data_proto.user_portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.user_name\n        nick_name_new = data_proto.name_show\n        return UserInfo_u(user_id, portrait, user_name, nick_name_new)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_u:\n        user_id = int(data_map[\"user_id\"])\n        portrait = data_map[\"user_portrait\"]\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_map[\"user_name\"]\n        nick_name_new = data_map[\"name_show\"]\n        return UserInfo_u(user_id, portrait, user_name, nick_name_new)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_u) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass UserPost:\n    \"\"\"\n    用户历史回复信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_up): 正文内容碎片列表\n\n        fid (int): 所在吧id\n        tid (int): 所在主题帖id\n        pid (int): 回复id\n        user (UserInfo_u): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        is_comment (bool): 是否为楼中楼\n\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    contents: Contents_up = dcs.field(default_factory=Contents_up)\n\n    fid: int = 0\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_u = dcs.field(default_factory=UserInfo_u)\n\n    is_comment: bool = False\n\n    create_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserPost:\n        contents = Contents_up.from_proto(data_proto)\n        pid = data_proto.post_id\n        is_comment = bool(data_proto.post_type)\n        create_time = data_proto.create_time\n        return UserPost(contents, 0, 0, pid, None, is_comment, create_time)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserPost:\n        contents = Contents_up.from_json(data_map)\n        pid = int(data_map[\"post_id\"])\n        is_comment = bool(int(data_map[\"post_type\"]))\n        create_time = int(data_map[\"create_time\"])\n        return UserPost(contents, 0, 0, pid, None, is_comment, create_time)\n\n    def __eq__(self, obj: UserPost) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @property\n    def text(self) -> str:\n        return self.contents.text\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n\n@dcs.dataclass\nclass UserPosts(Containers[UserPost]):\n    \"\"\"\n    用户历史回复信息列表\n\n    Attributes:\n        objs (list[UserPost]): 用户历史回复信息列表\n\n        fid (int): 所在吧id\n        tid (int): 所在主题帖id\n    \"\"\"\n\n    fid: int = 0\n    tid: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserPosts:\n        fid = data_proto.forum_id\n        tid = data_proto.thread_id\n        objs = [UserPost.from_proto(p) for p in data_proto.content]\n        for upost in objs:\n            upost.fid = fid\n            upost.tid = tid\n        return UserPosts(objs, fid, tid)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserPosts:\n        fid = int(data_map[\"forum_id\"])\n        tid = int(data_map[\"thread_id\"])\n        objs = [UserPost.from_json(m) for m in data_map[\"content\"]]\n        for upost in objs:\n            upost.fid = fid\n            upost.tid = tid\n        return UserPosts(objs, fid, tid)\n\n\n@dcs.dataclass\nclass UserPostss(TbErrorExt, Containers[UserPosts]):\n    \"\"\"\n    用户历史回复信息列表的列表\n\n    Attributes:\n        objs (list[UserPosts]): 用户历史回复信息列表的列表\n        err (Exception | None): 捕获的异常\n    \"\"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserPostss:\n        objs = [UserPosts.from_proto(p) for p in data_proto.post_list]\n        if objs:\n            user = UserInfo_u.from_proto(data_proto.post_list[0])\n            for uposts in objs:\n                for upost in uposts:\n                    upost.user = user\n        return UserPostss(objs)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserPostss:\n        objs = [UserPosts.from_json(m) for m in data_map[\"post_list\"]]\n        if objs:\n            user = UserInfo_u.from_json(data_map[\"post_list\"][0])\n            for uposts in objs:\n                for upost in uposts:\n                    upost.user = user\n        return UserPostss(objs)\n\n\n@dcs.dataclass\nclass FragImage_ut:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 小图链接 宽580px 一定是静态图\n        big_src (str): 大图链接 宽960px\n        origin_src (str): 原图链接\n        origin_size (int): 原图大小\n        width (int): 图像宽度\n        height (int): 图像高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    big_src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    origin_size: int = 0\n    width: int = 0\n    height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragImage_ut:\n        src = data_proto.small_pic\n        big_src = data_proto.big_pic\n        origin_src = data_proto.origin_pic\n        origin_size = data_proto.origin_size\n\n        width = data_proto.width\n        height = data_proto.height\n\n        hash_ = _IMAGEHASH_EXP.search(src).group(1)\n\n        return FragImage_ut(src, big_src, origin_src, origin_size, width, height, hash_)\n\n\n@dcs.dataclass\nclass Contents_ut(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_ut]): 表情碎片列表\n        imgs (list[FragImage_ut]): 图像碎片列表\n        ats (list[FragAt_ut]): @碎片列表\n        links (list[FragLink_ut]): 链接碎片列表\n        video (FragVideo_ut): 视频碎片\n        voice (FragVoice_ut): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_ut] = dcs.field(default_factory=list, repr=False)\n    imgs: list[FragImage_ut] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_ut] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_ut] = dcs.field(default_factory=list, repr=False)\n    video: FragVideo_ut = dcs.field(default_factory=FragVideo_ut, repr=False)\n    voice: FragVoice_ut = dcs.field(default_factory=FragVoice_ut, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_ut:\n        content_protos = data_proto.first_post_content\n\n        texts = []\n        emojis = []\n        imgs = [FragImage_ut.from_proto(p) for p in data_proto.media if p.type != 5]\n        ats = []\n        links = []\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条\n                if _type in [0, 9, 18, 27]:\n                    frag = FragText_ut.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_ut.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                # img will be init elsewhere\n                elif _type in [3, 20]:\n                    continue\n                elif _type == 4:\n                    frag = FragAt_ut.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_ut.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 5:  # video\n                    continue\n                elif _type == 10:  # voice\n                    continue\n                else:\n                    yield FragUnknown.from_proto(frag)\n\n        objs = list(_frags())\n        objs += imgs\n\n        if data_proto.video_info.video_width:\n            video = FragVideo_ut.from_proto(data_proto.video_info)\n            objs.append(video)\n        else:\n            video = FragVideo_ut()\n\n        if data_proto.voice_info:\n            voice = FragVoice_ut.from_proto(data_proto.voice_info[0])\n            objs.append(voice)\n        else:\n            voice = FragVoice_ut()\n\n        return Contents_ut(objs, texts, emojis, imgs, ats, links, video, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass UserThread:\n    \"\"\"\n    主题帖信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_ut): 正文内容碎片列表\n        title (str): 标题内容\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 主题帖tid\n        pid (int): 首楼回复pid\n        user (UserInfo_u): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        type (ThreadType): 帖子类型\n        is_help (bool): 是否为求助帖\n\n        vote_info (VoteInfo): 投票信息\n        view_num (int): 浏览量\n        reply_num (int): 回复数\n        share_num (int): 分享数\n        agree (int): 点赞数\n        disagree (int): 点踩数\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    contents: Contents_ut = dcs.field(default_factory=Contents_ut)\n    title: str = \"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_u = dcs.field(default_factory=UserInfo_u)\n\n    type: ThreadType = ThreadType.UNKNOWN\n\n    vote_info: VoteInfo = dcs.field(default_factory=VoteInfo)\n    view_num: int = 0\n    reply_num: int = 0\n    share_num: int = 0\n    agree: int = 0\n    disagree: int = 0\n    create_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserThread:\n        contents = Contents_ut.from_proto(data_proto)\n        title = data_proto.title\n        fid = data_proto.forum_id\n        fname = data_proto.forum_name\n        tid = data_proto.thread_id\n        pid = data_proto.post_id\n\n        type_ = ThreadType(data_proto.thread_type)\n        if type_ == ThreadType.UNKNOWN:\n            LOG().debug(\"Unknown thread type. tid=%d, type=%s\", tid, data_proto.thread_type)\n\n        vote_info = VoteInfo.from_proto(data_proto.poll_info)\n        view_num = data_proto.freq_num\n        reply_num = data_proto.reply_num\n        share_num = data_proto.share_num\n        agree = data_proto.agree.agree_num\n        disagree = data_proto.agree.disagree_num\n        create_time = data_proto.create_time\n        return UserThread(\n            contents,\n            title,\n            fid,\n            fname,\n            tid,\n            pid,\n            None,\n            type_,\n            vote_info,\n            view_num,\n            reply_num,\n            share_num,\n            agree,\n            disagree,\n            create_time,\n        )\n\n    def __eq__(self, obj: UserThread) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @cached_property\n    def text(self) -> str:\n        if self.title:\n            text = f\"{self.title}\\n{self.contents.text}\"\n        else:\n            text = self.contents.text\n        return text\n\n    @property\n    @deprecated(\"使用 thread.type == ThreadType.HELP 作为替代\")\n    def is_help(self) -> bool:\n        return self.type == ThreadType.HELP\n\n\n@dcs.dataclass\nclass UserThreads(TbErrorExt, Containers[UserThread]):\n    \"\"\"\n    用户发布主题帖列表\n\n    Attributes:\n        objs (list[UserThread]): 用户发布主题帖列表\n        err (Exception | None): 捕获的异常\n    \"\"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserThreads:\n        objs = [UserThread.from_proto(p) for p in data_proto.post_list]\n        if objs:\n            user = UserInfo_u.from_proto(data_proto.post_list[0])\n            for uthread in objs:\n                uthread.user = user\n        return UserThreads(objs)\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/_const.py",
    "content": "CMD = 303002\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/get_posts/__init__.py",
    "content": "from ._api import pack_proto, parse_body, request_http, request_ws\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/get_posts/_api.py",
    "content": "import yarl\n\nfrom ....const import APP_BASE_HOST\nfrom ....core import Account, HttpCore, WsCore\nfrom ....exception import TiebaServerError\nfrom .._classdef import UserPostss\nfrom .._const import CMD\nfrom ..protobuf import UserPostReqIdl_pb2, UserPostResIdl_pb2\n\n\ndef pack_proto(account: Account, user_id: int, pn: int, rn: int, version: str) -> bytes:\n    req_proto = UserPostReqIdl_pb2.UserPostReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.common._client_version = version\n    req_proto.data.uid = user_id\n    req_proto.data.need_content = 1\n    req_proto.data.pn = pn\n    req_proto.data.rn = rn\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> UserPostss:\n    res_proto = UserPostResIdl_pb2.UserPostResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    upostss = UserPostss.from_proto(data_proto)\n\n    return upostss\n\n\nasync def request_http(http_core: HttpCore, user_id: int, pn: int, rn: int, version: str) -> UserPostss:\n    data = pack_proto(http_core.account, user_id, pn, rn, version)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/feed/userpost\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=64 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, user_id: int, pn: int, rn: int, version: str) -> UserPostss:\n    data = pack_proto(ws_core.account, user_id, pn, rn, version)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/get_posts_form/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/get_posts_form/_api.py",
    "content": "import yarl\n\nfrom ....const import LATEST_VERSION, WEB_BASE_HOST\nfrom ....core import HttpCore\nfrom ....exception import TiebaServerError\nfrom ....helper import parse_json\nfrom .._classdef import UserPostss\n\n\ndef parse_body(body: bytes) -> UserPostss:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    upostss = UserPostss.from_json(res_json)\n\n    return upostss\n\n\nasync def request(http_core: HttpCore, user_id: int, pn: int, rn: int) -> UserPostss:\n    data = [\n        (\"_client_version\", LATEST_VERSION),\n        (\"res_num\", rn),\n        (\"is_thread\", 1),\n        (\"need_content\", 1),\n        (\"is_view_card\", 1),\n        # (\"forum_id\", 0),\n        (\"uid\", user_id),\n        (\"pn\", pn),\n        (\"subapp_type\", \"hybrid\"),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/c/u/feed/userpost\"),\n        data,\n        extra_headers=[(\"Subapp-Type\", \"hybrid\")],\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=64 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/get_threads/__init__.py",
    "content": "from ._api import pack_proto, parse_body, request_http, request_ws\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/get_threads/_api.py",
    "content": "import yarl\n\nfrom ....const import APP_BASE_HOST, LATEST_VERSION\nfrom ....core import HttpCore, WsCore\nfrom ....exception import TiebaServerError\nfrom .._classdef import UserThreads\nfrom .._const import CMD\nfrom ..protobuf import UserPostReqIdl_pb2, UserPostResIdl_pb2\n\n\ndef pack_proto(user_id: int, pn: int, public_only: bool) -> bytes:\n    req_proto = UserPostReqIdl_pb2.UserPostReqIdl()\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.uid = user_id\n    req_proto.data.is_thread = 1\n    req_proto.data.need_content = 1\n    req_proto.data.pn = pn\n    req_proto.data.is_view_card = 2 if public_only else 1\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> UserThreads:\n    res_proto = UserPostResIdl_pb2.UserPostResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    uthreads = UserThreads.from_proto(data_proto)\n\n    return uthreads\n\n\nasync def request_http(http_core: HttpCore, user_id: int, pn: int, public_only: bool) -> UserThreads:\n    data = pack_proto(user_id, pn, public_only)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/u/feed/userpost\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=64 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, user_id: int, pn: int, public_only: bool) -> UserThreads:\n    data = pack_proto(user_id, pn, public_only)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/protobuf/UserPostReqIdl.proto",
    "content": "// tbclient.UserPost.UserPostReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage UserPostReqIdl {\n    message DataReq {\n        int64 uid = 1;\n        uint32 rn = 2;\n        uint32 is_thread = 4;\n        uint32 need_content = 5;\n        uint32 pn = 26;\n        int32 is_view_card = 33;\n        CommonReq common = 27;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/protobuf/UserPostReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x14UserPostReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\xc3\\x01\\n\\x0eUserPostReqIdl\\x12%\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x17.UserPostReqIdl.DataReq\\x1a\\x89\\x01\\n\\x07\\x44\\x61taReq\\x12\\x0b\\n\\x03uid\\x18\\x01 \\x01(\\x03\\x12\\n\\n\\x02rn\\x18\\x02 \\x01(\\r\\x12\\x11\\n\\tis_thread\\x18\\x04 \\x01(\\r\\x12\\x14\\n\\x0cneed_content\\x18\\x05 \\x01(\\r\\x12\\n\\n\\x02pn\\x18\\x1a \\x01(\\r\\x12\\x14\\n\\x0cis_view_card\\x18! \\x01(\\x05\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x1b \\x01(\\x0b\\x32\\n.CommonReqb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"UserPostReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_USERPOSTREQIDL\"]._serialized_start = 42\n    _globals[\"_USERPOSTREQIDL\"]._serialized_end = 237\n    _globals[\"_USERPOSTREQIDL_DATAREQ\"]._serialized_start = 100\n    _globals[\"_USERPOSTREQIDL_DATAREQ\"]._serialized_end = 237\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/protobuf/UserPostResIdl.proto",
    "content": "// tbclient.UserPost.UserPostResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"PostInfoList.proto\";\n\nmessage UserPostResIdl {\n    Error error = 1;\n    message DataRes {\n        repeated PostInfoList post_list = 1;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/get_user_contents/protobuf/UserPostResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import PostInfoList_pb2 as PostInfoList__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x14UserPostResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\x12PostInfoList.proto\"{\\n\\x0eUserPostResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12%\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x17.UserPostResIdl.DataRes\\x1a+\\n\\x07\\x44\\x61taRes\\x12 \\n\\tpost_list\\x18\\x01 \\x03(\\x0b\\x32\\r.PostInfoListb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"UserPostResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_USERPOSTRESIDL\"]._serialized_start = 57\n    _globals[\"_USERPOSTRESIDL\"]._serialized_end = 180\n    _globals[\"_USERPOSTRESIDL_DATARES\"]._serialized_start = 137\n    _globals[\"_USERPOSTRESIDL_DATARES\"]._serialized_end = 180\n"
  },
  {
    "path": "src/aiotieba/api/get_user_forum_info/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import UserForumInfo, UserInfo_uf\n"
  },
  {
    "path": "src/aiotieba/api/get_user_forum_info/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import UserForumInfo\n\n\ndef parse_body(body: bytes) -> UserForumInfo:\n    res_json = parse_json(body)\n    if code := int(res_json.get(\"error_code\", 0) or 0):\n        err_msg = res_json.get(\"error_msg\") or res_json.get(\"error\") or res_json.get(\"errmsg\") or \"\"\n        raise TiebaServerError(code, err_msg)\n\n    data_map = res_json.get(\"data\", {})\n    return UserForumInfo.from_json(data_map)\n\n\nasync def request(http_core: HttpCore, forum_id: int, friend_portrait: str) -> UserForumInfo:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"forum_id\", forum_id),\n        (\"friend_portrait\", friend_portrait),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/f/forum/getUserForumLevelInfo\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=4 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/get_user_forum_info/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass UserInfo_uf:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        show_name (str): 显示名称\n        is_like (bool): 是否已关注该用户\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    show_name: str = \"\"\n    is_like: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_uf:\n        show_name = data_map.get(\"name\", \"\")\n        user_id = int(data_map.get(\"id\", 0) or 0)\n        portrait = data_map.get(\"portrait\", \"\")\n        if \"?\" in portrait:\n            portrait = portrait.split(\"?\", 1)[0]\n        is_like = bool(int(data_map.get(\"is_like\", 0) or 0))\n        return UserInfo_uf(user_id, portrait, show_name, is_like)\n\n    def __str__(self) -> str:\n        return self.show_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: object) -> bool:\n        return isinstance(obj, UserInfo_uf) and self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n\n@dcs.dataclass\nclass UserForumInfo(TbErrorExt):\n    \"\"\"\n    用户在吧内的信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        user (UserInfo_uf): 用户信息\n\n        fname (str): 贴吧名\n        small_avatar (str): 吧头像(小)\n\n        is_follow (bool): 是否已关注该吧\n        follow_days (int): 关注天数\n        sign_days (int): 签到天数\n        thread_num (int): 本吧发帖数\n        day_post_num (int): 今日发帖数\n        member_rank (int): 吧内排名\n        day_sign_rank (int): 今日签到排名\n        level (int): 等级\n        level_name (str): 本吧头衔名称\n        exp (int): 当前经验\n        levelup_exp (int): 升级经验\n        role_name (str): 吧务名称\n        identify (str): 身份标识\n        high_light_sign_days (int): 连续签到天数\n    \"\"\"\n\n    user: UserInfo_uf = dcs.field(default_factory=UserInfo_uf)\n\n    fname: str = \"\"\n    small_avatar: str = \"\"\n\n    is_follow: bool = False\n    follow_days: int = 0\n    sign_days: int = 0\n    thread_num: int = 0\n    day_post_num: int = 0\n    member_rank: int = 0\n    day_sign_rank: int = 0\n    level: int = 0\n    level_name: str = \"\"\n    exp: int = 0\n    levelup_exp: int = 0\n    role_name: str = \"\"\n    identify: str = \"\"\n    high_light_sign_days: int = 0\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserForumInfo:\n        user = UserInfo_uf.from_json(data_map.get(\"user_info\", {}))\n        user_forum = data_map.get(\"user_forum_info\", {})\n        forum = data_map.get(\"forum_info\", {})\n        return UserForumInfo(\n            user=user,\n            is_follow=bool(int(user_forum.get(\"is_follow\", 0) or 0)),\n            follow_days=int(user_forum.get(\"follow_days\", 0) or 0),\n            sign_days=int(user_forum.get(\"sign_days\", 0) or 0),\n            thread_num=int(user_forum.get(\"thread_num\", 0) or 0),\n            day_post_num=int(user_forum.get(\"day_post_num\", 0) or 0),\n            member_rank=int(user_forum.get(\"member_no\", 0) or 0),\n            day_sign_rank=int(user_forum.get(\"day_sign_no\", 0) or 0),\n            level=int(user_forum.get(\"level_id\", 0) or 0),\n            level_name=user_forum.get(\"level_name\", \"\"),\n            exp=int(user_forum.get(\"cur_score\", 0) or 0),\n            levelup_exp=int(user_forum.get(\"levelup_score\", 0) or 0),\n            role_name=user_forum.get(\"role_name\", \"\"),\n            identify=user_forum.get(\"identify\", \"\"),\n            high_light_sign_days=int(user_forum.get(\"high_light_sign_days\", 0) or 0),\n            fname=forum.get(\"forum_name\", \"\"),\n            small_avatar=forum.get(\"forum_avatar\", \"\"),\n        )\n"
  },
  {
    "path": "src/aiotieba/api/good/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/good/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fname: str, fid: int, tid: int, cid: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"cid\", cid),\n        (\"fid\", fid),\n        (\"ntn\", \"set\"),\n        (\"tbs\", http_core.account.tbs),\n        (\"word\", fname),\n        (\"z\", tid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/commitgood\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/handle_unblock_appeals/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/handle_unblock_appeals/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\nif TYPE_CHECKING:\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n\nasync def request(http_core: HttpCore, fid: int, appeal_ids: list[int], refuse: bool) -> BoolResponse:\n    data = (\n        [\n            (\"fn\", \"-\"),\n            (\"fid\", fid),\n        ]\n        + [(f\"appeal_list[{i}]\", appeal_id) for i, appeal_id in enumerate(appeal_ids)]\n        + [\n            (\"refuse_reason\", \"_\"),\n            (\"status\", 2 if refuse else 1),\n            (\"tbs\", http_core.account.tbs),\n        ]\n    )\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/multiAppealhandle\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/init_websocket/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request\nfrom ._classdef import WsMsgGroupInfo\n"
  },
  {
    "path": "src/aiotieba/api/init_websocket/_api.py",
    "content": "from __future__ import annotations\n\nimport binascii\nimport time\nfrom typing import TYPE_CHECKING\n\nfrom cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15\nfrom cryptography.hazmat.primitives.serialization import load_der_public_key\n\nfrom ...const import STABLE_VERSION\nfrom ...exception import TiebaServerError\nfrom ...helper import pack_json\nfrom ._classdef import WsMsgGroupInfo\nfrom .protobuf import UpdateClientInfoReqIdl_pb2, UpdateClientInfoResIdl_pb2\n\nif TYPE_CHECKING:\n    from ...core import Account, WsCore\n\nCMD = 1001\n\n\nPUBLIC_KEY = binascii.a2b_base64(\n    b\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwQpwBZxXJV/JVRF/uNfyMSdu7YWwRNLM8+2xbniGp2iIQHOikPpTYQjlQgMi1uvq1kZpJ32rHo3hkwjy2l0lFwr3u4Hk2Wk7vnsqYQjAlYlK0TCzjpmiI+OiPOUNVtbWHQiLiVqFtzvpvi4AU7C1iKGvc/4IS45WjHxeScHhnZZ7njS4S1UgNP/GflRIbzgbBhyZ9kEW5/OO5YfG1fy6r4KSlDJw4o/mw5XhftyIpL+5ZBVBC6E1EIiP/dd9AbK62VV1PByfPMHMixpxI3GM2qwcmFsXcCcgvUXJBa9k6zP8dDQ3csCM2QNT+CQAOxthjtp/TFWaD7MzOdsIYb3THwIDAQAB\"\n)\n\n\ndef pack_proto(account: Account) -> bytes:\n    req_proto = UpdateClientInfoReqIdl_pb2.UpdateClientInfoReqIdl()\n    req_proto.data.bduss = account.BDUSS\n\n    device = {\n        \"cuid\": account.cuid,\n        \"_client_version\": STABLE_VERSION,\n        \"_msg_status\": \"1\",\n        \"cuid_galaxy2\": account.cuid_galaxy2,\n        \"_client_type\": \"2\",\n        \"timestamp\": str(int(time.time() * 1000)),\n    }\n    req_proto.data.device = pack_json(device)\n\n    rsa_chiper = load_der_public_key(PUBLIC_KEY)\n    secret_key = rsa_chiper.encrypt(account.aes_ecb_sec_key, PKCS1v15())\n    req_proto.data.secretKey = secret_key\n    req_proto.data.stoken = account.STOKEN\n    req_proto.cuid = f\"{account.cuid}|com.baidu.tieba{STABLE_VERSION}\"\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> list[WsMsgGroupInfo]:\n    res_proto = UpdateClientInfoResIdl_pb2.UpdateClientInfoResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    groups = [WsMsgGroupInfo.from_proto(p) for p in res_proto.data.groupInfo]\n\n    return groups\n\n\nasync def request(ws_core: WsCore) -> list[WsMsgGroupInfo]:\n    data = pack_proto(ws_core.account)\n\n    resp = await ws_core.send(data, CMD, compress=False, encrypt=False)\n    groups = parse_body(await resp.read())\n\n    return groups\n"
  },
  {
    "path": "src/aiotieba/api/init_websocket/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from .._classdef import TypeMessage\n\n\n@dcs.dataclass\nclass WsMsgGroupInfo:\n    \"\"\"\n    websocket消息组的相关信息\n\n    Attributes:\n        group_id (int): 消息组id\n        group_type (int): 消息组类别\n        last_msg_id (int): 最新消息的id\n    \"\"\"\n\n    group_id: int = 0\n    group_type: int = 0\n    last_msg_id: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> WsMsgGroupInfo:\n        group_id = data_proto.groupId\n        group_type = data_proto.groupType\n        last_msg_id = data_proto.lastMsgId\n        return WsMsgGroupInfo(group_id, group_type, last_msg_id)\n"
  },
  {
    "path": "src/aiotieba/api/init_websocket/protobuf/UpdateClientInfoReqIdl.proto",
    "content": "// protobuf.UpdateClientInfo.UpdateClientInfoReqIdl\nsyntax = \"proto3\";\n\nmessage UpdateClientInfoReqIdl {\n    string cuid = 1;\n    message DataReq {\n        string bduss = 1;\n        string device = 2;\n        bytes secretKey = 3;\n        string stoken = 12;\n    }\n    DataReq data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/init_websocket/protobuf/UpdateClientInfoReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1cUpdateClientInfoReqIdl.proto\"\\xa2\\x01\\n\\x16UpdateClientInfoReqIdl\\x12\\x0c\\n\\x04\\x63uid\\x18\\x01 \\x01(\\t\\x12-\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1f.UpdateClientInfoReqIdl.DataReq\\x1aK\\n\\x07\\x44\\x61taReq\\x12\\r\\n\\x05\\x62\\x64uss\\x18\\x01 \\x01(\\t\\x12\\x0e\\n\\x06\\x64\\x65vice\\x18\\x02 \\x01(\\t\\x12\\x11\\n\\tsecretKey\\x18\\x03 \\x01(\\x0c\\x12\\x0e\\n\\x06stoken\\x18\\x0c \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"UpdateClientInfoReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_UPDATECLIENTINFOREQIDL\"]._serialized_start = 33\n    _globals[\"_UPDATECLIENTINFOREQIDL\"]._serialized_end = 195\n    _globals[\"_UPDATECLIENTINFOREQIDL_DATAREQ\"]._serialized_start = 120\n    _globals[\"_UPDATECLIENTINFOREQIDL_DATAREQ\"]._serialized_end = 195\n"
  },
  {
    "path": "src/aiotieba/api/init_websocket/protobuf/UpdateClientInfoResIdl.proto",
    "content": "// protobuf.UpdateClientInfo.UpdateClientInfoResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage UpdateClientInfoResIdl {\n    Error error = 1;\n    message DataRes {\n        message GroupInfo {\n            int64 groupId = 1;\n            int32 groupType = 20;\n            int64 lastMsgId = 21;\n        }\n        repeated GroupInfo groupInfo = 1;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/init_websocket/protobuf/UpdateClientInfoResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1cUpdateClientInfoResIdl.proto\\x1a\\x0b\\x45rror.proto\"\\xec\\x01\\n\\x16UpdateClientInfoResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12-\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x1f.UpdateClientInfoResIdl.DataRes\\x1a\\x8b\\x01\\n\\x07\\x44\\x61taRes\\x12<\\n\\tgroupInfo\\x18\\x01 \\x03(\\x0b\\x32).UpdateClientInfoResIdl.DataRes.GroupInfo\\x1a\\x42\\n\\tGroupInfo\\x12\\x0f\\n\\x07groupId\\x18\\x01 \\x01(\\x03\\x12\\x11\\n\\tgroupType\\x18\\x14 \\x01(\\x05\\x12\\x11\\n\\tlastMsgId\\x18\\x15 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"UpdateClientInfoResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_UPDATECLIENTINFORESIDL\"]._serialized_start = 46\n    _globals[\"_UPDATECLIENTINFORESIDL\"]._serialized_end = 282\n    _globals[\"_UPDATECLIENTINFORESIDL_DATARES\"]._serialized_start = 143\n    _globals[\"_UPDATECLIENTINFORESIDL_DATARES\"]._serialized_end = 282\n    _globals[\"_UPDATECLIENTINFORESIDL_DATARES_GROUPINFO\"]._serialized_start = 216\n    _globals[\"_UPDATECLIENTINFORESIDL_DATARES_GROUPINFO\"]._serialized_end = 282\n"
  },
  {
    "path": "src/aiotieba/api/init_z_id/__init__.py",
    "content": "from ._api import request\n"
  },
  {
    "path": "src/aiotieba/api/init_z_id/_api.py",
    "content": "import binascii\nimport gzip\nimport hashlib\nimport time\n\nimport aiohttp\nimport yarl\nfrom cryptography.hazmat.primitives import padding\nfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\n\nfrom ...const import STABLE_VERSION\nfrom ...core import HttpCore\nfrom ...helper import pack_json, parse_json\nfrom ...helper.crypto import rc4_42\n\nSOFIRE_HOST = \"sofire.baidu.com\"\n\n\nasync def request(http_core: HttpCore):\n    app_key = \"200033\"  # get by p/5/aio\n    sec_key = \"ea737e4f435b53786043369d2e5ace4f\"\n    xyus = (\n        hashlib.md5((http_core.account.android_id + http_core.account.uuid).encode(\"ascii\")).hexdigest().upper() + \"|0\"\n    )\n    xyus_md5_str = hashlib.md5(xyus.encode(\"ascii\")).hexdigest()\n    current_ts = str(int(time.time()))\n\n    params = {\"module_section\": [{\"zid\": xyus}]}\n\n    req_body = pack_json(params)\n    req_body = gzip.compress(req_body.encode(\"utf-8\"), compresslevel=6, mtime=0)\n\n    padder = padding.PKCS7(algorithms.AES.block_size).padder()\n    padded_req_body = padder.update(req_body) + padder.finalize()\n    aes_encryptor = http_core.account.aes_cbc_chiper.encryptor()\n    req_body_aes = aes_encryptor.update(padded_req_body) + aes_encryptor.finalize()\n\n    req_body_md5 = hashlib.md5(req_body).digest()\n\n    payload = aiohttp.payload.BytesPayload(\n        req_body_aes + req_body_md5,\n        content_type=\"application/x-www-form-urlencoded\",\n    )\n\n    headers = {\n        \"x-device-id\": xyus_md5_str,\n        \"User-Agent\": f\"x6/{app_key}/{STABLE_VERSION}/4.4.1.3\",\n        \"x-plu-ver\": \"x6/4.4.1.3\",\n    }\n\n    path_combine = \"\".join((app_key, current_ts, sec_key))\n    path_combine_md5 = hashlib.md5(path_combine.encode(\"ascii\")).hexdigest()\n    req_query_skey = rc4_42(xyus_md5_str, http_core.account.aes_cbc_sec_key)\n    req_query_skey = binascii.b2a_base64(req_query_skey).decode(\"ascii\")\n    url = yarl.URL.build(\n        scheme=\"https\",\n        host=SOFIRE_HOST,\n        path=f\"/c/11/z/100/{app_key}/{current_ts}/{path_combine_md5}\",\n        query=[(\"skey\", req_query_skey)],\n    )\n\n    request = aiohttp.ClientRequest(\n        aiohttp.hdrs.METH_POST,\n        url,\n        headers=headers,\n        data=payload,\n        proxy=http_core.net_core.proxy.url,\n        proxy_auth=http_core.net_core.proxy.auth,\n        ssl=False,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    res_json = parse_json(body)\n\n    res_query_skey = binascii.a2b_base64(res_json[\"skey\"])\n    res_aes_sec_key = rc4_42(xyus_md5_str, res_query_skey)\n    res_data = binascii.a2b_base64(res_json[\"data\"])\n\n    iv = b\"\\x00\" * 16\n    aes_chiper = Cipher(algorithms.AES(res_aes_sec_key), modes.CBC(iv))\n    aes_decryptor = aes_chiper.decryptor()\n    decrypted_res_data = aes_decryptor.update(res_data) + aes_decryptor.finalize()\n    decrypted_res_data = decrypted_res_data[:-16]  # remove suffix md5\n    unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()\n    unpadded_data = unpadder.update(decrypted_res_data) + unpadder.finalize()\n\n    res_data = unpadded_data.decode(\"utf-8\")\n    res_data = parse_json(res_data)\n    zid = res_data[\"token\"]\n\n    return zid\n"
  },
  {
    "path": "src/aiotieba/api/login/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/login/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import UserInfo_login\n\nif TYPE_CHECKING:\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> tuple[UserInfo_login, str]:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    user_dict = res_json[\"user\"]\n    user = UserInfo_login.from_json(user_dict)\n    tbs = res_json[\"anti\"][\"tbs\"]\n\n    return user, tbs\n\n\nasync def request(http_core: HttpCore) -> tuple[UserInfo_login, str]:\n    data = [\n        (\"_client_version\", LATEST_VERSION),\n        (\"bdusstoken\", http_core.account.BDUSS),\n    ]\n\n    request = http_core.pack_form_request(yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/s/login\"), data)\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/login/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass UserInfo_login:\n    \"\"\"\n    用户信息\n\n    Attributes:\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> UserInfo_login:\n        user_id = int(data_map[\"id\"])\n        portrait = data_map[\"portrait\"]\n        user_name = data_map[\"name\"]\n        return UserInfo_login(user_id, portrait, user_name)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n"
  },
  {
    "path": "src/aiotieba/api/move/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/move/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import pack_json, parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int, tid: int, to_tab_id: int, from_tab_id: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"forum_id\", fid),\n        (\"tbs\", http_core.account.tbs),\n        (\"threads\", pack_json([{\"thread_id\": tid, \"from_tab_id\": from_tab_id, \"to_tab_id\": to_tab_id}])),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/moveTabThread\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/profile/__init__.py",
    "content": "from . import get_homepage, get_uinfo_profile\nfrom ._classdef import Homepage, Thread_pf, UserInfo_pf\nfrom ._const import CMD\n"
  },
  {
    "path": "src/aiotieba/api/profile/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom functools import cached_property\n\nfrom ...enums import Gender, PrivLike, PrivReply\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers, TypeMessage, VoteInfo\nfrom .._classdef.contents import (\n    _IMAGEHASH_EXP,\n    FragAt,\n    FragEmoji,\n    FragLink,\n    FragText,\n    FragUnknown,\n    FragVideo,\n    FragVoice,\n    TypeFragment,\n    TypeFragText,\n)\n\nFragText_pf = FragText\nFragEmoji_pf = FragEmoji\nFragAt_pf = FragAt\nFragLink_pf = FragLink\nFragVideo_pf = FragVideo\nFragVoice_pf = FragVoice\n\n\n@dcs.dataclass\nclass UserInfo_pf(TbErrorExt):\n    \"\"\"\n    用户信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n        tieba_uid (int): 用户个人主页uid\n\n        glevel (int): 贴吧成长等级\n        gender (Gender): 性别\n        age (float): 吧龄 以年为单位\n        post_num (int): 发帖数\n        agree_num (int): 获赞数\n        fan_num (int): 粉丝数\n        follow_num (int): 关注数\n        forum_num (int): 关注贴吧数\n        sign (str): 个性签名\n        ip (str): ip归属地\n        icons (list[str]): 印记信息\n\n        is_vip (bool): 是否超级会员\n        is_god (bool): 是否大神\n        is_blocked (bool): 是否被永久封禁屏蔽\n        priv_like (PrivLike): 关注吧列表的公开状态\n        priv_reply (PrivReply): 帖子评论权限\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n    tieba_uid: int = 0\n\n    glevel: int = 0\n    gender: Gender = Gender.UNKNOWN\n    age: float = 0.0\n    post_num: int = 0\n    agree_num: int = 0\n    fan_num: int = 0\n    follow_num: int = 0\n    forum_num: int = 0\n    sign: str = \"\"\n    ip: str = \"\"\n    icons: list[str] = dcs.field(default_factory=list)\n\n    is_vip: bool = False\n    is_god: bool = False\n    is_blocked: bool = False\n    priv_like: PrivLike = PrivLike.PUBLIC\n    priv_reply: PrivReply = PrivReply.ALL\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_pf:\n        user_proto = data_proto.user\n        user_id = user_proto.id\n        portrait = user_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = user_proto.name\n        nick_name_new = user_proto.name_show\n        tieba_uid = int(tieba_uid) if (tieba_uid := user_proto.tieba_uid) else 0\n        glevel = user_proto.user_growth.level_id\n        gender = Gender(user_proto.sex)\n        age = float(age) if (age := user_proto.tb_age) else 0.0\n        post_num = user_proto.post_num\n        agree_num = data_proto.user_agree_info.total_agree_num\n        fan_num = user_proto.fans_num\n        follow_num = user_proto.concern_num\n        forum_num = user_proto.my_like_num\n        sign = user_proto.intro\n        ip = user_proto.ip_address\n        icons = [name for i in user_proto.iconinfo if (name := i.name)]\n        is_vip = bool(user_proto.new_tshow_icon)\n        is_god = bool(user_proto.new_god_data.status)\n        anti_proto = data_proto.anti_stat\n        if anti_proto.block_stat and anti_proto.hide_stat and anti_proto.days_tofree > 30:\n            is_blocked = True\n        else:\n            is_blocked = False\n        priv_like = PrivLike(priv_like) if (priv_like := user_proto.priv_sets.like) else PrivLike.PUBLIC\n        priv_reply = PrivReply(priv_reply) if (priv_reply := user_proto.priv_sets.reply) else PrivReply.ALL\n        return UserInfo_pf(\n            user_id,\n            portrait,\n            user_name,\n            nick_name_new,\n            tieba_uid,\n            glevel,\n            gender,\n            age,\n            post_num,\n            agree_num,\n            fan_num,\n            follow_num,\n            forum_num,\n            sign,\n            ip,\n            icons,\n            is_vip,\n            is_god,\n            is_blocked,\n            priv_like,\n            priv_reply,\n        )\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_pf) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @cached_property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n\n\n@dcs.dataclass\nclass FragImage_pf:\n    \"\"\"\n    图像碎片\n\n    Attributes:\n        src (str): 大图链接 宽960px\n        origin_src (str): 原图链接\n        width (int): 图像宽度\n        height (int): 图像高度\n        hash (str): 百度图床hash\n    \"\"\"\n\n    src: str = dcs.field(default=\"\", repr=False)\n    big_src: str = dcs.field(default=\"\", repr=False)\n    origin_src: str = dcs.field(default=\"\", repr=False)\n    width: int = 0\n    height: int = 0\n    hash: str = \"\"\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> FragImage_pf:\n        src = data_proto.big_pic\n        origin_src = data_proto.origin_pic\n        origin_size = data_proto.origin_size\n\n        width = data_proto.width\n        height = data_proto.height\n\n        hash_ = _IMAGEHASH_EXP.search(src).group(1)\n\n        return FragImage_pf(src, origin_src, origin_size, width, height, hash_)\n\n\n@dcs.dataclass\nclass Contents_pf(Containers[TypeFragment]):\n    \"\"\"\n    内容碎片列表\n\n    Attributes:\n        objs (list[TypeFragment]): 所有内容碎片的混合列表\n\n        text (str): 文本内容\n\n        texts (list[TypeFragText]): 纯文本碎片列表\n        emojis (list[FragEmoji_pf]): 表情碎片列表\n        imgs (list[FragImage_pf]): 图像碎片列表\n        ats (list[FragAt_pf]): @碎片列表\n        links (list[FragLink_pf]): 链接碎片列表\n        video (FragVideo_pf): 视频碎片\n        voice (FragVoice_pf): 音频碎片\n    \"\"\"\n\n    texts: list[TypeFragText] = dcs.field(default_factory=list, repr=False)\n    emojis: list[FragEmoji_pf] = dcs.field(default_factory=list, repr=False)\n    imgs: list[FragImage_pf] = dcs.field(default_factory=list, repr=False)\n    ats: list[FragAt_pf] = dcs.field(default_factory=list, repr=False)\n    links: list[FragLink_pf] = dcs.field(default_factory=list, repr=False)\n    video: FragVideo_pf = dcs.field(default_factory=FragVideo_pf, repr=False)\n    voice: FragVoice_pf = dcs.field(default_factory=FragVoice_pf, repr=False)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Contents_pf:\n        content_protos = data_proto.first_post_content\n\n        texts = []\n        emojis = []\n        imgs = [FragImage_pf.from_proto(p) for p in data_proto.media if p.type != 5]\n        ats = []\n        links = []\n\n        def _frags():\n            for proto in content_protos:\n                _type = proto.type\n                # 0纯文本 9电话号 18话题 27百科词条\n                if _type in [0, 9, 18, 27]:\n                    frag = FragText_pf.from_proto(proto)\n                    texts.append(frag)\n                    yield frag\n                # 11:tid=5047676428\n                elif _type in [2, 11]:\n                    frag = FragEmoji_pf.from_proto(proto)\n                    emojis.append(frag)\n                    yield frag\n                elif _type in [3, 20]:\n                    continue\n                elif _type == 4:\n                    frag = FragAt_pf.from_proto(proto)\n                    ats.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 1:\n                    frag = FragLink_pf.from_proto(proto)\n                    links.append(frag)\n                    texts.append(frag)\n                    yield frag\n                elif _type == 5:  # video\n                    continue\n                elif _type == 10:  # voice\n                    continue\n                else:\n                    yield FragUnknown.from_proto(frag)\n\n        objs = list(_frags())\n        objs += imgs\n\n        if data_proto.video_info.video_width:\n            video = FragVideo_pf.from_proto(data_proto.video_info)\n            objs.append(video)\n        else:\n            video = FragVideo_pf()\n\n        if data_proto.voice_info:\n            voice = FragVoice_pf.from_proto(data_proto.voice_info[0])\n            objs.append(voice)\n        else:\n            voice = FragVoice_pf()\n\n        return Contents_pf(objs, texts, emojis, imgs, ats, links, video, voice)\n\n    @cached_property\n    def text(self) -> str:\n        text = \"\".join(frag.text for frag in self.texts)\n        return text\n\n\n@dcs.dataclass\nclass Thread_pf:\n    \"\"\"\n    主题帖信息\n\n    Attributes:\n        text (str): 文本内容\n        contents (Contents_pf): 正文内容碎片列表\n        title (str): 标题内容\n\n        fid (int): 所在吧id\n        fname (str): 所在贴吧名\n        tid (int): 主题帖tid\n        pid (int): 首楼回复pid\n        user (UserInfo_pf): 发布者的用户信息\n        author_id (int): 发布者的user_id\n\n        vote_info (VoteInfo): 投票信息\n        view_num (int): 浏览量\n        reply_num (int): 回复数\n        share_num (int): 分享数\n        agree (int): 点赞数\n        disagree (int): 点踩数\n        create_time (int): 创建时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    contents: Contents_pf = dcs.field(default_factory=Contents_pf)\n    title: str = \"\"\n\n    fid: int = 0\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    user: UserInfo_pf = dcs.field(default_factory=UserInfo_pf)\n\n    vote_info: VoteInfo = dcs.field(default_factory=VoteInfo)\n    view_num: int = 0\n    reply_num: int = 0\n    share_num: int = 0\n    agree: int = 0\n    disagree: int = 0\n    create_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Thread_pf:\n        contents = Contents_pf.from_proto(data_proto)\n        title = data_proto.title\n        fid = data_proto.forum_id\n        fname = data_proto.forum_name\n        tid = data_proto.thread_id\n        pid = data_proto.post_id\n        vote_info = VoteInfo.from_proto(data_proto.poll_info)\n        view_num = data_proto.freq_num\n        reply_num = data_proto.reply_num\n        share_num = data_proto.share_num\n        agree = data_proto.agree.agree_num\n        disagree = data_proto.agree.disagree_num\n        create_time = data_proto.create_time\n        return Thread_pf(\n            contents,\n            title,\n            fid,\n            fname,\n            tid,\n            pid,\n            None,\n            vote_info,\n            view_num,\n            reply_num,\n            share_num,\n            agree,\n            disagree,\n            create_time,\n        )\n\n    def __eq__(self, obj: Thread_pf) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n    @cached_property\n    def text(self) -> str:\n        if self.title:\n            text = f\"{self.title}\\n{self.contents.text}\"\n        else:\n            text = self.contents.text\n        return text\n\n    @property\n    def author_id(self) -> int:\n        return self.user.user_id\n\n\n@dcs.dataclass\nclass Homepage(TbErrorExt, Containers[Thread_pf]):\n    \"\"\"\n    用户个人页信息\n\n    Attributes:\n        objs (list[Thread_pf]): 用户发布主题帖列表\n        err (Exception | None): 捕获的异常\n\n        user (UserInfo_pf): 用户信息\n    \"\"\"\n\n    user: UserInfo_pf = dcs.field(default_factory=UserInfo_pf)\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> Homepage:\n        objs = [Thread_pf.from_proto(p) for p in data_proto.post_list]\n        user = UserInfo_pf.from_proto(data_proto)\n\n        for thread in objs:\n            thread.user = user\n\n        return Homepage(objs, user)\n"
  },
  {
    "path": "src/aiotieba/api/profile/_const.py",
    "content": "CMD = 303012\n"
  },
  {
    "path": "src/aiotieba/api/profile/get_homepage/__init__.py",
    "content": "from ._api import pack_proto, parse_body, request_http, request_ws\n"
  },
  {
    "path": "src/aiotieba/api/profile/get_homepage/_api.py",
    "content": "import yarl\n\nfrom ....const import APP_BASE_HOST, LATEST_VERSION\nfrom ....core import HttpCore, WsCore\nfrom ....exception import TiebaServerError\nfrom .._classdef import Homepage\nfrom .._const import CMD\nfrom ..protobuf import ProfileReqIdl_pb2, ProfileResIdl_pb2\n\n\ndef pack_proto(user_id: int, pn: int) -> bytes:\n    req_proto = ProfileReqIdl_pb2.ProfileReqIdl()\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.common._client_type = 2\n    req_proto.data.uid = user_id\n    req_proto.data.need_post_count = 1\n    req_proto.data.pn = pn\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> Homepage:\n    res_proto = ProfileResIdl_pb2.ProfileResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    homepage = Homepage.from_proto(data_proto)\n\n    return homepage\n\n\nasync def request_http(http_core: HttpCore, user_id: int, pn: int) -> Homepage:\n    data = pack_proto(user_id, pn)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/u/user/profile\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=64 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, user_id: int, pn: int) -> Homepage:\n    data = pack_proto(user_id, pn)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/profile/get_uinfo_profile/__init__.py",
    "content": "from ._api import pack_proto, parse_body, request_http, request_ws\n"
  },
  {
    "path": "src/aiotieba/api/profile/get_uinfo_profile/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport yarl\n\nfrom ....const import APP_BASE_HOST, LATEST_VERSION\nfrom ....exception import TiebaServerError\nfrom .._classdef import UserInfo_pf\nfrom .._const import CMD\nfrom ..protobuf import ProfileReqIdl_pb2, ProfileResIdl_pb2\n\nif TYPE_CHECKING:\n    from ....core import HttpCore, WsCore\n\n\ndef pack_proto(uid_or_portrait: str | int) -> bytes:\n    req_proto = ProfileReqIdl_pb2.ProfileReqIdl()\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.common._client_type = 2\n    req_proto.data.need_post_count = 1\n    req_proto.data.page = 1\n\n    if isinstance(uid_or_portrait, int):\n        req_proto.data.uid = uid_or_portrait\n    else:\n        req_proto.data.friend_uid_portrait = uid_or_portrait\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> UserInfo_pf:\n    res_proto = ProfileResIdl_pb2.ProfileResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    data_proto = res_proto.data\n    user = UserInfo_pf.from_proto(data_proto)\n\n    return user\n\n\nasync def request_http(http_core: HttpCore, uid_or_portrait: str | int) -> UserInfo_pf:\n    data = pack_proto(uid_or_portrait)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/u/user/profile\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, uid_or_portrait: str | int) -> UserInfo_pf:\n    data = pack_proto(uid_or_portrait)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/profile/protobuf/ProfileReqIdl.proto",
    "content": "// tbclient.Profile.ProfileReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage ProfileReqIdl {\n    message DataReq {\n        int64 uid = 1;\n        uint32 need_post_count = 2;\n        uint32 pn = 6;\n        CommonReq common = 9;\n        int32 page = 15;\n        string friend_uid_portrait = 16;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/profile/protobuf/ProfileReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13ProfileReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\xba\\x01\\n\\rProfileReqIdl\\x12$\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x16.ProfileReqIdl.DataReq\\x1a\\x82\\x01\\n\\x07\\x44\\x61taReq\\x12\\x0b\\n\\x03uid\\x18\\x01 \\x01(\\x03\\x12\\x17\\n\\x0fneed_post_count\\x18\\x02 \\x01(\\r\\x12\\n\\n\\x02pn\\x18\\x06 \\x01(\\r\\x12\\x1a\\n\\x06\\x63ommon\\x18\\t \\x01(\\x0b\\x32\\n.CommonReq\\x12\\x0c\\n\\x04page\\x18\\x0f \\x01(\\x05\\x12\\x1b\\n\\x13\\x66riend_uid_portrait\\x18\\x10 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"ProfileReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PROFILEREQIDL\"]._serialized_start = 41\n    _globals[\"_PROFILEREQIDL\"]._serialized_end = 227\n    _globals[\"_PROFILEREQIDL_DATAREQ\"]._serialized_start = 97\n    _globals[\"_PROFILEREQIDL_DATAREQ\"]._serialized_end = 227\n"
  },
  {
    "path": "src/aiotieba/api/profile/protobuf/ProfileResIdl.proto",
    "content": "// tbclient.Profile.ProfileResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"User.proto\";\nimport \"PostInfoList.proto\";\n\nmessage ProfileResIdl {\n    Error error = 1;\n    message DataRes {\n        User user = 1;\n        message Anti {\n            int32 block_stat = 6;\n            int32 hide_stat = 7;\n            int32 days_tofree = 9;\n        }\n        Anti anti_stat = 2;\n        repeated PostInfoList post_list = 4;\n        message UserAgreeInfo {\n            int64 total_agree_num = 1;\n        }\n        UserAgreeInfo user_agree_info = 14;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/profile/protobuf/ProfileResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import PostInfoList_pb2 as PostInfoList__pb2\nfrom ..._protobuf import User_pb2 as User__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x13ProfileResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nUser.proto\\x1a\\x12PostInfoList.proto\"\\xec\\x02\\n\\rProfileResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12$\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32\\x16.ProfileResIdl.DataRes\\x1a\\x9d\\x02\\n\\x07\\x44\\x61taRes\\x12\\x13\\n\\x04user\\x18\\x01 \\x01(\\x0b\\x32\\x05.User\\x12.\\n\\tanti_stat\\x18\\x02 \\x01(\\x0b\\x32\\x1b.ProfileResIdl.DataRes.Anti\\x12 \\n\\tpost_list\\x18\\x04 \\x03(\\x0b\\x32\\r.PostInfoList\\x12=\\n\\x0fuser_agree_info\\x18\\x0e \\x01(\\x0b\\x32$.ProfileResIdl.DataRes.UserAgreeInfo\\x1a\\x42\\n\\x04\\x41nti\\x12\\x12\\n\\nblock_stat\\x18\\x06 \\x01(\\x05\\x12\\x11\\n\\thide_stat\\x18\\x07 \\x01(\\x05\\x12\\x13\\n\\x0b\\x64\\x61ys_tofree\\x18\\t \\x01(\\x05\\x1a(\\n\\rUserAgreeInfo\\x12\\x17\\n\\x0ftotal_agree_num\\x18\\x01 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"ProfileResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PROFILERESIDL\"]._serialized_start = 69\n    _globals[\"_PROFILERESIDL\"]._serialized_end = 433\n    _globals[\"_PROFILERESIDL_DATARES\"]._serialized_start = 148\n    _globals[\"_PROFILERESIDL_DATARES\"]._serialized_end = 433\n    _globals[\"_PROFILERESIDL_DATARES_ANTI\"]._serialized_start = 325\n    _globals[\"_PROFILERESIDL_DATARES_ANTI\"]._serialized_end = 391\n    _globals[\"_PROFILERESIDL_DATARES_USERAGREEINFO\"]._serialized_start = 393\n    _globals[\"_PROFILERESIDL_DATARES_USERAGREEINFO\"]._serialized_end = 433\n"
  },
  {
    "path": "src/aiotieba/api/push_notify/__init__.py",
    "content": "from ._api import CMD, parse_body\nfrom ._classdef import WsNotify\n"
  },
  {
    "path": "src/aiotieba/api/push_notify/_api.py",
    "content": "from __future__ import annotations\n\nfrom ._classdef import WsNotify\nfrom .protobuf import PushNotifyResIdl_pb2\n\nCMD = 202006\n\n\ndef parse_body(body: bytes) -> list[WsNotify]:\n    res_proto = PushNotifyResIdl_pb2.PushNotifyResIdl()\n    res_proto.ParseFromString(body)\n\n    notifies = [WsNotify.from_proto(p) for p in res_proto.multiMsg]\n\n    return notifies\n"
  },
  {
    "path": "src/aiotieba/api/push_notify/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from .._classdef import TypeMessage\n\n\n@dcs.dataclass\nclass WsNotify:\n    \"\"\"\n    websocket主动推送消息提醒\n\n    Attributes:\n        note_type (int): 提醒类别\n        group_id (int): 消息组id\n        group_type (int): 消息组类别\n        msg_id (int): 消息id\n        create_time (int): 推送时间 10位时间戳 以秒为单位\n    \"\"\"\n\n    note_type: int = 0\n    group_id: int = 0\n    group_type: int = 0\n    msg_id: int = 0\n    create_time: int = 0\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> WsNotify:\n        data_proto = data_proto.data\n        note_type = data_proto.type\n        group_type = data_proto.groupType\n        group_id = data_proto.groupId\n        msg_id = data_proto.msgId\n        create_time = str(create_time) if (create_time := data_proto.et) else 0\n        return WsNotify(note_type, group_id, group_type, msg_id, create_time)\n"
  },
  {
    "path": "src/aiotieba/api/push_notify/protobuf/PushNotifyResIdl.proto",
    "content": "// protobuf.PushNotify.PushNotifyResIdl\nsyntax = \"proto3\";\n\nmessage PushNotifyResIdl {\n    message PusherMsg {\n        message PusherMsgInfo {\n            int64 groupId = 1;\n            int64 msgId = 2;\n            int32 type = 4;\n            string et = 6;\n            int32 groupType = 7;\n        }\n        PusherMsgInfo data = 2;\n    }\n    repeated PusherMsg multiMsg = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/push_notify/protobuf/PushNotifyResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x16PushNotifyResIdl.proto\"\\xe6\\x01\\n\\x10PushNotifyResIdl\\x12-\\n\\x08multiMsg\\x18\\x02 \\x03(\\x0b\\x32\\x1b.PushNotifyResIdl.PusherMsg\\x1a\\xa2\\x01\\n\\tPusherMsg\\x12\\x37\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32).PushNotifyResIdl.PusherMsg.PusherMsgInfo\\x1a\\\\\\n\\rPusherMsgInfo\\x12\\x0f\\n\\x07groupId\\x18\\x01 \\x01(\\x03\\x12\\r\\n\\x05msgId\\x18\\x02 \\x01(\\x03\\x12\\x0c\\n\\x04type\\x18\\x04 \\x01(\\x05\\x12\\n\\n\\x02\\x65t\\x18\\x06 \\x01(\\t\\x12\\x11\\n\\tgroupType\\x18\\x07 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"PushNotifyResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_PUSHNOTIFYRESIDL\"]._serialized_start = 27\n    _globals[\"_PUSHNOTIFYRESIDL\"]._serialized_end = 257\n    _globals[\"_PUSHNOTIFYRESIDL_PUSHERMSG\"]._serialized_start = 95\n    _globals[\"_PUSHNOTIFYRESIDL_PUSHERMSG\"]._serialized_end = 257\n    _globals[\"_PUSHNOTIFYRESIDL_PUSHERMSG_PUSHERMSGINFO\"]._serialized_start = 165\n    _globals[\"_PUSHNOTIFYRESIDL_PUSHERMSG_PUSHERMSGINFO\"]._serialized_end = 257\n"
  },
  {
    "path": "src/aiotieba/api/recommend/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/recommend/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n    if code := int(res_json[\"data\"][\"is_push_success\"]) != 1:\n        raise TiebaServerError(code, res_json[\"data\"][\"msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int, tid: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"forum_id\", fid),\n        (\"thread_id\", tid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/pushRecomToPersonalized\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/recover/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/recover/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n\nasync def request(http_core: HttpCore, fid: int, tid: int, pid: int, is_hide: bool) -> BoolResponse:\n    data = [\n        (\"tbs\", http_core.account.tbs),\n        (\"fn\", \"-\"),\n        (\"fid\", fid),\n        (\"tid_list[]\", tid),\n        (\"pid_list[]\", pid),\n        (\"type_list[]\", 1 if pid else 0),\n        (\"is_frs_mask_list[]\", int(is_hide)),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/bawurecoverthread\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/remove_fan/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/remove_fan/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, user_id: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"fans_uid\", user_id),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/user/removeFans\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/search_exact/__init__.py",
    "content": "from ._api import parse_body, request\nfrom ._classdef import ExactSearches\n"
  },
  {
    "path": "src/aiotieba/api/search_exact/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...enums import SearchType\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\nfrom ._classdef import ExactSearches\n\n\ndef parse_body(body: bytes) -> ExactSearches:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    searches = ExactSearches.from_json(res_json)\n\n    return searches\n\n\nasync def request(\n    http_core: HttpCore, fname: str, query: str, pn: int, rn: int, search_type: SearchType, only_thread: bool\n) -> ExactSearches:\n    data = [\n        (\"_client_version\", LATEST_VERSION),\n        (\"kw\", fname),\n        (\"only_thread\", int(only_thread)),\n        (\"pn\", pn),\n        (\"rn\", rn),\n        (\"sm\", search_type),\n        (\"word\", query),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"http\", host=APP_BASE_HOST, path=\"/c/s/searchpost\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=8 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/search_exact/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\nfrom .._classdef import Containers\n\nif TYPE_CHECKING:\n    from collections.abc import Mapping\n\n\n@dcs.dataclass\nclass ExactSearch:\n    \"\"\"\n    搜索结果\n\n    Attributes:\n        text (str): 文本内容\n        title (str): 标题内容\n\n        fname (str): 所在贴吧名\n        tid (int): 所在主题帖id\n        pid (int): 回复id\n        show_name (str): 发布者的显示名称\n\n        is_comment (bool): 是否楼中楼\n        create_time (int): 创建时间\n    \"\"\"\n\n    text: str = \"\"\n    title: str = \"\"\n\n    fname: str = \"\"\n    tid: int = 0\n    pid: int = 0\n    show_name: str = \"\"\n\n    is_comment: bool = False\n    create_time: int = 0\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> ExactSearch:\n        text = data_map[\"content\"]\n        title = data_map[\"title\"]\n        fname = data_map[\"fname\"]\n        tid = int(data_map[\"tid\"])\n        pid = int(data_map[\"pid\"])\n        show_name = data_map[\"author\"][\"name_show\"]\n        is_comment = bool(int(data_map[\"is_floor\"]))\n        create_time = int(data_map[\"time\"])\n        return ExactSearch(text, title, fname, tid, pid, show_name, is_comment, create_time)\n\n    def __eq__(self, obj: ExactSearch) -> bool:\n        return self.pid == obj.pid\n\n    def __hash__(self) -> int:\n        return self.pid\n\n\n@dcs.dataclass\nclass Page_exsch:\n    \"\"\"\n    页信息\n\n    Attributes:\n        page_size (int): 页大小\n        current_page (int): 当前页码\n        total_page (int): 总页码\n        total_count (int): 总计数\n\n        has_more (bool): 是否有后继页\n        has_prev (bool): 是否有前驱页\n    \"\"\"\n\n    page_size: int = 0\n    current_page: int = 0\n    total_page: int = 0\n    total_count: int = 0\n\n    has_more: bool = False\n    has_prev: bool = False\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> Page_exsch:\n        page_size = int(data_map[\"page_size\"])\n        current_page = int(data_map[\"current_page\"])\n        total_page = int(data_map[\"total_page\"])\n        total_count = int(data_map[\"total_count\"])\n        has_more = bool(int(data_map[\"has_more\"]))\n        has_prev = bool(int(data_map[\"has_prev\"]))\n        return Page_exsch(page_size, current_page, total_page, total_count, has_more, has_prev)\n\n\n@dcs.dataclass\nclass ExactSearches(TbErrorExt, Containers[ExactSearch]):\n    \"\"\"\n    搜索结果列表\n\n    Attributes:\n        objs (list[ExactSearch]): 搜索结果列表\n        err (Exception | None): 捕获的异常\n\n        page (Page_exsch): 页信息\n        has_more (bool): 是否还有下一页\n    \"\"\"\n\n    page: Page_exsch = dcs.field(default_factory=Page_exsch)\n\n    @staticmethod\n    def from_json(data_map: Mapping) -> ExactSearches:\n        objs = [ExactSearch.from_json(m) for m in data_map.get(\"post_list\", [])]\n        page = Page_exsch.from_json(data_map[\"page\"])\n        return ExactSearches(objs, page)\n\n    @property\n    def has_more(self) -> bool:\n        return self.page.has_more\n"
  },
  {
    "path": "src/aiotieba/api/send_chatroom_msg/__init__.py",
    "content": "from ._api import request\n"
  },
  {
    "path": "src/aiotieba/api/send_chatroom_msg/_api.py",
    "content": "import json\nimport time\nfrom dataclasses import dataclass\n\nfrom ...core import BLCPCore, BLCPData\nfrom ...exception import BoolResponse\n\n\n@dataclass\nclass AppConstants:\n    appid: int = 10773430\n    sdk_version: int = 11250036\n\n\nasync def construct_request_data(\n    blcpcore, room_id, uk, user_id, origin_id, name, portrait, text, forum_id, level, vip, glevel, atdata=None, robot=-1\n):\n    constants = AppConstants()\n\n    # 构造app_safe_ext\n    app_safe_ext = {\"haotianjing\": {\"zid\": blcpcore.account.z_id or \"\"}}\n\n    # 构造content\n    content = {\n        \"text\": {\n            \"room_id\": str(room_id),\n            \"type\": \"0\",\n            \"to_uid\": \"0\",\n            \"vip\": str(int(vip)),\n            \"name\": name,\n            \"portrait\": portrait + \"?t=\" + str(int(time.time())),\n            \"content_type\": \"0\",\n            \"content_body\": json.dumps({\"text\": text}, ensure_ascii=False),\n            \"src\": \"\",\n            \"baidu_uk\": blcpcore.getBDUKfromUserId(str(user_id)),\n            \"ext\": {},\n        }\n    }\n\n    # 构造main_data，其主要包含名字、头像、昵称颜色、大会员标志等UI展示信息\n    main_data = []\n    if vip:\n        main_data.append({\n            \"icon\": {\n                \"height\": 75,\n                \"priority\": 2,\n                \"schema\": \"https://tieba.baidu.com/mo/q/hybrid-business-vip/tbvip?customfullscreen=1&nonavigationbar=1\",\n                \"type\": \"1\",\n                \"url\": \"https://tieba-ares.cdn.bcebos.com/mis/2023-7/1689061482682/13afea50121d.png\",\n                \"width\": 75,\n            },\n            \"type\": 2,\n        })\n\n    # namedata将被包含在main_data中\n    namedata = {\n        \"text\": {\n            \"short_enable\": 1,\n            \"short_length\": 5,\n            \"short_priority\": 1,\n            \"priority\": 1,\n            \"str\": name,\n            \"suffix\": \"...\",\n            \"type\": \"1\",\n        },\n        \"type\": 1,\n    }\n\n    if vip:\n        # 开通了贴吧大会员，更新名称颜色\n        namedata[\"text\"].update({\"text_color\": {\"day\": \"CAM_X0301\", \"night\": \"CAM_X0301\", \"type\": 2}})\n    main_data.extend((\n        namedata,\n        {\n            \"icon\": {\n                \"height\": 15,\n                \"priority\": 5,\n                \"schema\": \"https://tieba.baidu.com/mo/q/hybrid-main-user/taskCenter?customfullscreen=1&nonavigationbar=1\",\n                \"type\": \"3\",\n                \"url\": \"local://icon/icon_mask_level_usergrouth_\" + str(glevel) + \"?type=webp\",\n                \"width\": 24,\n            },\n            \"type\": 2,\n        },\n        {\n            \"icon\": {\n                \"height\": 12,\n                \"priority\": 2,\n                \"schema\": \"https://tieba.baidu.com/mo/q/wise-bawu-core/forum-level?customfullscreen=1&forum_id=\"\n                + str(forum_id)\n                + \"&nonavigationbar=1&obj_locate=5&portrait=\"\n                + portrait\n                + \"?t=\"\n                + str(int(time.time())),\n                \"type\": \"4\",\n                \"url\": \"local://icon/icon_level_\" + f\"{level:02d}\" + \"?type=webp\",\n                \"width\": 16,\n            },\n            \"type\": 2,\n        },\n    ))\n\n    ext_data = {\n        \"main_data\": main_data,\n        \"is_sys_msg\": 0,\n        \"version\": \"\",\n        \"portrait\": portrait + \"?t=\" + str(int(time.time())),\n        \"robot_role\": 0,\n        \"role\": 0,\n        \"send_status\": 0,\n        \"from\": \"android\",\n        \"session_id\": room_id,\n        \"type\": 1,\n        \"user_name\": name,\n    }\n\n    content[\"text\"][\"ext\"].update(ext_data)\n\n    # 携带文字、气泡信息，但这部分内容比较多，暂且未开发，注释之\n    # content['text']['ext']['bubble_info'] = {'color_info': {'at_text_color': '', 'at_text_color_dark': 'AF9EFF', 'text_color': '141414', 'text_color_dark': 'FFFFFF'}, 'end_time': 0, 'id': 1380005, 'img_info': {'android_left': 'https://tieba-ares.cdn.bcebos.com/mis/2023-3/1678971300129/4d9fd67c26e3.png', 'android_left_dark': 'https://tieba-ares.cdn.bcebos.com/mis/2023-3/1678971300129/4d9fd67c26e3.png', 'android_right': 'https://tieba-ares.cdn.bcebos.com/mis/2023-3/1678971357436/745549b2ae39.png', 'android_right_dark': 'https://tieba-ares.cdn.bcebos.com/mis/2023-3/1678971357436/745549b2ae39.png', 'ios_left': 'https://tieba-ares.cdn.bcebos.com/mis/2023-3/1678876082814/0a1741207e0d.png', 'ios_left_dark': 'https://tieba-ares.cdn.bcebos.com/mis/2023-3/1678876082814/0a1741207e0d.png', 'ios_right': 'https://tieba-ares.cdn.bcebos.com/mis/2023-3/1678876082435/c07fbaa4ad57.png', 'ios_right_dark': 'https://tieba-ares.cdn.bcebos.com/mis/2023-3/1678876082435/c07fbaa4ad57.png'}, 'jump_url': 'https://tieba.baidu.com/mo/q/hybrid/decorator?pageType=4&from_page=4&nonavigationbar=1&customfullscreen=1&decoratorId=1380005', 'type': 2}\n\n    if robot == -1:\n        # 没有机器人指令\n        content[\"text\"][\"ext\"][\"content\"] = {}\n    else:\n        # 携带机器人指令代码 robot，如签到10005、领取福利10004等\n        content[\"text\"][\"ext\"][\"content\"] = {\"robot_params\": {\"scene\": \"tieba_group_chat\", \"type\": robot}}\n\n    content[\"text\"][\"ext\"][\"level\"] = level\n    content[\"text\"][\"ext\"][\"forum_id\"] = forum_id\n\n    if atdata:\n        # 携带艾特@信息\n        content[\"text\"][\"at_data\"] = atdata\n    content[\"text\"][\"ext\"] = json.dumps(content[\"text\"][\"ext\"], ensure_ascii=False)\n\n    content[\"text\"] = json.dumps(content[\"text\"], ensure_ascii=False)\n    content = json.dumps(content, ensure_ascii=False)\n\n    # 构造最终请求数据\n    request_data = {\n        \"method\": 185,\n        \"mcast_id\": room_id,\n        \"role\": 3,\n        \"token\": blcpcore.account.BDUSS,\n        \"appid\": constants.appid,\n        \"uk\": uk,\n        \"origin_id\": origin_id,\n        \"type\": 81,\n        \"app_safe_ext\": json.dumps(app_safe_ext, ensure_ascii=False),\n        \"content\": content,\n        \"msg_key\": blcpcore.getmsgkey(blcpcore.getBDUKfromUserId(str(user_id))),\n        \"account_type\": 1,\n        \"sdk_version\": constants.sdk_version,\n        \"event_list\": [\n            {\"event\": \"CClickSendBegin\", \"timestamp_ms\": 0},\n            {\"event\": \"CSendBegin\", \"timestamp_ms\": 0},\n            {\"event\": \"CIMSendBegin\", \"timestamp_ms\": int(time.time() * 1000)},\n        ],\n    }\n\n    return request_data\n\n\nasync def send_request(blcpcore, request_data):\n    loginBLCPRequest = BLCPData(serviceId=3, methodId=185)\n    loginBLCPRequest.correlationId = int(time.time() * 1000 * 1000)\n    loginBLCPRequest.RpcBody = blcpcore.buildRpcBody(\n        serviceId=3, methodId=185, correlationId=loginBLCPRequest.correlationId\n    )\n\n    request_data[\"client_logid\"] = loginBLCPRequest.correlationId\n    request_data[\"rpc\"] = '{\"rpc_retry_time\":0}'\n\n    loginBLCPRequest.LcmBody = json.dumps(request_data, ensure_ascii=False).encode(\"utf-8\")\n    response = blcpcore.waiter.new(loginBLCPRequest.correlationId)\n    blcpcore.writer.write(loginBLCPRequest.toBytes())\n    reps = await response.read()\n    try:\n        err_code = json.loads(reps.LcmBody)[\"err_code\"]\n    except json.JSONDecodeError:\n        raise\n    if err_code == 0:\n        return BoolResponse()\n    raise\n\n\nasync def request(\n    blcpcore: BLCPCore,\n    room_id: int,\n    uk: int,\n    user_id: int,\n    origin_id: int,\n    name: str,\n    portrait: str,\n    text: str,\n    forum_id: int,\n    level: int,\n    vip: bool,\n    glevel: int,\n    atdata: list[dict] = None,\n    robot=-1,\n):\n    request_data = await construct_request_data(\n        blcpcore, room_id, uk, user_id, origin_id, name, portrait, text, forum_id, level, vip, glevel, atdata, robot\n    )\n    return await send_request(blcpcore, request_data)\n"
  },
  {
    "path": "src/aiotieba/api/send_msg/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/send_msg/_api.py",
    "content": "from ...core import WsCore\nfrom ...exception import TiebaServerError\nfrom .protobuf import CommitPersonalMsgReqIdl_pb2, CommitPersonalMsgResIdl_pb2\n\nCMD = 205001\n\n\ndef pack_proto(user_id: int, content: str, record_id: int) -> bytes:\n    req_proto = CommitPersonalMsgReqIdl_pb2.CommitPersonalMsgReqIdl()\n    req_proto.data.toUid = user_id\n    req_proto.data.content = content\n    req_proto.data.msgType = 1\n    req_proto.data.recordId = record_id\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> int:\n    res_proto = CommitPersonalMsgResIdl_pb2.CommitPersonalMsgResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n    if code := res_proto.data.blockInfo.blockErrno:\n        raise TiebaServerError(code, res_proto.data.blockInfo.blockErrmsg)\n\n    msg_id = res_proto.data.msgId\n    return msg_id\n\n\nasync def request(ws_core: WsCore, user_id: int, content: str) -> int:\n    data = pack_proto(user_id, content, ws_core.mid_manager.get_record_id())\n\n    resp = await ws_core.send(data, CMD)\n    msg_id = parse_body(await resp.read())\n\n    return msg_id\n"
  },
  {
    "path": "src/aiotieba/api/send_msg/protobuf/CommitPersonalMsgReqIdl.proto",
    "content": "// protobuf.CommitPersonalMsg.CommitPersonalMsgReqIdl\nsyntax = \"proto3\";\n\nmessage CommitPersonalMsgReqIdl {\n    message DataReq {\n        int64 toUid = 2;\n        int32 msgType = 3;\n        string content = 4;\n        int64 recordId = 6;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/send_msg/protobuf/CommitPersonalMsgReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1d\\x43ommitPersonalMsgReqIdl.proto\"\\x97\\x01\\n\\x17\\x43ommitPersonalMsgReqIdl\\x12.\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32 .CommitPersonalMsgReqIdl.DataReq\\x1aL\\n\\x07\\x44\\x61taReq\\x12\\r\\n\\x05toUid\\x18\\x02 \\x01(\\x03\\x12\\x0f\\n\\x07msgType\\x18\\x03 \\x01(\\x05\\x12\\x0f\\n\\x07\\x63ontent\\x18\\x04 \\x01(\\t\\x12\\x10\\n\\x08recordId\\x18\\x06 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"CommitPersonalMsgReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_COMMITPERSONALMSGREQIDL\"]._serialized_start = 34\n    _globals[\"_COMMITPERSONALMSGREQIDL\"]._serialized_end = 185\n    _globals[\"_COMMITPERSONALMSGREQIDL_DATAREQ\"]._serialized_start = 109\n    _globals[\"_COMMITPERSONALMSGREQIDL_DATAREQ\"]._serialized_end = 185\n"
  },
  {
    "path": "src/aiotieba/api/send_msg/protobuf/CommitPersonalMsgResIdl.proto",
    "content": "// protobuf.CommitPersonalMsg.CommitPersonalMsgResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage CommitPersonalMsgResIdl {\n    Error error = 1;\n    message DataRes {\n        int64 msgId = 1;\n        message BlockInfo {\n            int32 blockErrno = 1;\n            string blockErrmsg = 2;\n        }\n        BlockInfo blockInfo = 6;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/send_msg/protobuf/CommitPersonalMsgResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1d\\x43ommitPersonalMsgResIdl.proto\\x1a\\x0b\\x45rror.proto\"\\xf0\\x01\\n\\x17\\x43ommitPersonalMsgResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12.\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32 .CommitPersonalMsgResIdl.DataRes\\x1a\\x8d\\x01\\n\\x07\\x44\\x61taRes\\x12\\r\\n\\x05msgId\\x18\\x01 \\x01(\\x03\\x12=\\n\\tblockInfo\\x18\\x06 \\x01(\\x0b\\x32*.CommitPersonalMsgResIdl.DataRes.BlockInfo\\x1a\\x34\\n\\tBlockInfo\\x12\\x12\\n\\nblockErrno\\x18\\x01 \\x01(\\x05\\x12\\x13\\n\\x0b\\x62lockErrmsg\\x18\\x02 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"CommitPersonalMsgResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_COMMITPERSONALMSGRESIDL\"]._serialized_start = 47\n    _globals[\"_COMMITPERSONALMSGRESIDL\"]._serialized_end = 287\n    _globals[\"_COMMITPERSONALMSGRESIDL_DATARES\"]._serialized_start = 146\n    _globals[\"_COMMITPERSONALMSGRESIDL_DATARES\"]._serialized_end = 287\n    _globals[\"_COMMITPERSONALMSGRESIDL_DATARES_BLOCKINFO\"]._serialized_start = 235\n    _globals[\"_COMMITPERSONALMSGRESIDL_DATARES_BLOCKINFO\"]._serialized_end = 287\n"
  },
  {
    "path": "src/aiotieba/api/set_bawu_perm/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/set_bawu_perm/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...enums import BawuPermType\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import pack_json, parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n\ndef pack_perm_settings(perms: BawuPermType) -> list:\n    perm2id = [\n        (BawuPermType.UNBLOCK, 4),\n        (BawuPermType.UNBLOCK_APPEAL, 5),\n        (BawuPermType.RECOVER, 3),\n        (BawuPermType.RECOVER_APPEAL, 2),\n    ]\n    perm_settings = []\n\n    for perm, id_ in perm2id:\n        switch = 1 if perms & perm else 0\n        perm_setting = {\"switch\": switch, \"perm\": id_}\n        perm_settings.append(perm_setting)\n\n    return perm_settings\n\n\nasync def request(http_core: HttpCore, fid: int, portrait: str, perms: BawuPermType) -> BoolResponse:\n    data = [\n        (\"forum_id\", fid),\n        (\"auth_user_portrait\", portrait),\n        (\"perm_setting\", pack_json(pack_perm_settings(perms))),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/setAuthToolPerm\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/set_blacklist/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\n"
  },
  {
    "path": "src/aiotieba/api/set_blacklist/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import Account, HttpCore, WsCore\nfrom ...enums import BlacklistType\nfrom ...exception import BoolResponse, TiebaServerError\nfrom .protobuf import SetUserBlackReqIdl_pb2, SetUserBlackResIdl_pb2\n\nCMD = 309697\n\n\ndef pack_proto(account: Account, user_id: int, btype: BlacklistType) -> bytes:\n    req_proto = SetUserBlackReqIdl_pb2.SetUserBlackReqIdl()\n    req_proto.data.common.BDUSS = account.BDUSS\n    req_proto.data.common._client_type = 2\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.black_uid = user_id\n    req_proto.data.perm_list.follow = 1 if btype & BlacklistType.FOLLOW else 2\n    req_proto.data.perm_list.interact = 1 if btype & BlacklistType.INTERACT else 2\n    req_proto.data.perm_list.chat = 1 if btype & BlacklistType.CHAT else 2\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> None:\n    res_proto = SetUserBlackResIdl_pb2.SetUserBlackResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n\nasync def request_http(http_core: HttpCore, user_id: int, btype: BlacklistType) -> BoolResponse:\n    data = pack_proto(http_core.account, user_id, btype)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/user/setUserBlack\", query_string=f\"cmd={CMD}\"),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n\n\nasync def request_ws(ws_core: WsCore, user_id: int, btype: BlacklistType) -> BoolResponse:\n    data = pack_proto(ws_core.account, user_id, btype)\n\n    response = await ws_core.send(data, CMD)\n    parse_body(await response.read())\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/set_blacklist/protobuf/SetUserBlackReqIdl.proto",
    "content": "// tbclient.SetUserBlack.SetUserBlackReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage SetUserBlackReqIdl {\n    message DataReq {\n        CommonReq common = 1;\n        int64 black_uid = 2;\n        message PermissionList {\n            int32 follow = 1;\n            int32 interact = 2;\n            int32 chat = 3;\n        }\n        PermissionList perm_list = 3;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/set_blacklist/protobuf/SetUserBlackReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x18SetUserBlackReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\xfb\\x01\\n\\x12SetUserBlackReqIdl\\x12)\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32\\x1b.SetUserBlackReqIdl.DataReq\\x1a\\xb9\\x01\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x01 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\x11\\n\\tblack_uid\\x18\\x02 \\x01(\\x03\\x12=\\n\\tperm_list\\x18\\x03 \\x01(\\x0b\\x32*.SetUserBlackReqIdl.DataReq.PermissionList\\x1a@\\n\\x0ePermissionList\\x12\\x0e\\n\\x06\\x66ollow\\x18\\x01 \\x01(\\x05\\x12\\x10\\n\\x08interact\\x18\\x02 \\x01(\\x05\\x12\\x0c\\n\\x04\\x63hat\\x18\\x03 \\x01(\\x05\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"SetUserBlackReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_SETUSERBLACKREQIDL\"]._serialized_start = 46\n    _globals[\"_SETUSERBLACKREQIDL\"]._serialized_end = 297\n    _globals[\"_SETUSERBLACKREQIDL_DATAREQ\"]._serialized_start = 112\n    _globals[\"_SETUSERBLACKREQIDL_DATAREQ\"]._serialized_end = 297\n    _globals[\"_SETUSERBLACKREQIDL_DATAREQ_PERMISSIONLIST\"]._serialized_start = 233\n    _globals[\"_SETUSERBLACKREQIDL_DATAREQ_PERMISSIONLIST\"]._serialized_end = 297\n"
  },
  {
    "path": "src/aiotieba/api/set_blacklist/protobuf/SetUserBlackResIdl.proto",
    "content": "// tbclient.SetUserBlack.SetUserBlackResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage SetUserBlackResIdl {\n    Error error = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/set_blacklist/protobuf/SetUserBlackResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x18SetUserBlackResIdl.proto\\x1a\\x0b\\x45rror.proto\"+\\n\\x12SetUserBlackResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Errorb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"SetUserBlackResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_SETUSERBLACKRESIDL\"]._serialized_start = 41\n    _globals[\"_SETUSERBLACKRESIDL\"]._serialized_end = 84\n"
  },
  {
    "path": "src/aiotieba/api/set_msg_readed/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/set_msg_readed/_api.py",
    "content": "from ...core import WsCore\nfrom ...enums import MsgType\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ..get_group_msg import WsMessage\nfrom .protobuf import CommitReceivedPmsgReqIdl_pb2, CommitReceivedPmsgResIdl_pb2\n\nCMD = 205006\n\n\ndef pack_proto(user_id: int, group_id: int, msg_id: int) -> bytes:\n    req_proto = CommitReceivedPmsgReqIdl_pb2.CommitReceivedPmsgReqIdl()\n    req_proto.data.groupId = group_id\n    req_proto.data.toUid = user_id\n    req_proto.data.msgType = MsgType.READED\n    req_proto.data.msgId = msg_id\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> None:\n    res_proto = CommitReceivedPmsgResIdl_pb2.CommitReceivedPmsgResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n\nasync def request(ws_core: WsCore, message: WsMessage) -> BoolResponse:\n    user_id = message.user.user_id\n    msg_id = message.msg_id\n    data = pack_proto(user_id, ws_core.mid_manager.priv_gid, msg_id)\n\n    resp = await ws_core.send(data, CMD)\n    parse_body(await resp.read())\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/set_msg_readed/protobuf/CommitReceivedPmsgReqIdl.proto",
    "content": "// protobuf.CommitReceivedPmsg.CommitReceivedPmsgReqIdl\nsyntax = \"proto3\";\n\nmessage CommitReceivedPmsgReqIdl {\n    message DataReq {\n        int64 groupId = 1;\n        int64 toUid = 2;\n        int32 msgType = 3;\n        int64 msgId = 4;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/set_msg_readed/protobuf/CommitReceivedPmsgReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1e\\x43ommitReceivedPmsgReqIdl.proto\"\\x96\\x01\\n\\x18\\x43ommitReceivedPmsgReqIdl\\x12/\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32!.CommitReceivedPmsgReqIdl.DataReq\\x1aI\\n\\x07\\x44\\x61taReq\\x12\\x0f\\n\\x07groupId\\x18\\x01 \\x01(\\x03\\x12\\r\\n\\x05toUid\\x18\\x02 \\x01(\\x03\\x12\\x0f\\n\\x07msgType\\x18\\x03 \\x01(\\x05\\x12\\r\\n\\x05msgId\\x18\\x04 \\x01(\\x03\\x62\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"CommitReceivedPmsgReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_COMMITRECEIVEDPMSGREQIDL\"]._serialized_start = 35\n    _globals[\"_COMMITRECEIVEDPMSGREQIDL\"]._serialized_end = 185\n    _globals[\"_COMMITRECEIVEDPMSGREQIDL_DATAREQ\"]._serialized_start = 112\n    _globals[\"_COMMITRECEIVEDPMSGREQIDL_DATAREQ\"]._serialized_end = 185\n"
  },
  {
    "path": "src/aiotieba/api/set_msg_readed/protobuf/CommitReceivedPmsgResIdl.proto",
    "content": "// protobuf.CommitReceivedPmsg.CommitReceivedPmsgResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\n\nmessage CommitReceivedPmsgResIdl {\n    Error error = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/set_msg_readed/protobuf/CommitReceivedPmsgResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1e\\x43ommitReceivedPmsgResIdl.proto\\x1a\\x0b\\x45rror.proto\"1\\n\\x18\\x43ommitReceivedPmsgResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Errorb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"CommitReceivedPmsgResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_COMMITRECEIVEDPMSGRESIDL\"]._serialized_start = 47\n    _globals[\"_COMMITRECEIVEDPMSGRESIDL\"]._serialized_end = 96\n"
  },
  {
    "path": "src/aiotieba/api/set_nickname_old/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/set_nickname_old/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n\nasync def request(http_core: HttpCore, nick_name: str) -> BoolResponse:\n    params = [\n        (\"nickname\", nick_name),\n        (\"tbs\", 1),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/submit/modifyNickname\", query=params), []\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/set_profile/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/set_profile/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...enums import Gender\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, nick_name: str, sign: str, gender: Gender) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"intro\", sign),\n        (\"nick_name\", nick_name),\n        (\"sex\", int(gender)),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/profile/modify\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/set_thread_privacy/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/set_thread_privacy/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int, tid: int, pid: int, is_hide: bool) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"forum_id\", fid),\n        (\"is_hide\", str(int(is_hide))),\n        (\"post_id\", pid),\n        (\"thread_id\", tid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/thread/setPrivacy\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/sign_forum/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/sign_forum/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError, TiebaValueError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n    if int(res_json[\"user_info\"][\"sign_bonus_point\"]) == 0:\n        raise TiebaValueError(\"sign_bonus_point is 0\")\n\n\nasync def request(http_core: HttpCore, fname: str) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"kw\", fname),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/forum/sign\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/sign_forums/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/sign_forums/_api.py",
    "content": "import yarl\n\nfrom ...const import LATEST_VERSION, WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n    if \"error\" in res_json and (code := int(res_json[\"error\"][\"errno\"])):\n        raise TiebaServerError(code, res_json[\"error\"][\"errmsg\"])\n\n\nasync def request(http_core: HttpCore) -> BoolResponse:\n    data = [\n        (\"_client_version\", LATEST_VERSION),\n        (\"subapp_type\", \"hybrid\"),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/c/c/forum/msign\"),\n        data,\n        extra_headers=[(\"Subapp-Type\", \"hybrid\")],\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=2 * 1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/sign_growth/__init__.py",
    "content": "from ._api import parse_body_app, request_app, request_web\n"
  },
  {
    "path": "src/aiotieba/api/sign_growth/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body_web(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n\nasync def request_web(http_core: HttpCore, act_type: str) -> BoolResponse:\n    data = [\n        (\"tbs\", http_core.account.tbs),\n        (\"act_type\", act_type),\n        (\"cuid\", \"-\"),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/usergrowth/commitUGTaskInfo\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body_web(body)\n\n    return BoolResponse()\n\n\ndef parse_body_app(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request_app(http_core: HttpCore, act_type: str) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"act_type\", act_type),\n        (\"cuid\", \"-\"),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/user/commitUGTaskInfo\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body_app(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/sync/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/sync/_api.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nimport yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...exception import TiebaServerError\nfrom ...helper import parse_json\n\nif TYPE_CHECKING:\n    from ...core import HttpCore\n\n\ndef parse_body(body: bytes) -> tuple[str, str]:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n    client_id = res_json[\"client\"][\"client_id\"]\n    sample_id = res_json[\"wl_config\"][\"sample_id\"]\n\n    return client_id, sample_id\n\n\nasync def request(http_core: HttpCore) -> tuple[str, str]:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"_client_version\", LATEST_VERSION),\n        (\"cuid\", http_core.account.cuid_galaxy2),\n    ]\n\n    request = http_core.pack_form_request(yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/s/sync\"), data)\n\n    body = await http_core.net_core.send_request(request, read_bufsize=64 * 1024)\n    return parse_body(body)\n"
  },
  {
    "path": "src/aiotieba/api/tieba_uid2user_info/__init__.py",
    "content": "from ._api import CMD, pack_proto, parse_body, request_http, request_ws\nfrom ._classdef import UserInfo_TUid\n"
  },
  {
    "path": "src/aiotieba/api/tieba_uid2user_info/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST, LATEST_VERSION\nfrom ...core import HttpCore, WsCore\nfrom ...exception import TiebaServerError\nfrom ._classdef import UserInfo_TUid\nfrom .protobuf import GetUserByTiebaUidReqIdl_pb2, GetUserByTiebaUidResIdl_pb2\n\nCMD = 309702\n\n\ndef pack_proto(tieba_uid: int) -> bytes:\n    req_proto = GetUserByTiebaUidReqIdl_pb2.GetUserByTiebaUidReqIdl()\n    req_proto.data.common._client_version = LATEST_VERSION\n    req_proto.data.tieba_uid = str(tieba_uid)\n\n    return req_proto.SerializeToString()\n\n\ndef parse_body(body: bytes) -> UserInfo_TUid:\n    res_proto = GetUserByTiebaUidResIdl_pb2.GetUserByTiebaUidResIdl()\n    res_proto.ParseFromString(body)\n\n    if code := res_proto.error.errorno:\n        raise TiebaServerError(code, res_proto.error.errmsg)\n\n    user_proto = res_proto.data.user\n    user = UserInfo_TUid.from_proto(user_proto)\n\n    return user\n\n\nasync def request_http(http_core: HttpCore, tieba_uid: int) -> UserInfo_TUid:\n    data = pack_proto(tieba_uid)\n\n    request = http_core.pack_proto_request(\n        yarl.URL.build(\n            scheme=\"http\",\n            host=APP_BASE_HOST,\n            path=\"/c/u/user/getUserByTiebaUid\",\n            query_string=f\"cmd={CMD}\",\n        ),\n        data,\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    return parse_body(body)\n\n\nasync def request_ws(ws_core: WsCore, tieba_uid: int) -> UserInfo_TUid:\n    data = pack_proto(tieba_uid)\n\n    response = await ws_core.send(data, CMD)\n    return parse_body(await response.read())\n"
  },
  {
    "path": "src/aiotieba/api/tieba_uid2user_info/_classdef.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nfrom typing import TYPE_CHECKING\n\nfrom ...exception import TbErrorExt\n\nif TYPE_CHECKING:\n    from .._classdef import TypeMessage\n\n\n@dcs.dataclass\nclass UserInfo_TUid(TbErrorExt):\n    \"\"\"\n    用户信息\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n\n        user_id (int): user_id\n        portrait (str): portrait\n        user_name (str): 用户名\n        nick_name_new (str): 新版昵称\n        tieba_uid (int): 用户个人主页uid\n\n        age (float): 吧龄\n        sign (str): 个性签名\n\n        is_god (bool): 是否大神\n\n        nick_name (str): 用户昵称\n        show_name (str): 显示名称\n        log_name (str): 用于在日志中记录用户信息\n    \"\"\"\n\n    user_id: int = 0\n    portrait: str = \"\"\n    user_name: str = \"\"\n    nick_name_new: str = \"\"\n    tieba_uid: int = 0\n\n    age: float = 0.0\n    sign: str = \"\"\n\n    is_god: bool = False\n\n    @staticmethod\n    def from_proto(data_proto: TypeMessage) -> UserInfo_TUid:\n        user_id = data_proto.id\n        portrait = data_proto.portrait\n        if \"?\" in portrait:\n            portrait = portrait[:-13]\n        user_name = data_proto.name\n        nick_name_new = data_proto.name_show\n        tieba_uid = int(data_proto.tieba_uid)\n        age = float(data_proto.tb_age)\n        sign = data_proto.intro\n        is_god = bool(data_proto.new_god_data.status)\n        return UserInfo_TUid(user_id, portrait, user_name, nick_name_new, tieba_uid, age, sign, is_god)\n\n    def __str__(self) -> str:\n        return self.user_name or self.portrait or str(self.user_id)\n\n    def __eq__(self, obj: UserInfo_TUid) -> bool:\n        return self.user_id == obj.user_id\n\n    def __hash__(self) -> int:\n        return self.user_id\n\n    def __bool__(self) -> bool:\n        return bool(self.user_id)\n\n    @property\n    def nick_name(self) -> str:\n        return self.nick_name_new\n\n    @property\n    def show_name(self) -> str:\n        return self.nick_name_new or self.user_name\n\n    @property\n    def log_name(self) -> str:\n        if self.user_name:\n            return self.user_name\n        elif self.portrait:\n            return f\"{self.nick_name_new}/{self.portrait}\"\n        else:\n            return str(self.user_id)\n"
  },
  {
    "path": "src/aiotieba/api/tieba_uid2user_info/protobuf/GetUserByTiebaUidReqIdl.proto",
    "content": "// tbclient.GetUserByTiebaUid.GetUserByTiebaUidReqIdl\nsyntax = \"proto3\";\n\nimport \"CommonReq.proto\";\n\nmessage GetUserByTiebaUidReqIdl {\n    message DataReq {\n        CommonReq common = 1;\n        string tieba_uid = 2;\n    }\n    DataReq data = 1;\n}\n"
  },
  {
    "path": "src/aiotieba/api/tieba_uid2user_info/protobuf/GetUserByTiebaUidReqIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import CommonReq_pb2 as CommonReq__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1dGetUserByTiebaUidReqIdl.proto\\x1a\\x0f\\x43ommonReq.proto\"\\x83\\x01\\n\\x17GetUserByTiebaUidReqIdl\\x12.\\n\\x04\\x64\\x61ta\\x18\\x01 \\x01(\\x0b\\x32 .GetUserByTiebaUidReqIdl.DataReq\\x1a\\x38\\n\\x07\\x44\\x61taReq\\x12\\x1a\\n\\x06\\x63ommon\\x18\\x01 \\x01(\\x0b\\x32\\n.CommonReq\\x12\\x11\\n\\ttieba_uid\\x18\\x02 \\x01(\\tb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetUserByTiebaUidReqIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETUSERBYTIEBAUIDREQIDL\"]._serialized_start = 51\n    _globals[\"_GETUSERBYTIEBAUIDREQIDL\"]._serialized_end = 182\n    _globals[\"_GETUSERBYTIEBAUIDREQIDL_DATAREQ\"]._serialized_start = 126\n    _globals[\"_GETUSERBYTIEBAUIDREQIDL_DATAREQ\"]._serialized_end = 182\n"
  },
  {
    "path": "src/aiotieba/api/tieba_uid2user_info/protobuf/GetUserByTiebaUidResIdl.proto",
    "content": "// tbclient.GetUserByTiebaUid.GetUserByTiebaUidResIdl\nsyntax = \"proto3\";\n\nimport \"Error.proto\";\nimport \"User.proto\";\n\nmessage GetUserByTiebaUidResIdl {\n    Error error = 1;\n    message DataRes {\n        User user = 1;\n    }\n    DataRes data = 2;\n}\n"
  },
  {
    "path": "src/aiotieba/api/tieba_uid2user_info/protobuf/GetUserByTiebaUidResIdl_pb2.py",
    "content": "\"\"\"Generated protocol buffer code.\"\"\"\n\nfrom google.protobuf import descriptor as _descriptor\nfrom google.protobuf import descriptor_pool as _descriptor_pool\nfrom google.protobuf import symbol_database as _symbol_database\nfrom google.protobuf.internal import builder as _builder\n\n_sym_db = _symbol_database.Default()\n\n\nfrom ..._protobuf import Error_pb2 as Error__pb2\nfrom ..._protobuf import User_pb2 as User__pb2\n\nDESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(\n    b'\\n\\x1dGetUserByTiebaUidResIdl.proto\\x1a\\x0b\\x45rror.proto\\x1a\\nUser.proto\"\\x80\\x01\\n\\x17GetUserByTiebaUidResIdl\\x12\\x15\\n\\x05\\x65rror\\x18\\x01 \\x01(\\x0b\\x32\\x06.Error\\x12.\\n\\x04\\x64\\x61ta\\x18\\x02 \\x01(\\x0b\\x32 .GetUserByTiebaUidResIdl.DataRes\\x1a\\x1e\\n\\x07\\x44\\x61taRes\\x12\\x13\\n\\x04user\\x18\\x01 \\x01(\\x0b\\x32\\x05.Userb\\x06proto3'\n)\n\n_globals = globals()\n_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)\n_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, \"GetUserByTiebaUidResIdl_pb2\", _globals)\nif not _descriptor._USE_C_DESCRIPTORS:\n    DESCRIPTOR._loaded_options = None\n    _globals[\"_GETUSERBYTIEBAUIDRESIDL\"]._serialized_start = 59\n    _globals[\"_GETUSERBYTIEBAUIDRESIDL\"]._serialized_end = 187\n    _globals[\"_GETUSERBYTIEBAUIDRESIDL_DATARES\"]._serialized_start = 157\n    _globals[\"_GETUSERBYTIEBAUIDRESIDL_DATARES\"]._serialized_end = 187\n"
  },
  {
    "path": "src/aiotieba/api/top/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/top/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fname: str, fid: int, tid: int, is_vip: bool, is_set: bool) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"fid\", fid),\n        (\"is_member_top\", int(is_vip)),\n        (\"ntn\", \"set\" if is_set else \"\"),\n        (\"tbs\", http_core.account.tbs),\n        (\"word\", fname),\n        (\"z\", tid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/committop\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/unblock/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/unblock/_api.py",
    "content": "import yarl\n\nfrom ...const import WEB_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := res_json[\"no\"]:\n        raise TiebaServerError(code, res_json[\"error\"])\n\n\nasync def request(http_core: HttpCore, fid: int, user_id: int) -> BoolResponse:\n    data = [\n        (\"fn\", \"-\"),\n        (\"fid\", fid),\n        (\"block_un\", \"-\"),\n        (\"block_uid\", user_id),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_web_form_request(\n        yarl.URL.build(scheme=\"https\", host=WEB_BASE_HOST, path=\"/mo/q/bawublockclear\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/undislike_forum/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/undislike_forum/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"cuid\", http_core.account.cuid),\n        (\"forum_id\", fid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/excellent/submitCancelDislike\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/unfollow_forum/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/unfollow_forum/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fid: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"fid\", fid),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/forum/unfavolike\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/unfollow_user/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/unfollow_user/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, portrait: str) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"portrait\", portrait),\n        (\"tbs\", http_core.account.tbs),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/user/unfollow\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/api/ungood/__init__.py",
    "content": "from ._api import parse_body, request\n"
  },
  {
    "path": "src/aiotieba/api/ungood/_api.py",
    "content": "import yarl\n\nfrom ...const import APP_BASE_HOST\nfrom ...core import HttpCore\nfrom ...exception import BoolResponse, TiebaServerError\nfrom ...helper import parse_json\n\n\ndef parse_body(body: bytes) -> None:\n    res_json = parse_json(body)\n    if code := int(res_json[\"error_code\"]):\n        raise TiebaServerError(code, res_json[\"error_msg\"])\n\n\nasync def request(http_core: HttpCore, fname: str, fid: int, tid: int) -> BoolResponse:\n    data = [\n        (\"BDUSS\", http_core.account.BDUSS),\n        (\"fid\", fid),\n        (\"tbs\", http_core.account.tbs),\n        (\"word\", fname),\n        (\"z\", tid),\n    ]\n\n    request = http_core.pack_form_request(\n        yarl.URL.build(scheme=\"https\", host=APP_BASE_HOST, path=\"/c/c/bawu/commitgood\"), data\n    )\n\n    body = await http_core.net_core.send_request(request, read_bufsize=1024)\n    parse_body(body)\n\n    return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/client.py",
    "content": "from __future__ import annotations\n\nimport logging\nimport socket\nfrom typing import TYPE_CHECKING, Literal\n\nimport aiohttp\nimport yarl\n\nfrom .api import (\n    add_bawu,\n    add_bawu_blacklist,\n    add_blacklist_old,\n    add_post,\n    agree,\n    block,\n    del_bawu,\n    del_bawu_blacklist,\n    del_blacklist_old,\n    del_post,\n    del_posts,\n    del_thread,\n    del_threads,\n    dislike_forum,\n    follow_forum,\n    follow_user,\n    get_ats,\n    get_bawu_blacklist,\n    get_bawu_info,\n    get_bawu_perm,\n    get_bawu_postlogs,\n    get_bawu_userlogs,\n    get_blacklist,\n    get_blacklist_old,\n    get_blocks,\n    get_cid,\n    get_comments,\n    get_dislike_forums,\n    get_fans,\n    get_fid,\n    get_follow_forums,\n    get_follows,\n    get_forum,\n    get_forum_detail,\n    get_forum_level,\n    get_group_msg,\n    get_images,\n    get_last_replyers,\n    get_member_users,\n    get_posts,\n    get_rank_forums,\n    get_rank_users,\n    get_recom_status,\n    get_recovers,\n    get_replys,\n    get_roomlist_by_fid,\n    get_self_follow_forums,\n    get_selfinfo_initNickname,\n    get_selfinfo_moindex,\n    get_square_forums,\n    get_statistics,\n    get_tab_map,\n    get_threads,\n    get_uinfo_getuserinfo_app,\n    get_uinfo_getUserInfo_web,\n    get_uinfo_panel,\n    get_uinfo_user_json,\n    get_unblock_appeals,\n    get_user_contents,\n    get_user_forum_info,\n    good,\n    handle_unblock_appeals,\n    init_z_id,\n    login,\n    move,\n    profile,\n    recommend,\n    recover,\n    remove_fan,\n    search_exact,\n    send_chatroom_msg,\n    send_msg,\n    set_bawu_perm,\n    set_blacklist,\n    set_msg_readed,\n    set_nickname_old,\n    set_profile,\n    set_thread_privacy,\n    sign_forum,\n    sign_forums,\n    sign_growth,\n    sync,\n    tieba_uid2user_info,\n    top,\n    unblock,\n    undislike_forum,\n    unfollow_forum,\n    unfollow_user,\n    ungood,\n)\nfrom .api._classdef import UserInfo\nfrom .config import ProxyConfig, TimeoutConfig\nfrom .const import LATEST_VERSION, STABLE_VERSION\nfrom .core import Account, BLCPCore, HttpCore, NetCore, WsCore\nfrom .enums import (\n    BawuPermType,\n    BawuSearchType,\n    BawuType,\n    BlacklistType,\n    Gender,\n    GroupType,\n    PostSortType,\n    RankForumType,\n    ReqUInfo,\n    SearchType,\n    ThreadSortType,\n    WsStatus,\n)\nfrom .exception import BoolResponse, IntResponse, StrResponse\nfrom .helper import deprecated\nfrom .helper.cache import ForumInfoCache\nfrom .helper.utils import handle_exception, is_portrait, is_user_name\nfrom .logging import get_logger as LOG\n\nif TYPE_CHECKING:\n    import asyncio\n    import datetime\n\n\ndef _try_websocket(func):\n    async def awrapper(self: Client, *args, **kwargs):\n        if self._try_ws:\n            await self.init_websocket()\n        return await func(self, *args, **kwargs)\n\n    awrapper.__name__ = func.__name__\n\n    return awrapper\n\n\ndef _force_websocket(func):\n    async def awrapper(self: Client, *args, **kwargs):\n        await self.init_websocket()\n        return await func(self, *args, **kwargs)\n\n    awrapper.__name__ = func.__name__\n\n    return awrapper\n\n\nclass Client:\n    \"\"\"\n    贴吧客户端\n\n    Args:\n        BDUSS (str, optional): BDUSS. Defaults to ''.\n        STOKEN (str, optional): STOKEN. Defaults to ''.\n        account (Account, optional): Account实例 该字段会覆盖前两个参数. Defaults to None.\n        try_ws (bool, optional): 尝试使用websocket接口. Defaults to False.\n        proxy (bool | ProxyConfig, optional): True则使用环境变量代理 False则禁用代理 输入ProxyConfig实例以手动配置代理. Defaults to False.\n        timeout (TimeoutConfig, optional): 超时配置. Defaults to None.\n    \"\"\"\n\n    __slots__ = [\n        \"_account\",\n        \"_timeout\",\n        \"_proxy\",\n        \"_try_ws\",\n        \"_connector\",\n        \"_http_core\",\n        \"_ws_core\",\n        \"_user\",\n        \"_blcp_core\",\n    ]\n\n    def __init__(\n        self,\n        BDUSS: str = \"\",\n        STOKEN: str = \"\",\n        *,\n        account: Account | None = None,\n        try_ws: bool = False,\n        proxy: bool | ProxyConfig = False,\n        timeout: TimeoutConfig | None = None,\n    ) -> None:\n        if not isinstance(account, Account):\n            account = Account(BDUSS, STOKEN)\n        self._account = account\n\n        if not isinstance(timeout, TimeoutConfig):\n            timeout = TimeoutConfig()\n        self._timeout = timeout\n\n        if proxy is True:\n            proxy = ProxyConfig.from_env()\n        elif not proxy:\n            proxy = ProxyConfig()\n        self._proxy = proxy\n\n        self._try_ws = try_ws\n\n        self._user = UserInfo()\n\n    async def __aenter__(self) -> Client:\n        connector = aiohttp.TCPConnector(\n            ttl_dns_cache=self._timeout.dns_ttl,\n            family=socket.AF_INET,\n            keepalive_timeout=self._timeout.http_keepalive,\n            limit=0,\n            ssl=False,\n        )\n        self._connector = connector\n\n        net_core = NetCore(connector, self._proxy, self._timeout)\n        self._http_core = HttpCore(self._account, net_core)\n        self._ws_core = WsCore(self._account, net_core)\n        self._blcp_core = BLCPCore(account=self._account, net_core=net_core, user=self._user)\n\n        return self\n\n    async def __aexit__(self, exc_type=None, exc_val=None, exc_tb=None) -> None:\n        await self._ws_core.close()\n        await self._connector.close()\n\n    def __hash__(self) -> int:\n        return hash(self.account)\n\n    def __eq__(self, obj: Client) -> bool:\n        return self.account == obj.account\n\n    @property\n    def account(self) -> Account:\n        return self._http_core.account\n\n    @account.setter\n    def account(self, new_account: Account) -> None:\n        self._http_core.set_account(new_account)\n        self._ws_core.set_account(new_account)\n\n    @handle_exception(BoolResponse)\n    async def init_websocket(self) -> BoolResponse:\n        \"\"\"\n        初始化websocket\n\n        Returns:\n            BoolResponse: True无须执行 False失败\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.CLOSED:\n            try:\n                await self._ws_core.connect()\n                await self.__upload_sec_key()\n            except BaseException:\n                self._ws_core._status = WsStatus.CLOSED\n                raise\n\n        return BoolResponse()\n\n    async def __upload_sec_key(self) -> None:\n        from .api import init_websocket\n        from .core.websocket import MsgIDPair\n\n        groups = await init_websocket.request(self._ws_core)\n\n        mid_manager = self._ws_core.mid_manager\n        for group in groups:\n            if group.group_type == GroupType.PRIVATE_MSG:\n                mid_manager.priv_gid = group.group_id\n        mid_manager.gid2mid |= {g.group_id: MsgIDPair(g.last_msg_id, g.last_msg_id) for g in groups}\n\n        self._ws_core._status = WsStatus.OPEN\n\n    async def __init_tbs(self) -> None:\n        if self.account.tbs:\n            return\n        await self.__login()\n\n    @handle_exception(UserInfo)\n    async def get_self_info(self, require: ReqUInfo = ReqUInfo.ALL) -> UserInfo:\n        \"\"\"\n        获取本账号信息\n\n        Args:\n            require (ReqUInfo): 指示需要获取的字段\n\n        Returns:\n            TypeUserInfo: 用户信息\n        \"\"\"\n\n        if not self._user.user_id:\n            if require & ReqUInfo.BASIC:\n                await self.__login()\n        if not self._user.tieba_uid:\n            if require == ReqUInfo.ALL:\n                user = await self._get_uinfo_profile(self._user.user_id)\n                self._user |= user\n            elif require & (ReqUInfo.TIEBA_UID | ReqUInfo.NICK_NAME):\n                await self.__get_selfinfo_initNickname()\n\n        return self._user\n\n    async def __login(self) -> None:\n        user, tbs = await login.request(self._http_core)\n\n        self._user |= user\n        self.account.tbs = tbs\n\n    async def __init_client_id(self) -> None:\n        if self.account.client_id:\n            return\n        await self.__sync()\n\n    async def __init_sample_id(self) -> None:\n        if self.account.sample_id:\n            return\n        await self.__sync()\n\n    async def __sync(self) -> None:\n        client_id, sample_id = await sync.request(self._http_core)\n        self.account.client_id = client_id\n        self.account.sample_id = sample_id\n\n    async def __init_z_id(self) -> None:\n        if self.account.z_id:\n            return\n\n        z_id = await init_z_id.request(self._http_core)\n        self.account.z_id = z_id\n\n    @handle_exception(get_forum.Forum)\n    async def get_forum(self, fname_or_fid: str | int) -> get_forum.Forum:\n        \"\"\"\n        通过forum_id获取贴吧信息\n        此接口较`get_forum_detail`更强大\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先贴吧名\n\n        Returns:\n            Forum: 贴吧信息\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        return await get_forum.request(self._http_core, fname)\n\n    @handle_exception(get_forum_detail.Forum_detail)\n    @_try_websocket\n    async def get_forum_detail(self, fname_or_fid: str | int) -> get_forum_detail.Forum_detail:\n        \"\"\"\n        通过forum_id获取贴吧信息\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n\n        Returns:\n            Forum_detail: 贴吧信息\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_forum_detail.request_ws(self._ws_core, fid)\n\n        return await get_forum_detail.request_http(self._http_core, fid)\n\n    async def __get_fid(self, fname: str) -> int:\n        if fid := ForumInfoCache.get_fid(fname):\n            return fid\n\n        fid = await get_fid.request(self._http_core, fname)\n        ForumInfoCache.add_forum(fname, fid)\n\n        return fid\n\n    @handle_exception(IntResponse)\n    async def get_fid(self, fname: str) -> IntResponse:\n        \"\"\"\n        通过贴吧名获取forum_id\n\n        Args:\n            fname (str): 贴吧名\n\n        Returns:\n            IntResponse: forum_id\n        \"\"\"\n\n        fid = await self.__get_fid(fname)\n        return IntResponse(fid)\n\n    async def __get_fname(self, fid: int) -> str:\n        if fname := ForumInfoCache.get_fname(fid):\n            return fname\n\n        fdetail = await self.get_forum_detail(fid)\n        fname = fdetail.fname\n\n        if fname:\n            ForumInfoCache.add_forum(fname, fid)\n\n        return fname\n\n    @handle_exception(StrResponse)\n    async def get_fname(self, fid: int) -> StrResponse:\n        \"\"\"\n        通过forum_id获取贴吧名\n\n        Args:\n            fid (int): forum_id\n\n        Returns:\n            StrResponse: 贴吧名\n        \"\"\"\n\n        fname = await self.__get_fname(fid)\n        return StrResponse(fname)\n\n    @handle_exception(get_threads.Threads)\n    @_try_websocket\n    async def get_threads(\n        self,\n        fname_or_fid: str | int,\n        /,\n        pn: int = 1,\n        *,\n        rn: int = 30,\n        sort: ThreadSortType = ThreadSortType.REPLY,\n        is_good: bool = False,\n    ) -> get_threads.Threads:\n        \"\"\"\n        获取首页帖子\n\n        Args:\n            fname_or_fid (str | int): 贴吧名或fid 优先贴吧名\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 30. Max to 100.\n            sort (ThreadSortType, optional): HOT热门排序 REPLY按回复时间 CREATE按发布时间 FOLLOW关注的人. Defaults to ThreadSortType.REPLY.\n            is_good (bool, optional): True则获取精品区帖子 False则获取普通区帖子. Defaults to False.\n\n        Returns:\n            Threads: 帖子列表\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_threads.request_ws(self._ws_core, fname, pn, rn, sort, is_good, STABLE_VERSION)\n\n        return await get_threads.request_http(self._http_core, fname, pn, rn, sort, is_good, STABLE_VERSION)\n\n    @handle_exception(get_posts.Posts)\n    @_try_websocket\n    async def get_posts(\n        self,\n        tid: int,\n        /,\n        pn: int = 1,\n        *,\n        rn: int = 30,\n        sort: PostSortType = PostSortType.ASC,\n        only_thread_author: bool = False,\n        with_comments: bool = False,\n        comment_sort_by_agree: bool = True,\n        comment_rn: int = 4,\n    ) -> get_posts.Posts:\n        \"\"\"\n        获取主题帖内回复\n\n        Args:\n            tid (int): 所在主题帖tid\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 30.\n            sort (PostSortType, optional): ASC时间顺序 DESC时间倒序 HOT热门序. Defaults to PostSortType.ASC.\n            only_thread_author (bool, optional): True则只看楼主 False则请求全部. Defaults to False.\n            with_comments (bool, optional): True则同时请求高赞楼中楼 False则返回的Post.comments字段为空. Defaults to False.\n            comment_sort_by_agree (bool, optional): True则楼中楼按点赞数顺序 False则楼中楼按时间顺序. Defaults to True.\n            comment_rn (int, optional): 请求的楼中楼数量. Defaults to 4. Max to 50.\n\n        Returns:\n            Posts: 回复列表\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_posts.request_ws(\n                self._ws_core, tid, pn, rn, sort, only_thread_author, with_comments, comment_sort_by_agree, comment_rn\n            )\n\n        return await get_posts.request_http(\n            self._http_core, tid, pn, rn, sort, only_thread_author, with_comments, comment_sort_by_agree, comment_rn\n        )\n\n    @handle_exception(get_comments.Comments)\n    @_try_websocket\n    async def get_comments(\n        self, tid: int, pid: int, /, pn: int = 1, *, is_comment: bool = False\n    ) -> get_comments.Comments:\n        \"\"\"\n        获取楼中楼回复\n\n        Args:\n            tid (int): 所在主题帖tid\n            pid (int): 所在楼层的pid或楼中楼的pid\n            pn (int, optional): 页码. Defaults to 1.\n            is_comment (bool, optional): pid是否指向楼中楼 若指向楼中楼则获取其附近的楼中楼列表. Defaults to False.\n\n        Returns:\n            Comments: 楼中楼列表\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_comments.request_ws(self._ws_core, tid, pid, pn, is_comment)\n\n        return await get_comments.request_http(self._http_core, tid, pid, pn, is_comment)\n\n    @handle_exception(get_last_replyers.Threads_lp)\n    @_try_websocket\n    async def get_last_replyers(\n        self,\n        fname_or_fid: str | int,\n        /,\n        pn: int = 1,\n        *,\n        rn: int = 30,\n        sort: ThreadSortType = ThreadSortType.REPLY,\n        is_good: bool = False,\n    ) -> get_last_replyers.Threads_lp:\n        \"\"\"\n        通过旧版接口获取带最后回复人的首页帖子\n\n        Args:\n            fname_or_fid (str | int): 贴吧名或fid 优先贴吧名\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 30. Max to 100.\n            sort (ThreadSortType, optional): HOT热门排序 REPLY按回复时间 CREATE按发布时间 FOLLOW关注的人. Defaults to ThreadSortType.REPLY.\n            is_good (bool, optional): True则获取精品区帖子 False则获取普通区帖子. Defaults to False.\n\n        Returns:\n            Threads_lp: 带最后回复人的帖子列表\n\n        Note:\n            该接口主要用于反挖坟 目前未封装完整的返回信息\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_last_replyers.request_ws(self._ws_core, fname, pn, rn, sort, is_good)\n\n        return await get_last_replyers.request_http(self._http_core, fname, pn, rn, sort, is_good)\n\n    @handle_exception(search_exact.ExactSearches)\n    async def search_exact(\n        self,\n        fname_or_fid: str | int,\n        query: str,\n        /,\n        pn: int = 1,\n        *,\n        rn: int = 30,\n        search_type: SearchType = SearchType.ALL,\n        only_thread: bool = False,\n    ) -> search_exact.ExactSearches:\n        \"\"\"\n        贴吧搜索\n\n        Args:\n            fname_or_fid (str | int): 查询的贴吧名或fid 优先贴吧名\n            query (str): 查询文本\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 30.\n            search_type (SearchType, optional): 查询模式 默认查询全部. Defaults to SearchType.ALL.\n            only_thread (bool, optional): 是否仅查询主题帖. Defaults to False.\n\n        Returns:\n            ExactSearches: 搜索结果列表\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        return await search_exact.request(self._http_core, fname, query, pn, rn, search_type, only_thread)\n\n    @handle_exception(profile.UserInfo_pf)\n    @_try_websocket\n    async def _get_uinfo_profile(self, uid_or_portrait: str | int) -> profile.UserInfo_pf:\n        \"\"\"\n        接口 https://tiebac.baidu.com/c/u/user/profile\n\n        Args:\n            uid_or_portrait (str | int): 用户id user_id / portrait\n\n        Returns:\n            UserInfo_pf: 包含最全面的用户信息\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await profile.get_uinfo_profile.request_ws(self._ws_core, uid_or_portrait)\n\n        return await profile.get_uinfo_profile.request_http(self._http_core, uid_or_portrait)\n\n    @handle_exception(get_uinfo_getuserinfo_app.UserInfo_guinfo_app)\n    @_try_websocket\n    async def _get_uinfo_getuserinfo(self, user_id: int) -> get_uinfo_getuserinfo_app.UserInfo_guinfo_app:\n        \"\"\"\n        接口 https://tiebac.baidu.com/c/u/user/getuserinfo\n\n        Args:\n            user_id (int): 用户id user_id\n\n        Returns:\n            UserInfo_guinfo_app: 包含 user_id / portrait / user_name / nick_name_old / 性别 /\n                是否大神 / 是否超级会员\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            user = await get_uinfo_getuserinfo_app.request_ws(self._ws_core, user_id)\n\n        else:\n            user = await get_uinfo_getuserinfo_app.request_http(self._http_core, user_id)\n            if (user_id := user.user_id) < 0:\n                user.user_id = 0xFFFFFFFF + user_id\n\n        return user\n\n    @handle_exception(get_uinfo_getUserInfo_web.UserInfo_guinfo_web)\n    async def _get_uinfo_getUserInfo(self, user_id: int) -> get_uinfo_getUserInfo_web.UserInfo_guinfo_web:\n        \"\"\"\n        接口 http://tieba.baidu.com/im/pcmsg/query/getUserInfo\n\n        Args:\n            user_id (int): 用户id user_id\n\n        Returns:\n            UserInfo_guinfo_web: 包含 user_id / portrait / user_name / nick_name_new\n\n        Note:\n            该接口需要BDUSS\n        \"\"\"\n\n        user = await get_uinfo_getUserInfo_web.request(self._http_core, user_id)\n        user.user_id = user_id\n\n        return user\n\n    @handle_exception(get_uinfo_user_json.UserInfo_json)\n    async def _get_uinfo_user_json(self, user_name: str) -> get_uinfo_user_json.UserInfo_json:\n        \"\"\"\n        接口 http://tieba.baidu.com/i/sys/user_json\n\n        Args:\n            user_name (str): 用户id user_name\n\n        Returns:\n            UserInfo_json: 包含 user_id / portrait / user_name\n        \"\"\"\n\n        user = await get_uinfo_user_json.request(self._http_core, user_name)\n        user.user_name = user_name\n\n        return user\n\n    @handle_exception(get_uinfo_panel.UserInfo_panel)\n    async def _get_uinfo_panel(self, name_or_portrait: str) -> get_uinfo_panel.UserInfo_panel:\n        \"\"\"\n        接口 https://tieba.baidu.com/home/get/panel\n\n        Args:\n            name_or_portrait (str): 用户id user_name / portrait\n\n        Returns:\n            UserInfo_panel: 包含较全面的用户信息\n\n        Note:\n            从2022.08.30开始服务端不再返回user_id字段 请谨慎使用\\n\n            该接口可判断用户是否被屏蔽\\n\n            该接口rps阈值较低\n        \"\"\"\n\n        return await get_uinfo_panel.request(self._http_core, name_or_portrait)\n\n    async def get_user_info(self, id_: str | int, /, require: ReqUInfo = ReqUInfo.ALL) -> UserInfo:\n        \"\"\"\n        获取用户信息\n\n        Args:\n            id_ (str | int): 用户id user_id / portrait / user_name\n            require (ReqUInfo): 指示需要获取的字段\n\n        Returns:\n            UserInfo: 用户信息\n        \"\"\"\n\n        if not id_:\n            LOG().warning(\"Null input\")\n            return UserInfo()\n\n        if isinstance(id_, int):\n            if (require | ReqUInfo.BASIC) == ReqUInfo.BASIC:\n                # 仅有BASIC需求\n                return await self._get_uinfo_getuserinfo(id_)\n            else:\n                return await self._get_uinfo_profile(id_)\n        elif is_portrait(id_):\n            if (require | ReqUInfo.BASIC) == ReqUInfo.BASIC:\n                # 仅有BASIC需求\n                if not require & ReqUInfo.USER_ID:\n                    # 无USER_ID需求\n                    return await self._get_uinfo_panel(id_)\n            return await self._get_uinfo_profile(id_)\n        else:\n            if (require | ReqUInfo.BASIC) == ReqUInfo.BASIC:\n                return await self._get_uinfo_user_json(id_)\n            elif require & ReqUInfo.NICK_NAME and not require & ReqUInfo.USER_ID:\n                # 有NICK_NAME需求但无USER_ID需求\n                return await self._get_uinfo_panel(id_)\n            else:\n                user = await self._get_uinfo_user_json(id_)\n                return await self._get_uinfo_profile(user.portrait)\n\n    @handle_exception(tieba_uid2user_info.UserInfo_TUid)\n    @_try_websocket\n    async def tieba_uid2user_info(self, tieba_uid: int) -> tieba_uid2user_info.UserInfo_TUid:\n        \"\"\"\n        通过tieba_uid获取用户信息\n\n        Args:\n            tieba_uid (int): 用户id tieba_uid\n\n        Returns:\n            UserInfo_TUid: 包含较全面的用户信息\n\n        Note:\n            请注意tieba_uid与旧版user_id的区别\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await tieba_uid2user_info.request_ws(self._ws_core, tieba_uid)\n\n        return await tieba_uid2user_info.request_http(self._http_core, tieba_uid)\n\n    @handle_exception(profile.Homepage)\n    @_try_websocket\n    async def get_homepage(self, id_: str | int, /, pn: int = 1) -> profile.Homepage:\n        \"\"\"\n        获取用户个人页信息\n\n        Args:\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_id\n            pn (int, optional): 页码. Defaults to 1.\n\n        Returns:\n            Homepage: 用户个人页信息\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await profile.get_homepage.request_ws(self._ws_core, user_id, pn)\n\n        return await profile.get_homepage.request_http(self._http_core, user_id, pn)\n\n    @handle_exception(get_follows.Follows)\n    async def get_follows(self, id_: str | int | None = None, /, pn: int = 1) -> get_follows.Follows:\n        \"\"\"\n        获取关注列表\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n            id_ (str | int | None): 用户id user_id / user_name / portrait 优先user_id\n                默认为None即获取本账号信息. Defaults to None.\n\n        Returns:\n            Follows: 关注列表\n        \"\"\"\n\n        if id_ is None:\n            user = await self.get_self_info(ReqUInfo.USER_ID)\n            user_id = user.user_id\n        elif not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        return await get_follows.request(self._http_core, user_id, pn)\n\n    @handle_exception(get_fans.Fans)\n    async def get_fans(self, id_: str | int | None = None, /, pn: int = 1) -> get_fans.Fans:\n        \"\"\"\n        获取粉丝列表\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n            id_ (str | int | None): 用户id user_id / user_name / portrait 优先user_id\n                默认为None即获取本账号信息. Defaults to None.\n\n        Returns:\n            Fans: 粉丝列表\n        \"\"\"\n\n        if id_ is None:\n            user = await self.get_self_info(ReqUInfo.USER_ID)\n            user_id = user.user_id\n        elif not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        return await get_fans.request(self._http_core, user_id, pn)\n\n    @handle_exception(get_blacklist.BlacklistUsers)\n    async def get_blacklist(self) -> get_blacklist.BlacklistUsers:\n        \"\"\"\n        获取完整的新版用户黑名单列表\n\n        Returns:\n            BlacklistUsers: 新版用户黑名单列表\n        \"\"\"\n\n        return await get_blacklist.request(self._http_core)\n\n    @handle_exception(get_blacklist_old.BlacklistOldUsers)\n    @_try_websocket\n    async def get_blacklist_old(self, pn: int = 1, /, *, rn: int = 10) -> get_blacklist_old.BlacklistOldUsers:\n        \"\"\"\n        获取旧版用户黑名单列表\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 10. Max to Inf.\n\n        Returns:\n            BlacklistOldUsers: 旧版用户黑名单列表\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_blacklist_old.request_ws(self._ws_core, pn, rn)\n\n        return await get_blacklist_old.request_http(self._http_core, pn, rn)\n\n    @handle_exception(get_follow_forums.FollowForums)\n    async def get_follow_forums(\n        self, id_: str | int, /, pn: int = 1, *, rn: int = 50\n    ) -> get_follow_forums.FollowForums:\n        \"\"\"\n        获取用户关注贴吧列表\n\n        Args:\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_id\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 50. Max to Inf.\n\n        Returns:\n            FollowForums: 用户关注贴吧列表\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        return await get_follow_forums.request(self._http_core, user_id, pn, rn)\n\n    @handle_exception(get_user_forum_info.UserForumInfo)\n    async def get_user_forum_info(\n        self, fname_or_fid: str | int, id_: str | int, /\n    ) -> get_user_forum_info.UserForumInfo:\n        \"\"\"\n        获取用户在某吧内的信息\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n            id_ (str | int): 用户id user_id / user_name / portrait 优先portrait\n\n        Returns:\n            UserForumInfo: 用户在吧内的信息\n        \"\"\"\n\n        if not fname_or_fid or not id_:\n            LOG().warning(\"Null input\")\n            return get_user_forum_info.UserForumInfo()\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if not is_portrait(id_):\n            user = await self.get_user_info(id_, ReqUInfo.PORTRAIT)\n            portrait = user.portrait\n        else:\n            portrait = id_\n\n        if not portrait:\n            return get_user_forum_info.UserForumInfo()\n\n        return await get_user_forum_info.request(self._http_core, fid, portrait)\n\n    @handle_exception(get_self_follow_forums.SelfFollowForums)\n    async def get_self_follow_forums(self, pn: int = 1, *, rn: int = 200) -> get_self_follow_forums.SelfFollowForums:\n        \"\"\"\n        获取本账号关注贴吧列表\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 200. Max to 200.\n\n        Returns:\n            SelfFollowForums: 本账号关注贴吧列表\n\n        Note:\n            本接口需要STOKEN\n        \"\"\"\n\n        return await get_self_follow_forums.request(self._http_core, pn, rn)\n\n    @handle_exception(get_dislike_forums.DislikeForums)\n    @_try_websocket\n    async def get_dislike_forums(self, pn: int = 1, /, *, rn: int = 20) -> get_dislike_forums.DislikeForums:\n        \"\"\"\n        获取首页推荐屏蔽的贴吧列表\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 20. Max to 20.\n\n        Returns:\n            DislikeForums: 首页推荐屏蔽的贴吧列表\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_dislike_forums.request_ws(self._ws_core, pn, rn)\n\n        return await get_dislike_forums.request_http(self._http_core, pn, rn)\n\n    @handle_exception(get_user_contents.UserPostss)\n    @_try_websocket\n    async def get_self_posts(self, pn: int = 1, *, rn: int = 20):\n        \"\"\"\n        获取当前用户发布的回复列表\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 20. Max to 74.\n\n        Returns:\n            UserPostss: 回复列表\n        \"\"\"\n\n        user = await self.get_self_info(ReqUInfo.USER_ID)\n        user_id = user.user_id\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_user_contents.get_posts.request_ws(self._ws_core, user_id, pn, rn, LATEST_VERSION)\n\n        return await get_user_contents.get_posts.request_http(self._http_core, user_id, pn, rn, LATEST_VERSION)\n\n    @handle_exception(get_user_contents.UserPostss)\n    async def get_user_posts(self, id_: str | int, pn: int = 1, *, rn: int = 20) -> get_user_contents.UserPostss:\n        \"\"\"\n        获取用户发布的回复列表\n\n        Args:\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_id\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 20. Max to 74.\n\n        Returns:\n            UserPostss: 回复列表\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        USER_POSTS_VERSION = \"8\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_user_contents.get_posts.request_ws(self._ws_core, user_id, pn, rn, USER_POSTS_VERSION)\n\n        return await get_user_contents.get_posts.request_http(self._http_core, user_id, pn, rn, USER_POSTS_VERSION)\n\n    @handle_exception(get_user_contents.UserThreads)\n    @_try_websocket\n    async def get_self_threads(self, pn: int = 1, *, public_only: bool = False) -> get_user_contents.UserThreads:\n        \"\"\"\n        获取当前用户发布的主题帖列表\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n            public_only (bool, optional): 是否仅获取公开主题帖 该选项在获取他人主题帖时无效. Defaults to False.\n\n        Returns:\n            UserThreads: 主题帖列表\n        \"\"\"\n\n        user = await self.get_self_info(ReqUInfo.USER_ID)\n        user_id = user.user_id\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_user_contents.get_threads.request_ws(self._ws_core, user_id, pn, public_only)\n\n        return await get_user_contents.get_threads.request_http(self._http_core, user_id, pn, public_only)\n\n    @handle_exception(get_user_contents.UserThreads)\n    @_try_websocket\n    async def get_user_threads(self, id_: str | int, pn: int = 1) -> get_user_contents.UserThreads:\n        \"\"\"\n        获取用户发布的主题帖列表\n\n        Args:\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_id\n            pn (int, optional): 页码. Defaults to 1.\n\n        Returns:\n            UserThreads: 主题帖列表\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_user_contents.get_threads.request_ws(self._ws_core, user_id, pn, False)\n\n        return await get_user_contents.get_threads.request_http(self._http_core, user_id, pn, False)\n\n    @handle_exception(get_replys.Replys)\n    @_try_websocket\n    async def get_replys(self, pn: int = 1) -> get_replys.Replys:\n        \"\"\"\n        获取回复信息\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n\n        Returns:\n            Replys: 回复列表\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_replys.request_ws(self._ws_core, pn)\n\n        return await get_replys.request_http(self._http_core, pn)\n\n    @handle_exception(get_ats.Ats)\n    async def get_ats(self, pn: int = 1) -> get_ats.Ats:\n        \"\"\"\n        获取@信息\n\n        Args:\n            pn (int, optional): 页码. Defaults to 1.\n\n        Returns:\n            Ats: at列表\n        \"\"\"\n\n        return await get_ats.request(self._http_core, pn)\n\n    @handle_exception(get_images.ImageBytes)\n    async def get_image_bytes(self, img_url: str) -> get_images.ImageBytes:\n        \"\"\"\n        从链接获取静态图像的原始字节流\n\n        Args:\n            img_url (str): 图像链接\n\n        Returns:\n            ImageBytes: 未解码的原始字节流\n        \"\"\"\n\n        return await get_images.request_bytes(self._http_core, yarl.URL(img_url))\n\n    @handle_exception(get_images.Image)\n    async def get_image(self, img_url: str) -> get_images.Image:\n        \"\"\"\n        从链接获取静态图像\n\n        Args:\n            img_url (str): 图像链接\n\n        Returns:\n            Image: 图像\n        \"\"\"\n\n        return await get_images.request(self._http_core, yarl.URL(img_url))\n\n    @handle_exception(get_images.Image)\n    async def hash2image(self, raw_hash: str, /, size: Literal[\"s\", \"m\", \"l\"] = \"s\") -> get_images.Image:\n        \"\"\"\n        通过百度图库hash获取静态图像\n\n        Args:\n            raw_hash (str): 百度图库hash\n            size (Literal['s', 'm', 'l'], optional): 获取图像的大小 s为宽720 m为宽960 l为原图. Defaults to 's'.\n\n        Returns:\n            Image: 图像\n        \"\"\"\n\n        if size == \"s\":\n            img_url = yarl.URL.build(\n                scheme=\"http\", host=\"imgsrc.baidu.com\", path=f\"/forum/w=720;q=60;g=0/sign=__/{raw_hash}.jpg\"\n            )\n        elif size == \"m\":\n            img_url = yarl.URL.build(\n                scheme=\"http\", host=\"imgsrc.baidu.com\", path=f\"/forum/w=960;q=60;g=0/sign=__/{raw_hash}.jpg\"\n            )\n        elif size == \"l\":\n            img_url = yarl.URL.build(scheme=\"http\", host=\"imgsrc.baidu.com\", path=f\"/forum/pic/item/{raw_hash}.jpg\")\n        else:\n            LOG().warning(f\"Invalid size={size}\")\n            return get_images.Image()\n\n        return await get_images.request(self._http_core, img_url)\n\n    @handle_exception(get_images.Image)\n    async def get_portrait(self, id_: str | int, /, size: Literal[\"s\", \"m\", \"l\"] = \"s\") -> get_images.Image:\n        \"\"\"\n        获取用户头像\n\n        Args:\n            id_ (str | int): 用户id user_id / user_name / portrait 优先portrait\n            size (Literal['s', 'm', 'l'], optional): 获取头像的大小 s为55x55 m为110x110 l为原图. Defaults to 's'.\n\n        Returns:\n            Image: 头像\n        \"\"\"\n\n        if not is_portrait(id_):\n            user = await self.get_user_info(id_, ReqUInfo.PORTRAIT)\n            portrait = user.portrait\n        else:\n            portrait = id_\n\n        if size == \"s\":\n            path = \"n\"\n        elif size == \"m\":\n            path = \"\"\n        elif size == \"l\":\n            path = \"h\"\n        else:\n            LOG().warning(f\"Invalid size={size}\")\n            return get_images.Image()\n\n        img_url = yarl.URL.build(scheme=\"http\", host=\"tb.himg.baidu.com\", path=f\"/sys/portrait{path}/item/{portrait}\")\n\n        return await get_images.request(self._http_core, img_url)\n\n    async def __get_selfinfo_initNickname(self) -> None:\n        user = await get_selfinfo_initNickname.request(self._http_core)\n        self._user |= user\n\n    async def __get_selfinfo_moindex(self) -> None:\n        user = await get_selfinfo_moindex.request(self._http_core)\n        self._user |= user\n\n    @handle_exception(get_square_forums.SquareForums)\n    @_try_websocket\n    async def get_square_forums(self, cname: str, /, pn: int = 1, *, rn: int = 20) -> get_square_forums.SquareForums:\n        \"\"\"\n        获取吧广场列表\n\n        Args:\n            cname (str): 类别名\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 20. Max to Inf.\n\n        Returns:\n            SquareForums: 吧广场列表\n        \"\"\"\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_square_forums.request_ws(self._ws_core, cname, pn, rn)\n\n        return await get_square_forums.request_http(self._http_core, cname, pn, rn)\n\n    @handle_exception(get_bawu_info.BawuInfo)\n    @_try_websocket\n    async def get_bawu_info(self, fname_or_fid: str | int) -> get_bawu_info.BawuInfo:\n        \"\"\"\n        获取吧务团队信息\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n\n        Returns:\n            BawuInfo: 吧务团队信息\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_bawu_info.request_ws(self._ws_core, fid)\n\n        return await get_bawu_info.request_http(self._http_core, fid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def add_bawu(\n        self, fname_or_fid: str | int, /, id_: str | int, *, bawu_type: BawuType = BawuType.MANAGER\n    ) -> BoolResponse:\n        \"\"\"\n        添加吧务\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_name\n            bawu_type (BawuType): 吧务类型. Defaults to BawuType.MANAGER.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if not is_user_name(id_):\n            user = await self.get_user_info(id_, ReqUInfo.USER_NAME)\n            user_name = user.user_name\n        else:\n            user_name = id_\n\n        await self.__init_tbs()\n\n        return await add_bawu.request(self._http_core, fid, user_name, bawu_type)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def del_bawu(\n        self, fname_or_fid: str | int, /, id_: str | int, *, bawu_type: BawuType = BawuType.MANAGER\n    ) -> BoolResponse:\n        \"\"\"\n        删除吧务\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n            id_ (str | int): 用户id user_id / user_name / portrait 优先portrait\n            bawu_type (BawuType): 吧务类型. Defaults to BawuType.MANAGER.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if not is_portrait(id_):\n            user = await self.get_user_info(id_, ReqUInfo.PORTRAIT)\n            portrait = user.portrait\n        else:\n            portrait = id_\n\n        return await del_bawu.request(self._http_core, fid, portrait, bawu_type)\n\n    @handle_exception(get_bawu_perm.BawuPerm)\n    async def get_bawu_perm(self, fname_or_fid: str | int, /, id_: str | int) -> get_bawu_perm.BawuPerm:\n        \"\"\"\n        获取指定吧务已分配的权限\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n            id_ (str | int): 用户id user_id / user_name / portrait 优先portrait\n\n        Returns:\n            BawuPerm: 吧务已分配的权限\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if not is_portrait(id_):\n            user = await self.get_user_info(id_, ReqUInfo.PORTRAIT)\n            portrait = user.portrait\n        else:\n            portrait = id_\n\n        return await get_bawu_perm.request(self._http_core, fid, portrait)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def set_bawu_perm(\n        self, fname_or_fid: str | int, /, id_: str | int, *, perms: BawuPermType = BawuPermType.NULL\n    ) -> BoolResponse:\n        \"\"\"\n        为指定吧务分配权限\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n            id_ (str | int): 用户id user_id / user_name / portrait 优先portrait\n            perms (BawuPermType): 待分配的权限. Defaults to BawuPermType.NULL.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if not is_portrait(id_):\n            user = await self.get_user_info(id_, ReqUInfo.PORTRAIT)\n            portrait = user.portrait\n        else:\n            portrait = id_\n\n        return await set_bawu_perm.request(self._http_core, fid, portrait, perms)\n\n    @handle_exception(get_tab_map.TabMap)\n    @_try_websocket\n    async def get_tab_map(self, fname_or_fid: str | int) -> get_tab_map.TabMap:\n        \"\"\"\n        获取分区名到分区id的映射\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先贴吧名\n\n        Returns:\n            TabMap: 分区名到分区id的映射\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await get_tab_map.request_ws(self._ws_core, fname)\n\n        return await get_tab_map.request_http(self._http_core, fname)\n\n    @handle_exception(get_rank_users.RankUsers)\n    async def get_rank_users(self, fname_or_fid: str | int, /, pn: int = 1) -> get_rank_users.RankUsers:\n        \"\"\"\n        获取pn页的等级排行榜用户列表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先贴吧名\n            pn (int, optional): 页码. Defaults to 1.\n\n        Returns:\n            RankUsers: 等级排行榜用户列表\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        return await get_rank_users.request(self._http_core, fname, pn)\n\n    @handle_exception(get_member_users.MemberUsers)\n    async def get_member_users(self, fname_or_fid: str | int, /, pn: int = 1) -> get_member_users.MemberUsers:\n        \"\"\"\n        获取pn页的最新关注用户列表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先贴吧名\n            pn (int, optional): 页码. Defaults to 1.\n\n        Returns:\n            MemberUsers: 最新关注用户列表\n\n        Note:\n            本接口需要STOKEN\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        return await get_member_users.request(self._http_core, fname, pn)\n\n    @handle_exception(get_rank_forums.RankForums)\n    async def get_rank_forums(\n        self, fname_or_fid: str | int, /, pn: int = 1, *, rank_type: RankForumType = RankForumType.WEEKLY\n    ) -> get_rank_forums.RankForums:\n        \"\"\"\n        获取pn页的吧签到排行表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先贴吧名\n            pn (int, optional): 页码. Defaults to 1.\n            rank_type (RankForumType, optional): 榜单类型 默认为周榜. Defaults to RankForumType.WEEKLY.\n\n        Returns:\n            RankForums: 吧签到排行表\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        return await get_rank_forums.request(self._http_core, fname, pn, rank_type)\n\n    @handle_exception(get_blocks.Blocks)\n    async def get_blocks(self, fname_or_fid: str | int, /, name: str = \"\", pn: int = 1) -> get_blocks.Blocks:\n        \"\"\"\n        获取pn页的待解封用户列表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧的贴吧名或fid 优先fid\n            name (str, optional): 通过被封禁用户的用户名/昵称查询 默认为空即查询全部. Defaults to ''.\n            pn (int, optional): 页码. Defaults to 1.\n\n        Returns:\n            Blocks: 待解封用户列表\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        return await get_blocks.request(self._http_core, fid, name, pn)\n\n    @handle_exception(get_recovers.Recovers)\n    async def get_recovers(\n        self, fname_or_fid: str | int, /, pn: int = 1, *, rn: int = 10, id_: str | int | None = None\n    ) -> get_recovers.Recovers:\n        \"\"\"\n        获取pn页的待恢复帖子列表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧的贴吧名或fid 优先fid\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 10. Max to 50.\n            id_ (str | int, optional): 用于查询的被删帖用户的id user_id / user_name / portrait 优先user_id. Defaults to None.\n\n        Returns:\n            Recovers: 待恢复帖子列表\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if id_ and not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        return await get_recovers.request(self._http_core, fid, user_id, pn, rn)\n\n    @handle_exception(get_bawu_userlogs.Userlogs)\n    async def get_bawu_userlogs(\n        self,\n        fname_or_fid: str | int,\n        /,\n        pn: int = 1,\n        *,\n        search_value: str = \"\",\n        search_type: BawuSearchType = BawuSearchType.USER,\n        start_dt: datetime.datetime | None = None,\n        end_dt: datetime.datetime | None = None,\n        op_type: int = 0,\n    ) -> get_bawu_userlogs.Userlogs:\n        \"\"\"\n        获取吧务用户管理日志表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先贴吧名\n            pn (int, optional): 页码. Defaults to 1.\n            search_value (str, optional): 搜索关键字. Defaults to ''.\n            search_type (BawuSearchType, optional): 搜索类型. Defaults to BawuSearchType.USER.\n            start_dt (datetime.datetime, optional): 搜索的起始时间(含). Defaults to None.\n            end_dt (datetime.datetime, optional): 搜索的结束时间(含). Defaults to None.\n            op_type (int, optional): 搜索操作类型. Defaults to 0.\n\n        Returns:\n            Userlogs: 吧务用户管理日志表\n\n        Note:\n            本接口需要STOKEN\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        return await get_bawu_userlogs.request(\n            self._http_core, fname, pn, search_value, search_type, start_dt, end_dt, op_type\n        )\n\n    @handle_exception(get_bawu_postlogs.Postlogs)\n    async def get_bawu_postlogs(\n        self,\n        fname_or_fid: str | int,\n        /,\n        pn: int = 1,\n        *,\n        search_value: str = \"\",\n        search_type: BawuSearchType = BawuSearchType.USER,\n        start_dt: datetime.datetime | None = None,\n        end_dt: datetime.datetime | None = None,\n        op_type: int = 0,\n    ) -> get_bawu_postlogs.Postlogs:\n        \"\"\"\n        获取吧务帖子管理日志表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先贴吧名\n            pn (int, optional): 页码. Defaults to 1.\n            search_value (str, optional): 搜索关键字. Defaults to ''.\n            search_type (BawuSearchType, optional): 搜索类型. Defaults to BawuSearchType.USER.\n            start_dt (datetime.datetime, optional): 搜索的起始时间(含). Defaults to None.\n            end_dt (datetime.datetime, optional): 搜索的结束时间(含). Defaults to None.\n            op_type (int, optional): 搜索操作类型. Defaults to 0.\n\n        Returns:\n            Postlogs: 吧务帖子管理日志表\n\n        Note:\n            本接口需要STOKEN\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        return await get_bawu_postlogs.request(\n            self._http_core, fname, pn, search_value, search_type, start_dt, end_dt, op_type\n        )\n\n    @handle_exception(get_unblock_appeals.Appeals)\n    async def get_unblock_appeals(\n        self, fname_or_fid: str | int, /, pn: int = 1, *, rn: int = 5\n    ) -> get_unblock_appeals.Appeals:\n        \"\"\"\n        获取申诉请求列表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧的贴吧名或fid 优先fid\n            pn (int, optional): 页码. Defaults to 1.\n            rn (int, optional): 请求的条目数. Defaults to 5. Max to 50.\n\n        Returns:\n            Appeals: 申诉请求列表\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await get_unblock_appeals.request(self._http_core, fid, pn, rn)\n\n    @handle_exception(get_bawu_blacklist.BawuBlacklistUsers)\n    async def get_bawu_blacklist(\n        self, fname_or_fid: str | int, /, pn: int = 1\n    ) -> get_bawu_blacklist.BawuBlacklistUsers:\n        \"\"\"\n        获取pn页的吧务黑名单列表\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧的贴吧名或fid 优先贴吧名\n            pn (int, optional): 页码. Defaults to 1.\n\n        Returns:\n            BlacklistUsers: 吧务黑名单列表\n\n        Note:\n            本接口需要STOKEN\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        return await get_bawu_blacklist.request(self._http_core, fname, pn)\n\n    @handle_exception(get_statistics.Statistics)\n    async def get_statistics(self, fname_or_fid: str | int) -> get_statistics.Statistics:\n        \"\"\"\n        获取吧务后台中最近24天的统计数据\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n\n        Returns:\n            Statistics: 吧务后台统计信息\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        return await get_statistics.request(self._http_core, fid)\n\n    @handle_exception(get_recom_status.RecomStatus)\n    async def get_recom_status(self, fname_or_fid: str | int) -> get_recom_status.RecomStatus:\n        \"\"\"\n        获取大吧主推荐功能的月度配额状态\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧名或fid 优先fid\n\n        Returns:\n            RecomStatus: 大吧主推荐功能的月度配额状态\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        return await get_recom_status.request(self._http_core, fid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def block(\n        self, fname_or_fid: str | int, /, id_: str | int, *, day: int = 1, reason: str = \"\"\n    ) -> BoolResponse:\n        \"\"\"\n        封禁用户\n\n        Args:\n            fname_or_fid (str | int): 所在贴吧的贴吧名或fid 优先fid\n            id_ (str | int): 用户id user_id / user_name / portrait 优先portrait\n            day (int, optional): 封禁天数. Defaults to 1.\n            reason (str, optional): 封禁理由. Defaults to ''.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if not is_portrait(id_):\n            user = await self.get_user_info(id_, ReqUInfo.PORTRAIT)\n            portrait = user.portrait\n        else:\n            portrait = id_\n\n        await self.__init_tbs()\n\n        return await block.request(self._http_core, fid, portrait, day, reason)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def unblock(self, fname_or_fid: str | int, /, id_: str | int) -> BoolResponse:\n        \"\"\"\n        解封用户\n\n        Args:\n            fname_or_fid (str | int): 所在贴吧的贴吧名或fid 优先fid\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_id\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        await self.__init_tbs()\n\n        return await unblock.request(self._http_core, fid, user_id)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def add_bawu_blacklist(self, fname_or_fid: str | int, /, id_: str | int) -> BoolResponse:\n        \"\"\"\n        添加贴吧黑名单\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧的贴吧名或fid 优先贴吧名\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_id\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        await self.__init_tbs()\n\n        return await add_bawu_blacklist.request(self._http_core, fname, user_id)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def del_bawu_blacklist(self, fname_or_fid: str | int, /, id_: str | int) -> BoolResponse:\n        \"\"\"\n        移出贴吧黑名单\n\n        Args:\n            fname_or_fid (str | int): 目标贴吧的贴吧名或fid 优先贴吧名\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_id\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        await self.__init_tbs()\n\n        return await del_bawu_blacklist.request(self._http_core, fname, user_id)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def hide_thread(self, fname_or_fid: str | int, /, tid: int) -> BoolResponse:\n        \"\"\"\n        屏蔽主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int): 待屏蔽的主题帖tid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await del_thread.request(self._http_core, fid, tid, is_hide=True)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def del_thread(self, fname_or_fid: str | int, /, tid: int) -> BoolResponse:\n        \"\"\"\n        删除主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int): 待删除的主题帖tid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await del_thread.request(self._http_core, fid, tid, is_hide=False)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def del_threads(self, fname_or_fid: str | int, /, tids: list[int], *, block: bool = False) -> BoolResponse:\n        \"\"\"\n        批量删除主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tids (list[int]): 待删除的主题帖tid列表. Length Max to 30.\n            block (bool, optional): 是否同时封一天. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败 部分成功返回True\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await del_threads.request(self._http_core, fid, tids, block)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def del_post(self, fname_or_fid: str | int, /, tid: int, pid: int) -> BoolResponse:\n        \"\"\"\n        删除回复\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int): 所在主题帖tid\n            pid (int): 待删除的回复pid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await del_post.request(self._http_core, fid, tid, pid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def del_posts(\n        self, fname_or_fid: str | int, /, tid: int, pids: list[int], *, block: bool = False\n    ) -> BoolResponse:\n        \"\"\"\n        批量删除回复\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int): 所在主题帖tid\n            pids (list[int]): 待删除的回复pid列表. Length Max to 30.\n            block (bool, optional): 是否同时封一天. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败 部分成功返回True\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await del_posts.request(self._http_core, fid, tid, pids, block)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def unhide_thread(self, fname_or_fid: str | int, /, tid: int) -> BoolResponse:\n        \"\"\"\n        解除主题帖屏蔽\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int, optional): 待解除屏蔽的主题帖tid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await recover.request(self._http_core, fid, tid, 0, is_hide=True)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def recover_thread(self, fname_or_fid: str | int, /, tid: int) -> BoolResponse:\n        \"\"\"\n        恢复主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int, optional): 待恢复的主题帖tid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await recover.request(self._http_core, fid, tid, 0, is_hide=False)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def recover_post(self, fname_or_fid: str | int, /, pid: int) -> BoolResponse:\n        \"\"\"\n        恢复主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            pid (int, optional): 待恢复的回复pid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await recover.request(self._http_core, fid, 0, pid, is_hide=False)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def recover(\n        self, fname_or_fid: str | int, /, tid: int = 0, pid: int = 0, *, is_hide: bool = False\n    ) -> BoolResponse:\n        \"\"\"\n        帖子恢复相关操作\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int, optional): 待恢复的主题帖tid. Defaults to 0.\n            pid (int, optional): 待恢复的回复pid. Defaults to 0.\n            is_hide (bool, optional): True则取消屏蔽主题帖 False则恢复删帖. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await recover.request(self._http_core, fid, tid, pid, is_hide)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def good(self, fname_or_fid: str | int, /, tid: int, *, cname: str = \"\") -> BoolResponse:\n        \"\"\"\n        加精主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid\n            tid (int): 待加精的主题帖tid\n            cname (str, optional): 待添加的精华分区名称 默认为''即不分区. Defaults to ''.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if isinstance(fname_or_fid, str):\n            fname = fname_or_fid\n            fid = await self.__get_fid(fname)\n        else:\n            fid = fname_or_fid\n            fname = await self.__get_fname(fid)\n\n        await self.__init_tbs()\n\n        cid = await self.__get_cid(fname_or_fid, cname)\n\n        return await good.request(self._http_core, fname, fid, tid, cid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def ungood(self, fname_or_fid: str | int, /, tid: int) -> BoolResponse:\n        \"\"\"\n        撤精主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid\n            tid (int): 待撤精的主题帖tid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if isinstance(fname_or_fid, str):\n            fname = fname_or_fid\n            fid = await self.__get_fid(fname)\n        else:\n            fid = fname_or_fid\n            fname = await self.__get_fname(fid)\n\n        await self.__init_tbs()\n\n        return await ungood.request(self._http_core, fname, fid, tid)\n\n    async def __get_cid(self, fname_or_fid: str | int, /, cname: str = \"\") -> int:\n        if cname == \"\":\n            return 0\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n\n        cates = await get_cid.request(self._http_core, fname)\n\n        cid = 0\n        for item in cates:\n            if cname == item[\"class_name\"]:\n                cid = item[\"class_id\"]\n                break\n\n        return cid\n\n    @handle_exception(IntResponse)\n    async def get_cid(self, fname_or_fid: str | int, /, cname: str = \"\") -> IntResponse:\n        \"\"\"\n        通过精华分区名获取精华分区id\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid\n            cname (str, optional): 精华分区名. Defaults to ''.\n\n        Returns:\n            IntResponse: 精华分区id\n        \"\"\"\n\n        cid = await self.__get_cid(fname_or_fid, cname)\n        return IntResponse(cid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def top(self, fname_or_fid: str | int, /, tid: int, *, is_vip: bool = False) -> BoolResponse:\n        \"\"\"\n        置顶主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid\n            tid (int): 待置顶的主题帖tid\n            is_vip (bool, optional): 是否会员置顶. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if isinstance(fname_or_fid, str):\n            fname = fname_or_fid\n            fid = await self.__get_fid(fname)\n        else:\n            fid = fname_or_fid\n            fname = await self.__get_fname(fid)\n\n        await self.__init_tbs()\n\n        return await top.request(self._http_core, fname, fid, tid, is_vip, True)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def untop(self, fname_or_fid: str | int, /, tid: int, *, is_vip: bool = False) -> BoolResponse:\n        \"\"\"\n        撤销置顶主题帖\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid\n            tid (int): 待撤销置顶的主题帖tid\n            is_vip (bool, optional): 是否会员置顶. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if isinstance(fname_or_fid, str):\n            fname = fname_or_fid\n            fid = await self.__get_fid(fname)\n        else:\n            fid = fname_or_fid\n            fname = await self.__get_fname(fid)\n\n        await self.__init_tbs()\n\n        return await top.request(self._http_core, fname, fid, tid, is_vip, False)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def move(self, fname_or_fid: str | int, /, tid: int, *, to_tab_id: int, from_tab_id: int = 0) -> BoolResponse:\n        \"\"\"\n        将主题帖移动至另一分区\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int): 待移动的主题帖tid\n            to_tab_id (int): 目标分区id\n            from_tab_id (int, optional): 来源分区id 默认为0即无分区. Defaults to 0.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await move.request(self._http_core, fid, tid, to_tab_id, from_tab_id)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def recommend(self, fname_or_fid: str | int, /, tid: int) -> BoolResponse:\n        \"\"\"\n        大吧主首页推荐\n\n        Args:\n            fname_or_fid (str | int): 帖子所在贴吧的贴吧名或fid 优先fid\n            tid (int): 待推荐的主题帖tid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        return await recommend.request(self._http_core, fid, tid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def handle_unblock_appeals(\n        self, fname_or_fid: str | int, /, appeal_ids: list[int], *, refuse: bool = True\n    ) -> BoolResponse:\n        \"\"\"\n        拒绝或通过解封申诉\n\n        Args:\n            fname_or_fid (str | int): 申诉所在贴吧的贴吧名或fid 优先fid\n            appeal_ids (list[int]): 申诉请求的appeal_id列表. Length Max to 30.\n            refuse (bool, optional): True则拒绝申诉 False则接受申诉. Defaults to True.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await handle_unblock_appeals.request(self._http_core, fid, appeal_ids, refuse)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def agree(self, tid: int, pid: int = 0, is_comment: bool = False) -> BoolResponse:\n        \"\"\"\n        点赞主题帖或回复\n\n        Args:\n            tid (int): 待点赞的主题帖或回复所在的主题帖的tid\n            pid (int, optional): 待点赞的回复pid. Defaults to 0.\n            is_comment (bool, optional): pid是否指向楼中楼. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败\n\n        Note:\n            本接口仍处于测试阶段\\n\n            高频率调用会导致<发帖秒删>! 请谨慎使用!\n        \"\"\"\n\n        await self.__init_tbs()\n\n        return await agree.request(self._http_core, tid, pid, is_comment, is_disagree=False, is_undo=False)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def unagree(self, tid: int, pid: int = 0, is_comment: bool = False) -> BoolResponse:\n        \"\"\"\n        取消点赞主题帖或回复\n\n        Args:\n            tid (int): 待取消点赞的主题帖或回复所在的主题帖的tid\n            pid (int, optional): 待取消点赞的回复pid. Defaults to 0.\n            is_comment (bool, optional): pid是否指向楼中楼. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        await self.__init_tbs()\n\n        return await agree.request(self._http_core, tid, pid, is_comment, is_disagree=False, is_undo=True)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def disagree(self, tid: int, pid: int = 0, is_comment: bool = False) -> BoolResponse:\n        \"\"\"\n        点踩主题帖或回复\n\n        Args:\n            tid (int): 待点踩的主题帖或回复所在的主题帖的tid\n            pid (int, optional): 待点踩的回复pid. Defaults to 0.\n            is_comment (bool, optional): pid是否指向楼中楼. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        await self.__init_tbs()\n\n        return await agree.request(self._http_core, tid, pid, is_comment, is_disagree=True, is_undo=False)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def undisagree(self, tid: int, pid: int = 0, is_comment: bool = False) -> BoolResponse:\n        \"\"\"\n        取消点踩主题帖或回复\n\n        Args:\n            tid (int): 待取消点踩的主题帖或回复所在的主题帖的tid\n            pid (int, optional): 待取消点踩的回复pid. Defaults to 0.\n            is_comment (bool, optional): pid是否指向楼中楼. Defaults to False.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        await self.__init_tbs()\n\n        return await agree.request(self._http_core, tid, pid, is_comment, is_disagree=True, is_undo=True)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def follow_user(self, id_: str | int) -> BoolResponse:\n        \"\"\"\n        关注用户\n\n        Args:\n            id_ (str | int): 用户id user_id / user_name / portrait 优先portrait\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if not is_portrait(id_):\n            user = await self.get_user_info(id_, ReqUInfo.PORTRAIT)\n            portrait = user.portrait\n        else:\n            portrait = id_\n\n        await self.__init_tbs()\n\n        return await follow_user.request(self._http_core, portrait)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def unfollow_user(self, id_: str | int) -> BoolResponse:\n        \"\"\"\n        取关用户\n\n        Args:\n            id_ (str | int): 用户id user_id / user_name / portrait 优先portrait\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if not is_portrait(id_):\n            user = await self.get_user_info(id_, ReqUInfo.PORTRAIT)\n            portrait = user.portrait\n        else:\n            portrait = id_\n\n        await self.__init_tbs()\n\n        return await unfollow_user.request(self._http_core, portrait)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def remove_fan(self, id_: str | int) -> BoolResponse:\n        \"\"\"\n        移除粉丝\n\n        Args:\n            id_ (str | int): 待移除粉丝的id user_id / user_name / portrait 优先user_id\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        await self.__init_tbs()\n\n        return await remove_fan.request(self._http_core, user_id)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    @_try_websocket\n    async def set_blacklist(self, id_: str | int, *, btype: BlacklistType = BlacklistType.ALL) -> BoolResponse:\n        \"\"\"\n        设置新版用户黑名单\n\n        Args:\n            id_ (str | int): 待设置黑名单的用户id user_id / user_name / portrait 优先user_id\n            btype (BlacklistType): 黑名单类型. 默认全屏蔽. Defaults to BlacklistType.ALL.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await set_blacklist.request_ws(self._ws_core, user_id, btype)\n\n        return await set_blacklist.request_http(self._http_core, user_id, btype)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def add_blacklist_old(self, id_: str | int) -> BoolResponse:\n        \"\"\"\n        添加旧版用户黑名单\n\n        Args:\n            id_ (str | int): 待添加黑名单的用户id user_id / user_name / portrait 优先user_id\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        return await add_blacklist_old.request(self._http_core, user_id)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def del_blacklist_old(self, id_: str | int) -> BoolResponse:\n        \"\"\"\n        移除旧版用户黑名单\n\n        Args:\n            id_ (str | int): 待移除黑名单的用户id user_id / user_name / portrait 优先user_id\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        return await del_blacklist_old.request(self._http_core, user_id)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def follow_forum(self, fname_or_fid: str | int) -> BoolResponse:\n        \"\"\"\n        关注贴吧\n\n        Args:\n            fname_or_fid (str | int): 要关注贴吧的贴吧名或fid 优先fid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await follow_forum.request(self._http_core, fid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def unfollow_forum(self, fname_or_fid: str | int) -> BoolResponse:\n        \"\"\"\n        取关贴吧\n\n        Args:\n            fname_or_fid (str | int): 要取关贴吧的贴吧名或fid 优先fid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n        await self.__init_tbs()\n\n        return await unfollow_forum.request(self._http_core, fid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def dislike_forum(self, fname_or_fid: str | int) -> BoolResponse:\n        \"\"\"\n        屏蔽贴吧 使其不再出现在首页推荐列表中\n\n        Args:\n            fname_or_fid (str | int): 待屏蔽贴吧的贴吧名或fid 优先fid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        return await dislike_forum.request(self._http_core, fid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def undislike_forum(self, fname_or_fid: str | int) -> BoolResponse:\n        \"\"\"\n        解除贴吧的首页推荐屏蔽\n\n        Args:\n            fname_or_fid (str | int): 待屏蔽贴吧的贴吧名或fid 优先fid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        return await undislike_forum.request(self._http_core, fid)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def set_thread_private(self, fname_or_fid: str | int, /, tid: int, pid: int) -> BoolResponse:\n        \"\"\"\n        隐藏主题帖\n\n        Args:\n            fname_or_fid (str | int): 主题帖所在贴吧的贴吧名或fid 优先fid\n            tid (int): 主题帖tid\n            tid (int): 主题帖pid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        return await set_thread_privacy.request(self._http_core, fid, tid, pid, is_hide=True)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def set_thread_public(self, fname_or_fid: str | int, /, tid: int, pid: int) -> BoolResponse:\n        \"\"\"\n        公开主题帖\n\n        Args:\n            fname_or_fid (str | int): 主题帖所在贴吧的贴吧名或fid 优先fid\n            tid (int): 主题帖tid\n            tid (int): 主题帖pid\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fid = fname_or_fid if isinstance(fname_or_fid, int) else await self.__get_fid(fname_or_fid)\n\n        return await set_thread_privacy.request(self._http_core, fid, tid, pid, is_hide=False)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def set_profile(self, nick_name: str, sign: str = \"\", gender: Gender = Gender.UNKNOWN) -> BoolResponse:\n        \"\"\"\n        设置主页信息\n\n        Args:\n            nick_name (str): 昵称\n            sign (str): 个性签名. Defaults to ''.\n            gender (Gender): 性别. Defaults to Gender.UNKNOWN.\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        return await set_profile.request(self._http_core, nick_name, sign, gender)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def set_nickname_old(self, nick_name: str) -> BoolResponse:\n        \"\"\"\n        设置旧版昵称\n\n        Args:\n            nick_name (str): 昵称\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        return await set_nickname_old.request(self._http_core, nick_name)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def sign_forum(self, fname_or_fid: str | int) -> BoolResponse:\n        \"\"\"\n        单个贴吧签到\n\n        Args:\n            fname_or_fid (str | int): 要签到贴吧的贴吧名或fid 优先贴吧名\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        fname = fname_or_fid if isinstance(fname_or_fid, str) else await self.__get_fname(fname_or_fid)\n        await self.__init_tbs()\n\n        return await sign_forum.request(self._http_core, fname)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def sign_forums(self) -> BoolResponse:\n        \"\"\"\n        一键签到\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        return await sign_forums.request(self._http_core)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def sign_growth(self) -> BoolResponse:\n        \"\"\"\n        用户成长等级任务: 签到\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        await self.__init_tbs()\n\n        return await sign_growth.request_web(self._http_core, act_type=\"page_sign\")\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    @_try_websocket\n    @deprecated(\"此接口风险极高，可能导致账号被永久封禁屏蔽，故弃用并将于近期移除\")\n    async def add_post(self, fname_or_fid: str | int, /, tid: int, content: str) -> BoolResponse:\n        \"\"\"\n        回复主题帖\n\n        Args:\n            fname_or_fid (str | int): 要回复的主题帖所在贴吧的贴吧名或fid\n            tid (int): 要回复的主题帖的tid\n            content (str): 回复内容\n\n        Returns:\n            BoolResponse: 回帖是否成功\n\n        Note:\n            本接口仍处于测试阶段\\n\n            高频率调用会导致<永久封禁屏蔽>! 请谨慎使用!\n        \"\"\"\n\n        if isinstance(fname_or_fid, str):\n            fname = fname_or_fid\n            fid = await self.__get_fid(fname)\n        else:\n            fid = fname_or_fid\n            fname = await self.__get_fname(fid)\n\n        await self.__init_z_id()\n        await self.__init_tbs()\n        await self.__init_client_id()\n        await self.__init_sample_id()\n        await self.__get_selfinfo_initNickname()\n\n        show_name = self._user.show_name\n\n        if self._ws_core.status == WsStatus.OPEN:\n            return await add_post.request_ws(self._ws_core, fname, fid, tid, show_name, content)\n\n        return await add_post.request_http(self._http_core, fname, fid, tid, show_name, content)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    @_force_websocket\n    async def send_msg(self, id_: str | int, content: str) -> BoolResponse:\n        \"\"\"\n        发送私信\n\n        Args:\n            id_ (str | int): 用户id user_id / user_name / portrait 优先user_id\n            content (str): 发送内容\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if not isinstance(id_, int):\n            user = await self.get_user_info(id_, ReqUInfo.USER_ID)\n            user_id = user.user_id\n        else:\n            user_id = id_\n\n        msg_id = await send_msg.request(self._ws_core, user_id, content)\n\n        mid_manager = self._ws_core.mid_manager\n        mid_manager.update_msg_id(mid_manager.priv_gid, msg_id)\n\n        return BoolResponse()\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    @_force_websocket\n    async def set_msg_readed(self, message: get_group_msg.WsMessage) -> BoolResponse:\n        \"\"\"\n        将一条私信设为已读\n\n        Args:\n            message (WsMessage): websocket私信消息\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        return await set_msg_readed.request(self._ws_core, message)\n\n    @handle_exception(get_group_msg.WsMsgGroups)\n    @_force_websocket\n    async def get_group_msg(self, group_ids: list[int], *, get_type: int = 1) -> get_group_msg.WsMsgGroups:\n        \"\"\"\n        获取分组信息\n\n        Args:\n            group_ids (list[int]): 待获取分组的group_id\n            get_type (int, optional): 获取类型. Defaults to 1.\n\n        Returns:\n            WsMsgGroups: websocket消息组列表\n        \"\"\"\n\n        return await get_group_msg.request(self._ws_core, group_ids, get_type)\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def send_chatroom_msg(\n        self, chatroom_id: int, forum_id: int, text: str, atuser_ids: list[int] = None, robotc: int = -1\n    ) -> BoolResponse:\n        \"\"\"\n        向吧群发送信息，仅限简单文本。如需要@他人需要指定atuser_ids，如需与bot交互需要指定atuser_ids和robot\n\n        Args:\n            chatroom_id (int): 聊天室id\n            forum_id (int): 吧id\n            text (str): 待发送内容\n            atuser_ids (list[int], optional): 需要@的人的user_id列表\n            robotc (int, optional): 机器人指令id。机器人靠此分辨指令，而非text内容。\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        async def _ensure_user_info():\n            required_attrs = [\"user_id\", \"portrait\"]\n            max_retries = 3\n\n            for _ in range(max_retries + 1):\n                if all(getattr(self._user, attr) for attr in required_attrs):\n                    return\n                await self.get_self_info()\n\n            raise ValueError(\"登录失败\")\n\n        await _ensure_user_info()\n        if self._blcp_core.status != 1:\n            await self._init_blcp()\n\n        level_info = await self.__get_forum_level(forum_id)\n        level = level_info.user_level\n        isvip = self._user.is_vip\n        glevel = self._user.glevel\n\n        # 处理艾特@\n        atdata = []\n        if atuser_ids:\n            for count, user_id in enumerate(atuser_ids):\n                userforAt = await self._get_uinfo_profile(user_id)\n                if not all([userforAt.portrait, userforAt.nick_name]):\n                    userforAt = await self._get_uinfo_profile(user_id)\n\n                atdata.append({\n                    \"at_type\": \"user\",\n                    \"at_baidu_uk\": self._blcp_core.getBDUKfromUserId(str(user_id)),\n                    \"at_name\": userforAt.nick_name,\n                    \"at_portrait\": userforAt.portrait,\n                    \"position\": str(count),\n                })\n\n        return await send_chatroom_msg.request(\n            self._blcp_core,\n            chatroom_id,\n            self._user.uk,\n            self._user.user_id,\n            self._user.trigger_id,\n            self._user.nick_name,\n            self._user.portrait,\n            text,\n            forum_id,\n            level,\n            isvip,\n            glevel,\n            atdata,\n            robot=robotc,\n        )\n\n    @handle_exception(BoolResponse, ok_log_level=logging.INFO)\n    async def _init_blcp(self):\n        if self._blcp_core.status == -1:\n            await self._blcp_core.connect()\n        if self._blcp_core.status == 0:\n            await self._blcp_core.login()\n        if self._blcp_core.status == 1:\n            return BoolResponse()\n        else:\n            raise\n\n    async def __get_forum_level(self, forum_id: int) -> get_forum_level.LevelInfo:\n        if not self._user.user_id:\n            await self.get_self_info()\n\n        return await get_forum_level.request_http(self._http_core, forum_id)\n\n    @handle_exception(get_roomlist_by_fid.RoomList)\n    async def get_roomlist_by_fid(self, forum_id: int) -> get_roomlist_by_fid.RoomList:\n        \"\"\"\n        获取某吧所有群聊\n\n        Args:\n            forum_id (int),: 吧id.\n\n        Returns:\n            RoomList: 群信息\n        \"\"\"\n\n        if not self._user.user_id:  # 检查是否登陆\n            await self.get_self_info()\n\n        return await get_roomlist_by_fid.request(self._http_core, forum_id)\n\n    def get_chat_message_queue(self) -> asyncio.Queue:\n        \"\"\"\n        获取消息队列（全局共用），该队列仅包含通知（Notify）类型消息\n\n        Returns:\n            Queue: 消息队列\n        \"\"\"\n\n        return self._blcp_core.message_queue\n\n    @handle_exception(BoolResponse)\n    async def join_chatroom(self, room_id: int) -> BoolResponse:\n        \"\"\"\n        获取某吧等级\n\n        Args:\n            room_id (int): 房间id\n\n        Returns:\n            BoolResponse: True成功 False失败\n        \"\"\"\n\n        if not self._user.user_id:  # 检查是否登陆\n            await self.get_self_info()\n        if self._blcp_core.status != 1:  # 检查BLCP是否登陆\n            await self._init_blcp()\n\n        try:\n            await self._blcp_core.joinChatRoom(room_id)\n        except Exception as err:\n            raise Exception(\"加入房间失败\") from err\n\n        return BoolResponse()\n"
  },
  {
    "path": "src/aiotieba/config.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\n\nimport aiohttp\nimport yarl\n\n\n@dcs.dataclass\nclass ProxyConfig:\n    \"\"\"\n    代理配置\n\n    Args:\n        url (str | yarl.URL, optional): 代理url. Defaults to None.\n        auth (aiohttp.BasicAuth, optional): 代理认证. Defaults to None.\n    \"\"\"\n\n    url: yarl.URL | None = None\n    auth: aiohttp.BasicAuth | None = None\n\n    def __init__(self, url: str | yarl.URL | None = None, auth: aiohttp.BasicAuth | None = None) -> None:\n        if isinstance(url, str):\n            url = yarl.URL(url)\n        self.url = url\n        self.auth = auth\n\n    @staticmethod\n    def from_env() -> ProxyConfig:\n        proxy_info = aiohttp.helpers.proxies_from_env().get(\"http\", None)\n        if proxy_info is None:\n            url, auth = None, None\n        else:\n            url, auth = proxy_info.proxy, proxy_info.proxy_auth\n        return ProxyConfig(url, auth)\n\n\n@dcs.dataclass\nclass TimeoutConfig:\n    \"\"\"\n    各种超时配置\n\n    Args:\n        http_acquire_conn (float, optional): 从连接池获取一个可用连接的超时时间. Defaults to 4.0.\n        http_read (float, optional): 从发送http请求到读取全部响应的超时时间. Defaults to 12.0.\n        http_connect (float, optional): 新建一个socket连接的超时时间. Defaults to 3.0.\n        http_keepalive (float, optional): http长连接的保持时间. Defaults to 30.0.\n        ws_send (float, optional): websocket发送数据的超时时间. Defaults to 3.0.\n        ws_read (float, optional): 从发送websocket数据到结束等待响应的超时时间. Defaults to 8.0.\n        ws_close (float, optional): 等待websocket终止连接的时间. Defaults to 10.0.\n        ws_keepalive (float, optional): websocket在长达ws_keepalive的时间内未发生IO则发送close信号关闭连接. Defaults to 300.0.\n        ws_heartbeat (float, optional): websocket心跳间隔. 为None则不发送心跳. Defaults to None.\n        dns_ttl (int, optional): dns的本地缓存超时时间. Defaults to 600.\n\n    Note:\n        所有时间均以秒为单位\n    \"\"\"\n\n    http_acquire_conn: float = 4.0\n    http_read: float = 12.0\n    http_connect: float = 3.0\n    http_keepalive: float = 30.0\n    ws_send: float = 3.0\n    ws_read: float = 8.0\n    ws_close: float = 10.0\n    ws_keepalive: float = 300.0\n    ws_heartbeat: float | None = None\n    dns_ttl: int = 600\n\n    @property\n    def http_timeout(self) -> aiohttp.ClientTimeout:\n        return aiohttp.ClientTimeout(\n            connect=self.http_acquire_conn, sock_read=self.http_read, sock_connect=self.http_connect\n        )\n\n    @property\n    def ws_timeout(self) -> aiohttp.ClientWSTimeout:\n        return aiohttp.ClientWSTimeout(self.ws_read, self.ws_close)\n"
  },
  {
    "path": "src/aiotieba/const.py",
    "content": "LATEST_VERSION = \"22.5.1.0\"  # 通常用于写API (`agree`...)\nSTABLE_VERSION = \"12.64.1.1\"  # 通常用于只读API (`get_threads`...)\nCHAT_VERSION = \"12.68.1.0\"\nCHAT_APPID = 10773430\nCHAT_SDK_VERSION = 11250036\n\nAPP_BASE_HOST = \"tiebac.baidu.com\"\nWEB_BASE_HOST = \"tieba.baidu.com\"\n"
  },
  {
    "path": "src/aiotieba/core/__init__.py",
    "content": "from .account import Account\nfrom .blcp import BLCPCore, BLCPData\nfrom .http import HttpCore\nfrom .net import NetCore\nfrom .websocket import TypeWebsocketCallback, WsCore, WsResponse\n"
  },
  {
    "path": "src/aiotieba/core/account.py",
    "content": "from __future__ import annotations\n\nimport random\n\nfrom cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\nfrom cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC\n\nfrom ..helper.crypto import c3_aid, cuid_galaxy2\n\n\nclass Account:\n    \"\"\"\n    贴吧的用户参数容器\n\n    Args:\n        BDUSS (str, optional): BDUSS. Defaults to ''.\n        STOKEN (str, optional): 网页STOKEN. Defaults to ''.\n\n    Attributes:\n        BDUSS (str): BDUSS\n        STOKEN (str): 网页STOKEN\n        tbs (str): 长度为26的小写16进制字符串 例:91be894d01799c4991be894d01\n        android_id (str): 长度为16的小写16进制字符串 包含8字节信息 例:91be894d01799c49\n        uuid (str): 包含16字节信息 例:e4200716-58a8-4170-af15-ea7edeb8e513\n        client_id (str): 例:wappc_1653660000000_123\n        sample_id (str): 例:104505_3-105324_2-...-107269_1\n        cuid (str): 例:baidutiebaappe4200716-58a8-4170-af15-ea7edeb8e513\n        cuid_galaxy2 (str): 例:A3ED2D7B9CFC28E8934A3FBD3A9579C7|VZ5FKB5XS\n        c3_aid (str): 例:A00-ZNU3O3EP74D727LMQY745CZSGZQJQZGP-3JXCKC7X\n        z_id (str): z_id\n        aes_ecb_sec_key (bytes): 供贴吧AES-ECB加密使用的随机密码 长度为31字节\n        aes_ecb_chiper (Any): AES-ECB加密器\n        aes_cbc_sec_key (bytes): 供贴吧AES-CBC加密使用的随机密码 长度为16字节\n        aes_cbc_chiper (Any): AES-CBC加密器\n    \"\"\"\n\n    __slots__ = [\n        \"_BDUSS\",\n        \"_STOKEN\",\n        \"_tbs\",\n        \"_android_id\",\n        \"_uuid\",\n        \"_client_id\",\n        \"_sample_id\",\n        \"_cuid\",\n        \"_cuid_galaxy2\",\n        \"_c3_aid\",\n        \"_z_id\",\n        \"_aes_ecb_sec_key\",\n        \"_aes_ecb_chiper\",\n        \"_aes_cbc_sec_key\",\n        \"_aes_cbc_chiper\",\n    ]\n\n    __serialize__ = [\n        \"_BDUSS\",\n        \"_STOKEN\",\n        \"_tbs\",\n        \"_android_id\",\n        \"_uuid\",\n        \"_client_id\",\n        \"_sample_id\",\n        \"_cuid\",\n        \"_cuid_galaxy2\",\n        \"_c3_aid\",\n        \"_z_id\",\n        \"_aes_ecb_sec_key\",\n        \"_aes_cbc_sec_key\",\n    ]\n\n    def __init__(self, BDUSS: str = \"\", STOKEN: str = \"\") -> None:\n        self.BDUSS = BDUSS\n        self.STOKEN = STOKEN\n\n        self._tbs: str = None\n        self._android_id: str = None\n        self._uuid: str = None\n        self._client_id: str = None\n        self._sample_id: str = None\n        self._cuid: str = None\n        self._cuid_galaxy2: str = None\n        self._c3_aid: str = None\n        self._z_id: str = None\n        self._aes_ecb_sec_key: bytes = None\n        self._aes_ecb_chiper = None\n        self._aes_cbc_sec_key: bytes = None\n        self._aes_cbc_chiper = None\n\n    def __repr__(self) -> str:\n        return str(self.to_dict())\n\n    def __hash__(self) -> int:\n        return hash(self.BDUSS)\n\n    def __eq__(self, obj: Account) -> bool:\n        return self.BDUSS == obj.BDUSS\n\n    def to_dict(self) -> dict[str, str | bytes]:\n        \"\"\"\n        将Account转换为字典\n\n        Returns:\n            dict[str, str | bytes]: 包含用户参数的字典\n        \"\"\"\n\n        dic = {}\n\n        for key in self.__serialize__:\n            value = getattr(self, key)\n            if not value:\n                continue\n            key = key[1:]\n            dic[key] = value\n\n        return dic\n\n    @staticmethod\n    def from_dict(dic: dict[str, str | bytes]) -> Account:\n        \"\"\"\n        将字典转换为Account\n\n        Args:\n            dic (Dict[str, str | bytes]): 包含用户参数的字典\n\n        Returns:\n            Account: 用户参数容器\n        \"\"\"\n\n        account = Account()\n\n        for key, value in dic.items():\n            if not value:\n                continue\n            key = \"_\" + key\n            setattr(account, key, value)\n\n        return account\n\n    @property\n    def BDUSS(self) -> str:\n        \"\"\"\n        当前账号的BDUSS\n        \"\"\"\n\n        return self._BDUSS\n\n    @BDUSS.setter\n    def BDUSS(self, new_BDUSS: str) -> None:\n        if new_BDUSS and len(new_BDUSS) != 192:\n            raise ValueError(f\"BDUSS的长度应为192个字符 而输入的{new_BDUSS}有{len(new_BDUSS)}个字符\")\n        self._BDUSS = new_BDUSS\n\n    @property\n    def STOKEN(self) -> str:\n        \"\"\"\n        当前账号的STOKEN\n        \"\"\"\n\n        return self._STOKEN\n\n    @STOKEN.setter\n    def STOKEN(self, new_STOKEN: str) -> None:\n        if new_STOKEN and len(new_STOKEN) != 64:\n            raise ValueError(f\"STOKEN的长度应为64个字符 而输入的{new_STOKEN}有{len(new_STOKEN)}个字符\")\n        self._STOKEN = new_STOKEN\n\n    @property\n    def android_id(self) -> str:\n        \"\"\"\n        返回一个随机的android_id\n\n        Returns:\n            str: 长度为16的16进制字符串 包含8字节信息 字母为小写\n\n        Examples:\n            91be894d01799c49\n\n        Note:\n            在初始化后该属性便不会再发生变化\n        \"\"\"\n\n        if self._android_id is None:\n            self._android_id = random.randbytes(8).hex()\n        return self._android_id\n\n    @android_id.setter\n    def android_id(self, new_android_id: str) -> None:\n        self._android_id = new_android_id\n\n    @property\n    def uuid(self) -> str:\n        \"\"\"\n        使用uuid.uuid4生成并返回一个随机的uuid\n\n        Returns:\n            str: 包含16字节信息\n\n        Examples:\n            e4200716-58a8-4170-af15-ea7edeb8e513\n\n        Note:\n            在初始化后该属性便不会再发生变化\n        \"\"\"\n\n        if self._uuid is None:\n            import uuid\n\n            self._uuid = str(uuid.uuid4())\n\n        return self._uuid\n\n    @uuid.setter\n    def uuid(self, new_uuid: str) -> None:\n        self._uuid = new_uuid\n\n    @property\n    def tbs(self) -> str:\n        \"\"\"\n        返回一个可作为请求参数的反csrf校验码tbs\n\n        Returns:\n            str: 长度为26的16进制字符串 字母为小写\n\n        Examples:\n            17634e03cbe25e6e1674526199\n\n        Note:\n            在初始化后该属性便不会再发生变化\n        \"\"\"\n\n        return self._tbs\n\n    @tbs.setter\n    def tbs(self, new_tbs: str) -> None:\n        self._tbs = new_tbs\n\n    @property\n    def client_id(self) -> str:\n        \"\"\"\n        返回一个可作为请求参数的client_id\n\n        Returns:\n            str\n\n        Examples:\n            wappc_1653660000000_123\n\n        Note:\n            在初始化后该属性便不会再发生变化\n        \"\"\"\n\n        return self._client_id\n\n    @client_id.setter\n    def client_id(self, new_client_id: str) -> None:\n        self._client_id = new_client_id\n\n    @property\n    def sample_id(self) -> str:\n        \"\"\"\n        返回一个可作为请求参数的sample_id\n\n        Returns:\n            str\n\n        Examples:\n            104505_3-105324_2-...-107269_1\n\n        Note:\n            在初始化后该属性便不会再发生变化\n        \"\"\"\n\n        return self._sample_id\n\n    @sample_id.setter\n    def sample_id(self, new_sample_id: str) -> None:\n        self._sample_id = new_sample_id\n\n    @property\n    def cuid(self) -> str:\n        \"\"\"\n        返回一个可作为请求参数的cuid\n\n        Returns:\n            str\n\n        Examples:\n            baidutiebaappe4200716-58a8-4170-af15-ea7edeb8e513\n\n        Note:\n            在初始化后该属性便不会再发生变化\\n\n            此实现仅用于9.x等旧版本 11.x后请使用cuid_galaxy2填充对应字段\n        \"\"\"\n\n        if self._cuid is None:\n            self._cuid = f\"baidutiebaapp{self.uuid}\"\n        return self._cuid\n\n    @cuid.setter\n    def cuid(self, new_cuid: str) -> None:\n        self._cuid = new_cuid\n\n    @property\n    def cuid_galaxy2(self) -> str:\n        \"\"\"\n        返回一个可作为请求参数的cuid_galaxy2\n\n        Returns:\n            str\n\n        Examples:\n            A3ED2D7B9CFC28E8934A3FBD3A9579C7|VZ5FKB5XS\n\n        Note:\n            在初始化后该属性便不会再发生变化\\n\n            此实现与12.x版本及以前的官方实现一致\n        \"\"\"\n\n        if self._cuid_galaxy2 is None:\n            self._cuid_galaxy2 = cuid_galaxy2(self.android_id)\n        return self._cuid_galaxy2\n\n    @cuid_galaxy2.setter\n    def cuid_galaxy2(self, new_cuid_galaxy2: str) -> None:\n        self._cuid_galaxy2 = new_cuid_galaxy2\n\n    @property\n    def c3_aid(self) -> str:\n        \"\"\"\n        返回一个可作为请求参数的c3_aid\n\n        Returns:\n            str\n\n        Examples:\n            A00-ZNU3O3EP74D727LMQY745CZSGZQJQZGP-3JXCKC7X\n\n        Note:\n            在初始化后该属性便不会再发生变化\\n\n            此实现与12.x版本及以前的官方实现一致\n        \"\"\"\n\n        if self._c3_aid is None:\n            self._c3_aid = c3_aid(self.android_id, self.uuid)\n        return self._c3_aid\n\n    @c3_aid.setter\n    def c3_aid(self, new_c3_aid: str) -> None:\n        self._c3_aid = new_c3_aid\n\n    @property\n    def z_id(self) -> str:\n        \"\"\"\n        返回一个可作为请求参数的z_id\n\n        Returns:\n            str\n\n        Note:\n            在初始化后该属性便不会再发生变化\\n\n            此实现与12.x版本及以前的官方实现一致\n        \"\"\"\n\n        return self._z_id\n\n    @z_id.setter\n    def z_id(self, new_z_id: str) -> None:\n        self._z_id = new_z_id\n\n    @property\n    def aes_ecb_sec_key(self) -> bytes:\n        \"\"\"\n        返回一个供贴吧AES-ECB加密使用的随机密码\n\n        Returns:\n            bytes: 长度为31字节的随机密码\n\n        Note:\n            在初始化后该属性便不会再发生变化\n        \"\"\"\n\n        if self._aes_ecb_sec_key is None:\n            self._aes_ecb_sec_key = random.randbytes(31)\n        return self._aes_ecb_sec_key\n\n    @aes_ecb_sec_key.setter\n    def aes_ecb_sec_key(self, new_aes_ecb_sec_key: bytes) -> None:\n        self._aes_ecb_sec_key = new_aes_ecb_sec_key\n\n    @property\n    def aes_ecb_chiper(self) -> Cipher[modes.ECB]:\n        \"\"\"\n        获取供贴吧websocket使用的AES-ECB加密器\n\n        Returns:\n            Cipher[ECB]: AES-ECB加密器\n        \"\"\"\n\n        if self._aes_ecb_chiper is None:\n            salt = b\"\\xa4\\x0b\\xc8\\x34\\xd6\\x95\\xf3\\x13\"\n            kdf = PBKDF2HMAC(hashes.SHA1(), 32, salt, 5)\n            ws_secret_key = kdf.derive(self.aes_ecb_sec_key)\n            self._aes_ecb_chiper = Cipher(algorithms.AES(ws_secret_key), modes.ECB())\n\n        return self._aes_ecb_chiper\n\n    @property\n    def aes_cbc_sec_key(self) -> bytes:\n        \"\"\"\n        返回一个供贴吧AES-CBC加密使用的随机密码\n\n        Returns:\n            bytes: 长度为16字节的随机密码\n\n        Note:\n            在初始化后该属性便不会再发生变化\n        \"\"\"\n\n        if self._aes_cbc_sec_key is None:\n            self._aes_cbc_sec_key = random.randbytes(16)\n        return self._aes_cbc_sec_key\n\n    @aes_ecb_sec_key.setter\n    def aes_ecb_sec_key(self, new_aes_ecb_sec_key: bytes) -> None:\n        self._aes_ecb_sec_key = new_aes_ecb_sec_key\n\n    @property\n    def aes_cbc_chiper(self) -> Cipher[modes.CBC]:\n        \"\"\"\n        获取供贴吧客户端使用的AES-CBC加密器\n\n        Returns:\n            Cipher[CBC]: AES-CBC加密器\n        \"\"\"\n\n        if self._aes_cbc_chiper is None:\n            iv = b\"\\x00\" * 16\n            self._aes_cbc_chiper = Cipher(algorithms.AES(self.aes_cbc_sec_key), modes.CBC(iv))\n\n        return self._aes_cbc_chiper\n"
  },
  {
    "path": "src/aiotieba/core/blcp.py",
    "content": "# ruff: noqa: B904, E722\n\nfrom __future__ import annotations\n\nimport asyncio\nimport base64\nimport dataclasses as dcs\nimport gzip\nimport json\nimport random\nimport socket\nimport ssl\nimport time\nimport urllib.parse\nimport weakref\nfrom asyncio import IncompleteReadError, Queue, StreamReader, StreamWriter\nfrom hashlib import md5\nfrom sys import maxsize as LongMax\nfrom typing import TYPE_CHECKING\n\nimport aiohttp\nimport yarl\nfrom cryptography.hazmat.primitives import padding\nfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\n\nfrom ..api._protobuf import Lcm_pb2, Rpc_pb2\nfrom ..config import ProxyConfig, TimeoutConfig\nfrom ..const import CHAT_APPID, CHAT_SDK_VERSION, CHAT_VERSION\nfrom ..helper import timeout\nfrom ..helper.crypto import enuid\n\nif TYPE_CHECKING:\n    from ..api._classdef import UserInfo\n    from .account import Account\n    from .net import NetCore\n\n\n@dcs.dataclass\nclass BLCPCore:\n    \"\"\"\n    网络请求相关容器\n\n    Args:\n        proxy (ProxyConfig, optional): 代理配置. Defaults to None.\n        timeout (TimeoutConfig, optional): 超时配置. Defaults to None.\n    \"\"\"\n\n    account: Account\n    user: UserInfo\n    reader: StreamReader\n    writer: StreamWriter\n    loop: asyncio.AbstractEventLoop\n    proxy: ProxyConfig\n    timeout: TimeoutConfig\n    blcp_dispatcher: asyncio.Task\n    blcp_responses: ClientBLCPResponses\n    waiter: BLCPWaiter\n    net_core: NetCore\n    trigger_id: int\n    message_queue: Queue  # 该消息队列只有群聊消息，没有各种握手\n    heartbeater: asyncio.Task\n\n    def __init__(\n        self,\n        proxy: ProxyConfig | None = None,\n        timeout: TimeoutConfig | None = None,\n        loop: asyncio.AbstractEventLoop = None,\n        net_core: NetCore = None,\n        account: Account = None,\n        user: UserInfo = None,\n        max_queue_length: int = 100,\n    ) -> None:\n        if not isinstance(proxy, ProxyConfig):\n            proxy = ProxyConfig()\n        self.proxy = proxy\n\n        if not isinstance(timeout, TimeoutConfig):\n            timeout = TimeoutConfig()\n        self.timeout = timeout\n        if not loop:\n            self.loop = asyncio.get_running_loop()\n        else:\n            self.loop = loop\n        self.blcp_dispatcher: asyncio.Task = None\n        self.heartbeater = None\n        self.net_core = net_core\n        self.account = account\n        self.trigger_id = -1\n        self.user = user\n        self.status = -1\n        self.message_queue = Queue(maxsize=max_queue_length)\n\n    def set_account(self, new_account: Account) -> None:\n        self.account = new_account\n\n    async def connect(self) -> None:\n        self.waiter = BLCPWaiter(5)\n\n        context = ssl.create_default_context()\n        context.check_hostname = False\n\n        # print(\"connecting\")\n\n        try:\n            reader, writer = await asyncio.open_connection(\n                \"common.lcs.baidu.com\", 443, ssl=context, family=socket.AF_INET\n            )  # 实际上可以支持ipv6\n        except BaseException:\n            raise\n        else:\n            self.reader = reader\n            self.writer = writer\n\n        if self.blcp_dispatcher is not None and not self.blcp_dispatcher.done():\n            self.blcp_dispatcher.cancel()\n\n        self.blcp_responses = ClientBLCPResponses(reader=reader, writer=writer)\n        self.blcp_dispatcher = self.loop.create_task(self.__blcp_dispatch(), name=\"blcp_dispatcher\")\n\n        self.status = 0\n\n    async def login(self) -> None:\n        cuid_galaxy2 = self.account.cuid_galaxy2\n        blcp_token = await self.generate_lcm_token(cuid_galaxy2)\n\n        # 握手\n        loginBLCPRequest = BLCPData(serviceId=1, methodId=1)\n        loginBLCPRequest.RpcBody = self.buildRpcBody(\n            serviceId=1, methodId=1, correlationId=loginBLCPRequest.correlationId, need_common=1\n        )\n        LcmBody = Lcm_pb2.RpcData()\n        LcmBody.lcm_request.log_id = loginBLCPRequest.correlationId\n        LcmBody.lcm_request.token = blcp_token\n        LcmBody.lcm_request.common.cuid = cuid_galaxy2\n        LcmBody.lcm_request.common.device = \"android\"\n        LcmBody.lcm_request.common.app_id = str(CHAT_APPID)\n        LcmBody.lcm_request.common.app_version = CHAT_VERSION\n        LcmBody.lcm_request.common.sdk_version = \"3460016\"\n        LcmBody.lcm_request.common.network = \"wifi\"\n        LcmBody.lcm_request.timestamp = int(time.time() * 1000)\n        LcmBody.lcm_request.start_type = -1\n        LcmBody.lcm_request.conn_type = 1\n        loginBLCPRequest.LcmBody = LcmBody.SerializeToString()\n\n        response = self.waiter.new(loginBLCPRequest.correlationId)\n        self.writer.write(loginBLCPRequest.toBytes())\n        reps = await response.read()\n        try:\n            rpc, lcm = ClientBLCPResponses.parseBLCPResponse(reps.toBytes())\n        except:\n            raise Exception(\"BLCP Handshake error\")  # TODO: 特殊的握手错误\n\n        if rpc.response.error_text != \"success\" or lcm.lcm_response.error_msg != \"success\":\n            raise Exception(\"BLCP Handshake error\")  # TODO: 特殊的握手错误\n\n        # 第二部分登陆\n        # 构造部分位于 com.baidu.searchbox.cloudcontrolblcp.CloudControlBlCPManager\n\n        # 参数来源在com.baidu.common.param.CommonUrlParamManager中找到\n        sid = self.account.sample_id\n        ua = \"900_1600_android_12.68.1.0_240\"  # 分辨率\n        uid = enuid(cuid_galaxy2)  # Enuid，由cuid_galaxy2经过libbase64encoder_v2_0.so得到\n        login_from = \"1008550l\"  # baidu_imsdk_common_data.xml\n        cfrom = login_from\n        c3_aid = self.account.c3_aid\n        reqdict = {\n            \"params\": {\n                \"appname\": \"tieba\",\n                \"sid\": sid,\n                \"ua\": ua,\n                \"uid\": uid,\n                \"cfrom\": cfrom,\n                \"from\": login_from,\n                \"network\": \"1_-1\",\n                \"p_sv\": \"32\",\n                \"mps\": \"\",\n                \"mpv\": \"1\",\n                \"c3_aid\": c3_aid,\n                \"type_id\": \"0\",\n            },\n            \"filter\": {\"aps\": {\"cpu_abi\": \"armeabi-v7a\"}, \"command\": {\"step\": \"0\"}},\n        }\n\n        loginBLCPRequest = BLCPData(serviceId=4, methodId=1)\n        loginBLCPRequest.RpcBody = self.buildRpcBody(\n            serviceId=4, methodId=1, correlationId=loginBLCPRequest.correlationId\n        )\n        loginBLCPRequest.LcmBody = json.dumps(reqdict).encode()\n        response = self.waiter.new(loginBLCPRequest.correlationId)\n        self.writer.write(loginBLCPRequest.toBytes())\n        reps = await response.read()\n\n        try:\n            rpc, lcm = ClientBLCPResponses.parseBLCPResponse(reps.toBytes())\n        except:\n            raise Exception(\"BLCP Handshake error.\")  # TODO: 特殊的握手错误\n\n        if rpc.response.error_text != \"success\" or lcm.get(\"errno\") != \"0\":\n            raise Exception(\"BLCP Handshake error.\")  # TODO: 特殊的握手错误\n\n        loginBLCPRequest = BLCPData(serviceId=2, methodId=50)\n        loginBLCPRequest.correlationId = 2000003149381050\n        loginBLCPRequest.RpcBody = self.buildRpcBody(\n            serviceId=2, methodId=50, correlationId=loginBLCPRequest.correlationId\n        )\n\n        client_identifier = {\"zid\": \"\", \"version_code\": \"\"}  # 待完善，目前可以不携带zid\n        cookie = \"\"  # 待完善，目前可以不携带cookie\n        token = self.account.BDUSS\n\n        reqdict = {\n            \"method\": 50,\n            \"appid\": CHAT_APPID,\n            \"device_id\": \"android_\" + cuid_galaxy2,\n            \"account_type\": 1,\n            \"token\": token,\n            \"version\": 4,\n            \"sdk_version\": CHAT_SDK_VERSION,\n            \"app_version\": CHAT_VERSION,\n            \"app_open_type\": 0,\n            \"client_identifier\": json.dumps(client_identifier),\n            \"tail\": 0,\n            \"timeout\": 10,\n            \"cookie\": cookie,\n            \"device_info\": {\n                \"app_version\": CHAT_VERSION,\n                \"os_version\": \"32\",\n                \"platform\": \"android\",\n                \"appid\": str(CHAT_APPID),\n                \"from\": login_from,\n                \"cfrom\": cfrom,\n            },\n            \"rpc\": json.dumps({\"rpc_retry_time\": 0}),\n            \"user_type\": 0,\n            \"client_logid\": int(time.time() * 1000 * 1000),\n        }\n\n        loginBLCPRequest.LcmBody = json.dumps(reqdict).encode()\n        response = self.waiter.new(loginBLCPRequest.correlationId)\n        self.writer.write(loginBLCPRequest.toBytes())\n        reps = await response.read()\n\n        try:\n            rpc, lcm = ClientBLCPResponses.parseBLCPResponse(reps.toBytes())\n        except:\n            raise Exception(\"BLCP Handshake error.\")  # TODO: 特殊的握手错误\n\n        if rpc.response.error_text != \"success\" or lcm.get(\"err_code\") != 0:\n            raise Exception(\"BLCP Handshake error.\")  # TODO: 特殊的握手错误\n\n        rep = json.loads(reps.LcmBody)\n\n        self.user.trigger_id = rep[\"trigger_id\"][0]\n        self.user.uk = rep[\"uk\"]\n        self.user.bduk = rep[\"bd_uid\"]\n        self.login_id = rep[\"login_id\"]\n\n        self.status = 1\n\n    async def generate_lcm_token(self, cuid_galaxy2):\n        headers = {\n            \"Content-Type\": \"application/json\",\n            \"Accept-Encoding\": \"gzip\",\n            \"User-Agent\": \"okhttp/3.11.0\",\n            \"Host\": \"pim.baidu.com\",\n        }\n        request_id = str(int(time.time() * 1000))\n        ts = int(time.time() * 1000)\n        data = {\n            \"app_id\": str(CHAT_APPID),\n            \"app_version\": CHAT_VERSION,\n            \"cuid\": cuid_galaxy2,\n            \"device_type\": \"android\",\n            \"manufacture\": \"\",\n            \"model_type\": \"\",\n            \"request_id\": request_id,\n            \"sdk_version\": \"3460016\",\n            \"sign\": md5((str(CHAT_APPID) + cuid_galaxy2 + \"android\" + str(ts)).encode()).hexdigest(),  # appid\n            \"ts\": ts,\n            \"user_key\": \"\",\n        }\n        request = aiohttp.ClientRequest(\n            \"POST\",\n            yarl.URL.build(scheme=\"https\", host=\"pim.baidu.com\", path=\"/rest/5.0/generate_lcm_token\", port=443),\n            headers=headers,\n            # proxy=self.net_core.proxy.url,\n            # proxy_auth=self.net_core.proxy.auth,\n            # ssl=False,\n            data=json.dumps(data),\n        )  # TODO: 支持代理\n\n        try:\n            response = await self.net_core.req2res(request, False, 2 * 1024)\n            rjson = await response.json()\n            return rjson[\"token\"]\n        except Exception:\n            return \"\"\n\n    async def groupchat(self):  # 模拟正常请求，暂不清楚作用\n        headers = {\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n            \"Accept-Encoding\": \"gzip\",\n            \"User-Agent\": \"okhttp/3.11.0\",\n            \"Host\": \"pim.baidu.com\",\n            \"Cookie\": \"BDUSS=\" + self.account.BDUSS,\n        }\n        ts = int(time.time())\n        data = {\n            \"method\": \"get_joined_groups\",\n            \"appid\": CHAT_APPID,\n            \"timestamp\": ts,\n            \"sign\": md5((str(ts) + self.account.BDUSS + str(CHAT_APPID)).encode()).hexdigest(),\n        }\n        request = aiohttp.ClientRequest(\n            \"POST\",\n            yarl.URL.build(scheme=\"https\", host=\"pim.baidu.com\", path=\"/rest/2.0/im/groupchat\", port=443),\n            headers=headers,\n            # proxy=self.net_core.proxy.url,\n            # proxy_auth=self.net_core.proxy.auth,\n            # ssl=False,\n            data=urllib.parse.urlencode(data),\n        )  # TODO: 支持代理\n\n        try:\n            response = await self.net_core.req2res(request, False, 2 * 1024)\n            await response.json()\n        except Exception:\n            return \"\"\n\n    async def groupchatv1(self):  # 模拟正常请求，暂不清楚作用\n        headers = {\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n            \"Accept-Encoding\": \"gzip\",\n            \"User-Agent\": \"okhttp/3.11.0\",\n            \"Host\": \"pim.baidu.com\",\n            \"Cookie\": \"BDUSS=\" + self.account.BDUSS,\n        }\n        ts = int(time.time())\n        data = {\n            \"method\": \"get_joined_groups\",\n            \"group_type\": 3,\n            \"appid\": CHAT_APPID,\n            \"source\": 0,\n            \"cuid\": self.account.cuid_galaxy2,\n            \"app_version\": CHAT_VERSION,\n            \"sdk_version\": str(CHAT_SDK_VERSION),\n            \"timestamp\": ts,\n            \"device_type\": 2,\n            \"sign\": md5((str(ts) + self.account.BDUSS + str(CHAT_APPID)).encode()).hexdigest(),\n        }\n        request = aiohttp.ClientRequest(\n            \"POST\",\n            yarl.URL.build(scheme=\"https\", host=\"pim.baidu.com\", path=\"/rest/2.0/im/groupchatv1\", port=443),\n            headers=headers,\n            # proxy=self.net_core.proxy.url,\n            # proxy_auth=self.net_core.proxy.auth,\n            # ssl=False,\n            data=urllib.parse.urlencode(data),\n        )  # TODO: 支持代理\n\n        try:\n            response = await self.net_core.req2res(request, False, 2 * 1024)\n            await response.json()\n        except Exception:\n            return \"\"\n\n    @staticmethod\n    def getBDUKfromUserId(user_id: str):\n        padder = padding.PKCS7(algorithms.AES.block_size).padder()\n        padded_req_body = padder.update(user_id.encode()) + padder.finalize()\n\n        aes_encryptor = Cipher(algorithms.AES(b\"AFD311832EDEEAEF\"), modes.CBC(b\"2011121211143000\")).encryptor()\n        req_body_aes = aes_encryptor.update(padded_req_body) + aes_encryptor.finalize()\n\n        return base64.urlsafe_b64encode(req_body_aes).strip(b\"=\").decode()\n\n    @staticmethod\n    def getmsgkey(bduk: str):\n        return bduk + str(int(time.time() * 1000) * 1000) + f\"{random.randint(-LongMax, LongMax)}\"\n\n    @staticmethod\n    def buildRpcBody(serviceId, methodId, correlationId, compress_type=0, need_common=1):\n        event_timestamp = Rpc_pb2.EventTimestamp()\n        event_timestamp.event = \"CLCPReqBegin\"\n        event_timestamp.timestamp_ms = int(time.time() * 1000)\n\n        RpcRequestMeta = Rpc_pb2.RpcRequestMeta()\n        RpcRequestMeta.log_id = correlationId\n        RpcRequestMeta.service_id = serviceId\n        RpcRequestMeta.method_id = methodId\n        RpcRequestMeta.need_common = need_common\n        RpcRequestMeta.event_list.append(event_timestamp)\n\n        RpcMeta = Rpc_pb2.RpcMeta()\n        RpcMeta.request.CopyFrom(RpcRequestMeta)\n        RpcMeta.correlation_id = correlationId\n        RpcMeta.compress_type = compress_type  # i\n        RpcMeta.accept_compress_type = 1\n\n        return RpcMeta.SerializeToString()\n\n    async def __blcp_dispatch(self) -> None:\n        async for msg in self.blcp_responses:\n            if not msg:\n                continue\n            self.waiter.set_done(msg.correlationId, msg)\n            # TODO: 加上callbacks以处理服务端主动发送的消息，如群聊消息等。获取群聊消息推送需要先发包绑定群聊。\n            if msg.isNotify:\n                rpc, lcm = ClientBLCPResponses.parseBLCPResponse(\n                    msg.toBytes()\n                )  # TODO:优化。这里在解码后又编码再解码了一次\n                if self.message_queue.full():\n                    await self.message_queue.get()  # 队满自动丢弃\n                await self.message_queue.put(lcm)\n\n        self.status = -1\n        raise Exception(\"IM服务端断开连接\")\n\n    async def joinChatRoom(self, chatroom_id: int) -> bool:\n        if self.status != 1:\n            raise Exception(\"IM未登陆\")\n        request = BLCPData(serviceId=3, methodId=201)  # 加入Chatroom的请求\n        request.RpcBody = self.buildRpcBody(serviceId=3, methodId=201, correlationId=request.correlationId)\n        reqdict = {\n            \"method\": 201,\n            \"mcast_id\": chatroom_id,\n            \"appid\": CHAT_APPID,\n            \"uk\": self.user.uk,\n            \"origin_id\": self.user.trigger_id,\n            \"msg_key\": \"k\" + str(int(time.time() * 100000)),\n            \"sdk_version\": CHAT_SDK_VERSION,\n            \"is_reliable\": False,\n            \"client_logid\": int(time.time() * 1000 * 1000),\n            \"rpc\": json.dumps({\"rpc_retry_time\": 0}),\n        }\n        request.LcmBody = json.dumps(reqdict).encode()\n        response = self.waiter.new(request.correlationId)\n        self.writer.write(request.toBytes())\n        reps = await response.read()\n\n        try:\n            rpc, lcm = ClientBLCPResponses.parseBLCPResponse(reps.toBytes())\n        except:\n            raise Exception(\"BLCP Handshake error.\")  # TODO: 特殊的握手错误\n        if rpc.response.error_text != \"success\" or lcm.get(\"err_code\") != 0:\n            raise Exception(\"BLCP Handshake error.\")  # TODO: 特殊的握手错误\n\n        await self.fetch_mcast_msg_client_request(self.account.cuid_galaxy2, chatroom_id)  # TODO:获取历史消息\n\n        if self.heartbeater is not None and not self.heartbeater.done():\n            self.heartbeater.cancel()\n\n        self.heartbeater = self.loop.create_task(self.__heartbeater(), name=\"heartbeater\")\n\n        return True\n\n    async def __heartbeater(self, freq: int = 5):\n        while True:\n            await asyncio.sleep(freq)\n            await self.heartbeat()\n\n    async def exitChatRoom(self, chatroom_id: int, room_type: int) -> bool:\n        # 待实现\n        pass\n\n    async def heartbeat(self):\n        request = BLCPData(serviceId=1, methodId=3)\n        request.RpcBody = self.buildRpcBody(serviceId=1, methodId=3, correlationId=request.correlationId)\n        LcmBody = Lcm_pb2.RpcData()\n        LcmBody.lcm_request.log_id = request.correlationId\n        LcmBody.lcm_request.timestamp = request.timestamp\n        request.LcmBody = LcmBody.SerializeToString()\n        self.writer.write(request.toBytes())\n\n    async def enter_chatroom_client_request(\n        self, cuid_galaxy2: str, room_id: int, account_type: int = 1\n    ):  # 模拟正常请求，暂不清楚作用\n        headers = {\n            \"Content-Type\": \"application/json\",\n            \"Accept-Encoding\": \"gzip\",\n            \"User-Agent\": \"okhttp/3.11.0\",\n            \"Host\": \"pim.baidu.com\",\n            \"Cookie\": \"BDUSS=\" + self.account.BDUSS,\n        }\n        data = {\n            \"appid\": CHAT_APPID,\n            \"room_id\": room_id,\n            \"app_version\": CHAT_VERSION,\n            \"cuid\": cuid_galaxy2,\n            \"device_id\": cuid_galaxy2,\n            \"sdk_version\": CHAT_SDK_VERSION,\n            \"timestamp\": int(time.time()),\n            \"account_type\": account_type,\n        }\n        data[\"sign\"] = generate_sign(data)\n\n        request = aiohttp.ClientRequest(\n            \"POST\",\n            yarl.URL.build(\n                scheme=\"https\", host=\"pim.baidu.com\", path=\"/rest/3.0/im/chatroom/enter_chatroom_client\", port=443\n            ),\n            headers=headers,\n            # proxy=self.net_core.proxy.url,\n            # proxy_auth=self.net_core.proxy.auth,\n            # ssl=False,\n            data=json.dumps(data),\n        )  # TODO: 支持代理\n\n        try:\n            response = await self.net_core.req2res(request, False, 2 * 1024)\n            await response.read()\n            rjson = await response.json()\n        except:\n            raise Exception(\"进入群聊失败.\")\n        return rjson\n\n    async def fetch_mcast_msg_client_request(\n        self, cuid_galaxy2: str, room_id: int, account_type: int = 1\n    ):  # 该方法可以获取历史消息，暂未继续开发\n        headers = {\n            \"Content-Type\": \"application/json\",\n            \"Accept-Encoding\": \"gzip\",\n            \"User-Agent\": \"okhttp/3.11.0\",\n            \"Host\": \"pim.baidu.com\",\n            \"Cookie\": \"BDUSS=\" + self.account.BDUSS,\n        }\n        data = {\n            \"appid\": CHAT_APPID,\n            \"mcast_id\": room_id,\n            \"msgid_begin\": 0,\n            \"msgid_end\": 9223372036854775807,\n            \"count\": -60,\n            \"category\": 4,\n            \"app_version\": CHAT_VERSION,\n            \"sdk_version\": CHAT_SDK_VERSION,\n            \"device_id\": cuid_galaxy2,\n            \"device_type\": 2,\n            \"from_action\": 1,\n            \"ext_info\": urllib.parse.quote(\n                json.dumps({\"last_callback_msg_id\": 0, \"cast_id\": 0, \"local_ts\": 0, \"latest_msg_id\": 0})\n            ),\n            \"timestamp\": int(time.time()),\n            \"account_type\": account_type,\n        }\n        data[\"sign\"] = generate_sign(data)\n\n        request = aiohttp.ClientRequest(\n            \"POST\",\n            yarl.URL.build(scheme=\"https\", host=\"pim.baidu.com\", path=\"/rest/3.0/im/fetch_mcast_msg_client\", port=443),\n            headers=headers,\n            # proxy=self.net_core.proxy.url,\n            # proxy_auth=self.net_core.proxy.auth,\n            # ssl=False,\n            data=json.dumps(data),\n        )  # TODO: 支持代理\n\n        try:\n            response = await self.net_core.req2res(request, False, 2 * 1024)\n            await response.read()\n            rjson = await response.json()\n        except:\n            raise Exception(\"进入群聊失败.\")\n        return rjson\n\n\ndef generate_sign(json_obj):\n    if json_obj is None:\n        return \"\"\n\n    # 按照字典序排序键值对\n    sorted_items = sorted(json_obj.items())\n\n    # 构造签名字符串\n    sign_str = \"\".join(f\"{key}={value}\" for key, value in sorted_items)\n\n    # 计算MD5签名\n    return md5(sign_str.encode(\"utf-8\")).hexdigest()\n\n\n@dcs.dataclass\nclass BLCPData:\n    serviceId: int\n    methodId: int\n    RpcBody: bytes\n    LcmBody: bytes\n    timestamp: int\n    ifRequest: bool\n    correlationId: int\n    isNotify: bool\n\n    def __init__(\n        self,\n        serviceId: int,\n        methodId: int,\n        RpcBody: bytes = None,\n        LcmBody: bytes = None,\n        timestamp: int = None,\n        ifRequest: bool = True,\n        isNotify: bool = False,\n    ):\n        self.serviceId = serviceId\n        self.methodId = methodId\n        self.RpcBody = RpcBody\n        self.ifRequest = ifRequest\n        self.LcmBody = LcmBody\n        self.isNotify = isNotify\n\n        if timestamp is None:\n            self.timestamp = int(time.time() * 1000)\n        else:\n            self.timestamp = timestamp\n\n        self.correlationId = random.randint(0, LongMax)\n\n    def toBytes(self):\n        buffer = bytearray()\n        buffer.extend(b\"lcp\\x01\")\n        buffer.extend(int.to_bytes(len(self.RpcBody) + len(self.LcmBody), 4, \"big\"))\n        buffer.extend(int.to_bytes(len(self.RpcBody), 4, \"big\"))\n        buffer.extend(self.RpcBody)\n        buffer.extend(self.LcmBody)\n\n        return bytes(buffer)\n\n\nclass ClientBLCPResponses:\n    def __init__(self, reader: StreamReader, writer: StreamWriter):\n        self.reader = reader\n        self.writer = writer\n\n    def __aiter__(self) -> ClientBLCPResponses:\n        return self\n\n    async def __anext__(self):\n        rBytes = bytearray()\n        try:\n            rBytes += await self.reader.readuntil(b\"lcp\\x01\")\n        except IncompleteReadError:\n            raise StopAsyncIteration(\"BLCP连接被关闭\")  # 连接被关闭，reader会得到EOF返回空字节而不是阻塞。\n        else:\n            length_bytes = await self.reader.read(8)\n            rBytes += length_bytes\n            all_length = int.from_bytes(length_bytes[0:4], byteorder=\"big\")\n            rBytes += await self.reader.read(all_length)\n\n        try:\n            RpcMeta, Lcm = self.parseBLCPResponse(rBytes)\n        except:\n            return None\n        RpcMeta: Rpc_pb2.RpcMeta\n        if not RpcMeta or not Lcm:\n            return None\n        service_id = RpcMeta.response.service_id\n        method_id = RpcMeta.response.method_id\n        correlation_id = RpcMeta.correlation_id\n\n        responseBLCP = BLCPData(serviceId=service_id, methodId=method_id)\n        responseBLCP.correlationId = correlation_id\n        responseBLCP.RpcBody = RpcMeta.SerializeToString()\n        if RpcMeta.notify and (method_id != 3 and service_id != 1):  # 屏蔽心跳包\n            responseBLCP.isNotify = True\n\n        if isinstance(Lcm, Lcm_pb2.RpcData):\n            responseBLCP.LcmBody = Lcm.SerializeToString()\n        else:\n            responseBLCP.LcmBody = json.dumps(Lcm).encode()  # TODO:这里又编码回字节了，性能浪费\n        responseBLCP.ifRequest = False\n\n        return responseBLCP\n\n    async def __aenter__(self) -> ClientBLCPResponses:\n        return self\n\n    async def __aexit__(self) -> None:\n        await self.close()\n\n    async def close(self):\n        pass\n\n    @staticmethod\n    def parseBLCPResponse(receivedBytes: bytes) -> (Rpc_pb2.RpcMeta, Lcm_pb2.RpcData):\n        if len(receivedBytes) < 4:\n            return None, None\n        if receivedBytes[0:4] != b\"lcp\\x01\":\n            return None, None\n\n        # 跳过lcp和1\n        receivedBytes = receivedBytes[4:]\n        b1 = int.from_bytes(receivedBytes[0:4], byteorder=\"big\")\n        b2 = int.from_bytes(receivedBytes[4:8], byteorder=\"big\")\n        receivedBytes = receivedBytes[8:]\n\n        barr1 = receivedBytes[0:b2]\n        barr2 = receivedBytes[b2:b1]\n\n        Rpc: Rpc_pb2.RpcMeta = Rpc_pb2.RpcMeta()\n        Rpc.ParseFromString(barr1)\n\n        # TODO: 优化Lcm消息类型判别，目前try处理比较暴力\n        try:\n            Lcm = Lcm_pb2.RpcData()\n            Lcm.ParseFromString(barr2)\n        except:\n            try:\n                Lcm = Lcm_pb2.RpcData()\n                Lcm.ParseFromString(gzip.decompress(barr2))\n            except:\n                try:\n                    Lcm = json.loads(barr2)\n                except:\n                    try:\n                        Lcm = json.loads(gzip.decompress(barr2))\n                    except:\n                        raise ValueError(\"无法解析数据\")\n\n        return Rpc, Lcm\n\n\n@dcs.dataclass\nclass BLCPResponse:\n    \"\"\"\n    BLCP响应\n\n    Args:\n        future (asyncio.Future): 用于等待读事件到来的Future\n        req_id (int): 请求id\n        read_timeout (float): 读超时时间\n    \"\"\"\n\n    loop: asyncio.AbstractEventLoop\n    future: asyncio.Future\n    req_id: int\n    read_timeout: float\n\n    def __init__(self, req_id: int, read_timeout: float) -> None:\n        self.loop = asyncio.get_running_loop()\n        self.future = self.loop.create_future()\n        self.req_id = req_id\n        self.read_timeout = read_timeout\n\n    async def read(self) -> BLCPData:\n        \"\"\"\n        读取BLCP响应\n\n        Returns:\n            BLCPData\n\n        Raises:\n            asyncio.TimeoutError: 读取超时\n        \"\"\"\n\n        try:\n            async with timeout(self.read_timeout, self.loop):\n                return await self.future\n        except asyncio.TimeoutError as err:\n            self.future.cancel()\n            raise asyncio.TimeoutError(\"Timeout to read\") from err\n        except BaseException:\n            self.future.cancel()\n            raise\n\n\n@dcs.dataclass\nclass BLCPWaiter:\n    \"\"\"\n    BLCP等待映射\n    \"\"\"\n\n    loop: asyncio.AbstractEventLoop\n    waiter: weakref.WeakValueDictionary\n    req_id: int\n    read_timeout: float\n\n    def __init__(self, read_timeout: float) -> None:\n        self.loop = asyncio.get_running_loop()\n        self.waiter = weakref.WeakValueDictionary()\n        self.req_id = int(time.time())\n        self.read_timeout = read_timeout\n        weakref.finalize(self, self.__cancel_all)\n\n    def __cancel_all(self) -> None:\n        for ws_resp in self.waiter.values():\n            ws_resp.future.cancel()\n\n    def new(self, req_id: int = None) -> BLCPResponse:\n        \"\"\"\n        创建一个可用于等待数据的响应对象\n\n        Args:\n            req_id (int): 请求id\n\n        Returns:\n            WsResponse: websocket响应\n        \"\"\"\n        if not req_id:\n            self.req_id += 1\n        else:\n            self.req_id = req_id\n\n        blcp_resp = BLCPResponse(self.req_id, self.read_timeout)\n        self.waiter[self.req_id] = blcp_resp\n        return blcp_resp\n\n    def set_done(self, req_id: int, data: BLCPData) -> None:\n        \"\"\"\n        将req_id对应的响应Future设置为已完成\n\n        Args:\n            req_id (int): 请求id\n            data (bytes): 填入的数据\n        \"\"\"\n\n        blcp_resp: BLCPResponse = self.waiter.get(req_id, None)\n        if blcp_resp is None:\n            return\n        blcp_resp.future.set_result(data)\n"
  },
  {
    "path": "src/aiotieba/core/http.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\nimport urllib.parse\nfrom http.cookies import Morsel\nfrom typing import TYPE_CHECKING\n\nimport aiohttp\n\nfrom ..__version__ import __version__\nfrom ..const import APP_BASE_HOST\nfrom ..helper.crypto import sign\n\nif TYPE_CHECKING:\n    import yarl\n\n    from .account import Account\n    from .net import NetCore\n\n\n@dcs.dataclass\nclass HttpContainer:\n    \"\"\"\n    用于保存会话headers与cookies的容器\n    \"\"\"\n\n    headers: dict[str, str]\n    cookie_jar: aiohttp.CookieJar\n\n    def __init__(self, headers: dict[str, str], cookie_jar: aiohttp.CookieJar) -> None:\n        self.headers: dict[str, str] = headers\n        self.cookie_jar: aiohttp.CookieJar = cookie_jar\n\n\n@dcs.dataclass\nclass HttpCore:\n    \"\"\"\n    保存http接口相关状态的核心容器\n    \"\"\"\n\n    account: Account\n    net_core: NetCore\n    app: HttpContainer\n    app_proto: HttpContainer\n    web: HttpContainer\n\n    def __init__(self, account: Account, net_core: NetCore) -> None:\n        self.net_core = net_core\n\n        from aiohttp import hdrs\n\n        app_headers = {\n            hdrs.USER_AGENT: f\"aiotieba/{__version__}\",\n            hdrs.ACCEPT_ENCODING: \"gzip\",\n            hdrs.CONNECTION: \"keep-alive\",\n            hdrs.HOST: APP_BASE_HOST,\n        }\n        self.app = HttpContainer(app_headers, aiohttp.DummyCookieJar())\n\n        app_proto_headers = {\n            hdrs.USER_AGENT: f\"aiotieba/{__version__}\",\n            \"x_bd_data_type\": \"protobuf\",\n            hdrs.ACCEPT_ENCODING: \"gzip\",\n            hdrs.CONNECTION: \"keep-alive\",\n            hdrs.HOST: APP_BASE_HOST,\n        }\n        self.app_proto = HttpContainer(app_proto_headers, aiohttp.DummyCookieJar())\n\n        web_headers = {\n            hdrs.USER_AGENT: f\"aiotieba/{__version__}\",\n            hdrs.ACCEPT_ENCODING: \"gzip, deflate\",\n            hdrs.CACHE_CONTROL: \"no-cache\",\n            hdrs.CONNECTION: \"keep-alive\",\n        }\n        self.web = HttpContainer(web_headers, aiohttp.CookieJar())\n\n        self.set_account(account)\n\n    def set_account(self, new_account: Account) -> None:\n        self.account = new_account\n\n        BDUSS_morsel = Morsel()\n        BDUSS_morsel.set(\"BDUSS\", new_account.BDUSS, new_account.BDUSS)\n        BDUSS_morsel[\"domain\"] = \"baidu.com\"\n        self.web.cookie_jar._cookies[\"baidu.com\", \"\"][\"BDUSS\"] = BDUSS_morsel\n        STOKEN_morsel = Morsel()\n        STOKEN_morsel.set(\"STOKEN\", new_account.STOKEN, new_account.STOKEN)\n        STOKEN_morsel[\"domain\"] = \"tieba.baidu.com\"\n        self.web.cookie_jar._cookies[\"tieba.baidu.com\", \"\"][\"STOKEN\"] = STOKEN_morsel\n\n    def pack_form_request(self, url: yarl.URL, data: list[tuple[str, str]]) -> aiohttp.ClientRequest:\n        \"\"\"\n        自动签名参数元组列表\n        并将其打包为移动端表单请求\n\n        Args:\n            url (yarl.URL): 链接\n            data (list[tuple[str, str]]): 参数元组列表\n\n        Returns:\n            aiohttp.ClientRequest\n        \"\"\"\n\n        payload = aiohttp.payload.BytesPayload(\n            urllib.parse.urlencode(sign(data), doseq=True).encode(\"utf-8\"),\n            content_type=\"application/x-www-form-urlencoded\",\n        )\n\n        request = aiohttp.ClientRequest(\n            aiohttp.hdrs.METH_POST,\n            url,\n            headers=self.app.headers,\n            data=payload,\n            proxy=self.net_core.proxy.url,\n            proxy_auth=self.net_core.proxy.auth,\n            ssl=False,\n        )\n\n        return request\n\n    def pack_proto_request(self, url: yarl.URL, data: bytes) -> aiohttp.ClientRequest:\n        \"\"\"\n        打包移动端protobuf请求\n\n        Args:\n            url (yarl.URL): 链接\n            data (bytes): protobuf序列化后的二进制数据\n\n        Returns:\n            aiohttp.ClientRequest\n        \"\"\"\n\n        writer = aiohttp.MultipartWriter(\"form-data\", boundary=\"-*_r1999\")\n        payload_headers = {\n            aiohttp.hdrs.CONTENT_DISPOSITION: aiohttp.helpers.content_disposition_header(\n                \"form-data\", name=\"data\", filename=\"file\"\n            )\n        }\n        payload = aiohttp.BytesPayload(data, content_type=\"\", headers=payload_headers)\n        payload.headers.popone(aiohttp.hdrs.CONTENT_TYPE)\n        writer._parts.append((payload, None, None))\n\n        request = aiohttp.ClientRequest(\n            aiohttp.hdrs.METH_POST,\n            url,\n            headers=self.app_proto.headers,\n            data=writer,\n            proxy=self.net_core.proxy.url,\n            proxy_auth=self.net_core.proxy.auth,\n            ssl=False,\n        )\n\n        return request\n\n    def pack_web_get_request(\n        self, url: yarl.URL, params: list[tuple[str, str]], *, extra_headers: list[tuple[str, str]] | None = None\n    ) -> aiohttp.ClientRequest:\n        \"\"\"\n        打包网页端参数请求\n\n        Args:\n            url (yarl.URL): 链接\n            params (list[tuple[str, str]]): 参数元组列表\n            extra_headers (list[tuple[str, str]]): 额外的请求头\n\n        Returns:\n            aiohttp.ClientRequest\n        \"\"\"\n\n        url = url.update_query(params)\n        headers = self.web.headers\n        if extra_headers:\n            headers |= extra_headers\n\n        request = aiohttp.ClientRequest(\n            aiohttp.hdrs.METH_GET,\n            url,\n            headers=headers,\n            cookies=self.web.cookie_jar.filter_cookies(url),\n            proxy=self.net_core.proxy.url,\n            proxy_auth=self.net_core.proxy.auth,\n            ssl=False,\n        )\n\n        return request\n\n    def pack_web_form_request(\n        self, url: yarl.URL, data: list[tuple[str, str]], *, extra_headers: list[tuple[str, str]] | None = None\n    ) -> aiohttp.ClientRequest:\n        \"\"\"\n        打包网页端表单请求\n\n        Args:\n            url (yarl.URL): 链接\n            data (list[tuple[str, str]]): 参数元组列表\n            extra_headers (list[tuple[str, str]]): 额外的请求头\n\n        Returns:\n            aiohttp.ClientRequest\n        \"\"\"\n\n        headers = self.web.headers\n        if extra_headers:\n            headers |= extra_headers\n\n        payload = aiohttp.payload.BytesPayload(\n            urllib.parse.urlencode(data, doseq=True).encode(\"utf-8\"),\n            content_type=\"application/x-www-form-urlencoded\",\n        )\n\n        request = aiohttp.ClientRequest(\n            aiohttp.hdrs.METH_POST,\n            url,\n            headers=headers,\n            data=payload,\n            cookies=self.web.cookie_jar.filter_cookies(url),\n            proxy=self.net_core.proxy.url,\n            proxy_auth=self.net_core.proxy.auth,\n            ssl=False,\n        )\n\n        return request\n"
  },
  {
    "path": "src/aiotieba/core/net.py",
    "content": "from __future__ import annotations\n\nimport asyncio\nimport dataclasses as dcs\nfrom collections.abc import Callable\n\nimport aiohttp\n\nfrom ..config import ProxyConfig, TimeoutConfig\nfrom ..exception import HTTPStatusError\nfrom ..helper import timeout\n\n\ndef check_status_code(response: aiohttp.ClientResponse) -> None:\n    if response.status != 200:\n        raise HTTPStatusError(response.status, response.reason)\n\n\nTypeHeadersChecker = Callable[[aiohttp.ClientResponse], None]\n\n\n@dcs.dataclass\nclass NetCore:\n    \"\"\"\n    网络请求相关容器\n\n    Args:\n        connector (aiohttp.TCPConnector): 用于生成TCP连接的连接器\n        proxy (ProxyConfig, optional): 代理配置. Defaults to None.\n        timeout (TimeoutConfig, optional): 超时配置. Defaults to None.\n    \"\"\"\n\n    connector: aiohttp.TCPConnector\n    proxy: ProxyConfig\n    timeout: TimeoutConfig\n\n    def __init__(\n        self,\n        connector: aiohttp.TCPConnector,\n        proxy: ProxyConfig | None = None,\n        timeout: TimeoutConfig | None = None,\n    ) -> None:\n        self.connector = connector\n\n        if not isinstance(proxy, ProxyConfig):\n            proxy = ProxyConfig()\n        self.proxy = proxy\n\n        if not isinstance(timeout, TimeoutConfig):\n            timeout = TimeoutConfig()\n        self.timeout = timeout\n\n    async def req2res(\n        self, request: aiohttp.ClientRequest, read_until_eof: bool = True, read_bufsize: int = 64 * 1024\n    ) -> aiohttp.ClientResponse:\n        \"\"\"\n        发送http请求并返回ClientResponse\n\n        Args:\n            request (aiohttp.ClientRequest): 待发送的请求\n\n            read_until_eof (bool, optional): 是否读取到EOF就中止. Defaults to True.\n            read_bufsize (int, optional): 读缓冲区大小 以字节为单位. Defaults to 64KiB.\n\n        Returns:\n            ClientResponse: 响应\n        \"\"\"\n\n        # 获取TCP连接\n        try:\n            async with timeout(self.timeout.http_connect, self.connector._loop):\n                conn = await self.connector.connect(request, [], self.timeout.http_timeout)\n        except asyncio.TimeoutError as exc:\n            raise aiohttp.ServerTimeoutError(f\"Connection timeout to host {request.url}\") from exc\n\n        # 设置响应解析流程\n        conn.protocol.set_response_params(\n            read_until_eof=read_until_eof,\n            auto_decompress=True,\n            read_timeout=self.timeout.http_read,\n            read_bufsize=read_bufsize,\n        )\n\n        # 发送请求\n        try:\n            response = await request.send(conn)\n        except BaseException:\n            conn.close()\n            raise\n        try:\n            await response.start(conn)\n        except BaseException:\n            response.close()\n            raise\n\n        return response\n\n    async def send_request(\n        self,\n        request: aiohttp.ClientRequest,\n        read_bufsize: int = 64 * 1024,\n        headers_checker: TypeHeadersChecker = check_status_code,\n    ) -> bytes:\n        \"\"\"\n        简单发送http请求\n        不包含重定向和身份验证功能\n\n        Args:\n            request (aiohttp.ClientRequest): 待发送的请求\n            read_bufsize (int, optional): 读缓冲区大小 以字节为单位. Defaults to 64KiB.\n            headers_checker (TypeHeadersChecker, optional): headers检查函数. Defaults to check_status_code.\n\n        Returns:\n            bytes: body\n        \"\"\"\n\n        response = await self.req2res(request, True, read_bufsize)\n\n        # 检查headers\n        headers_checker(response)\n\n        # 读取响应\n        response._body = await response.content.read()\n        body = response._body\n\n        # 释放连接\n        response.release()\n\n        return body\n"
  },
  {
    "path": "src/aiotieba/core/websocket.py",
    "content": "from __future__ import annotations\n\nimport asyncio\nimport binascii\nimport dataclasses as dcs\nimport gzip\nimport random\nimport time\nimport weakref\nfrom collections.abc import Awaitable, Callable\nfrom typing import TYPE_CHECKING\n\nimport aiohttp\nimport yarl\nfrom cryptography.hazmat.primitives import padding\nfrom cryptography.hazmat.primitives.ciphers import algorithms\n\nfrom ..enums import WsStatus\nfrom ..exception import HTTPStatusError\nfrom ..helper import timeout\n\nif TYPE_CHECKING:\n    from .account import Account\n    from .net import NetCore\n\nTypeWebsocketCallback = Callable[[\"WsCore\", bytes, int], Awaitable[None]]\n\n\ndef pack_ws_bytes(\n    account: Account, data: bytes, cmd: int, req_id: int, *, compress: bool = False, encrypt: bool = True\n) -> bytes:\n    \"\"\"\n    打包数据并添加9字节头部\n\n    Args:\n        account (Account): 贴吧的用户参数容器\n        data (bytes): 待发送的websocket数据\n        cmd (int): 请求的cmd类型\n        req_id (int): 请求的id\n        compress (bool, optional): 是否需要gzip压缩. Defaults to False.\n        encrypt (bool, optional): 是否需要aes加密. Defaults to True.\n\n    Returns:\n        bytes: 打包后的websocket数据\n    \"\"\"\n\n    flag = 0x08\n\n    if compress:\n        flag |= 0b01000000\n        data = gzip.compress(data, compresslevel=6, mtime=0)\n    if encrypt:\n        flag |= 0b10000000\n        padder = padding.PKCS7(algorithms.AES.block_size).padder()\n        data = padder.update(data) + padder.finalize()\n        encryptor = account.aes_ecb_chiper.encryptor()\n        data = encryptor.update(data) + encryptor.finalize()\n\n    data = b\"\".join([\n        flag.to_bytes(1, \"big\"),\n        cmd.to_bytes(4, \"big\"),\n        req_id.to_bytes(4, \"big\"),\n        data,\n    ])\n\n    return data\n\n\ndef parse_ws_bytes(account: Account, data: bytes) -> tuple[bytes, int, int]:\n    \"\"\"\n    对websocket返回数据进行解包\n\n    Args:\n        account (Account): 贴吧的用户参数容器\n        data (bytes): 接收到的websocket数据\n\n    Returns:\n        bytes: 解包后的websocket数据\n        int: 对应请求的cmd类型\n        int: 对应请求的id\n    \"\"\"\n\n    data_view = memoryview(data)\n    flag = data_view[0]\n    cmd = int.from_bytes(data_view[1:5], \"big\")\n    req_id = int.from_bytes(data_view[5:9], \"big\")\n\n    data = data_view[9:].tobytes()\n    if flag & 0b10000000:\n        decryptor = account.aes_ecb_chiper.decryptor()\n        data = decryptor.update(data) + decryptor.finalize()\n        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()\n        data = unpadder.update(data) + unpadder.finalize()\n    if flag & 0b01000000:\n        data = gzip.decompress(data)\n\n    return data, cmd, req_id\n\n\n@dcs.dataclass\nclass MsgIDPair:\n    \"\"\"\n    长度为2的msg_id队列 记录新旧msg_id\n    \"\"\"\n\n    last_id: int = 0\n    curr_id: int = 0\n\n    def update_msg_id(self, curr_id: int) -> None:\n        \"\"\"\n        更新msg_id\n\n        Args:\n            curr_id (int): 当前消息的msg_id\n        \"\"\"\n\n        self.last_id = self.curr_id\n        self.curr_id = curr_id\n\n\n@dcs.dataclass\nclass MsgIDManager:\n    \"\"\"\n    msg_id管理器\n    \"\"\"\n\n    priv_gid: int = 0\n    gid2mid: dict[int, MsgIDPair] = dcs.field(default_factory=lambda: {0: MsgIDPair()})\n\n    def update_msg_id(self, group_id: int, msg_id: int) -> None:\n        \"\"\"\n        更新group_id对应的msg_id\n\n        Args:\n            group_id (int): 消息组id\n            msg_id (int): 当前消息的msg_id\n        \"\"\"\n\n        mid_pair = self.gid2mid.get(group_id, None)\n        if mid_pair is not None:\n            mid_pair.update_msg_id(msg_id)\n        else:\n            mid_pair = MsgIDPair(msg_id, msg_id)\n\n    def get_msg_id(self, group_id: int) -> int:\n        \"\"\"\n        获取group_id对应的msg_id\n\n        Args:\n            group_id (int): 消息组id\n\n        Returns:\n            int: 上一条消息的msg_id\n        \"\"\"\n\n        return self.gid2mid[group_id].last_id\n\n    def get_record_id(self) -> int:\n        \"\"\"\n        获取record_id\n\n        Returns:\n            int: record_id\n        \"\"\"\n\n        return self.get_msg_id(self.priv_gid) * 100 + 1\n\n\n@dcs.dataclass\nclass WsResponse:\n    \"\"\"\n    websocket响应\n\n    Args:\n        future (asyncio.Future): 用于等待读事件到来的Future\n        req_id (int): 请求id\n        read_timeout (float): 读超时时间\n    \"\"\"\n\n    loop: asyncio.AbstractEventLoop\n    future: asyncio.Future\n    req_id: int\n    read_timeout: float\n\n    def __init__(self, req_id: int, read_timeout: float) -> None:\n        self.loop = asyncio.get_running_loop()\n        self.future = self.loop.create_future()\n        self.req_id = req_id\n        self.read_timeout = read_timeout\n\n    async def read(self) -> bytes:\n        \"\"\"\n        读取websocket响应\n\n        Returns:\n            bytes\n\n        Raises:\n            asyncio.TimeoutError: 读取超时\n        \"\"\"\n\n        try:\n            async with timeout(self.read_timeout, self.loop):\n                return await self.future\n        except asyncio.TimeoutError as err:\n            self.future.cancel()\n            raise asyncio.TimeoutError(\"Timeout to read\") from err\n        except BaseException:\n            self.future.cancel()\n            raise\n\n\n@dcs.dataclass\nclass WsWaiter:\n    \"\"\"\n    websocket等待映射\n    \"\"\"\n\n    loop: asyncio.AbstractEventLoop\n    waiter: weakref.WeakValueDictionary\n    req_id: int\n    read_timeout: float\n\n    def __init__(self, read_timeout: float) -> None:\n        self.loop = asyncio.get_running_loop()\n        self.waiter = weakref.WeakValueDictionary()\n        self.req_id = int(time.time())\n        self.read_timeout = read_timeout\n        weakref.finalize(self, self.__cancel_all)\n\n    def __cancel_all(self) -> None:\n        for ws_resp in self.waiter.values():\n            ws_resp.future.cancel()\n\n    def new(self) -> WsResponse:\n        \"\"\"\n        创建一个可用于等待数据的响应对象\n\n        Args:\n            req_id (int): 请求id\n\n        Returns:\n            WsResponse: websocket响应\n        \"\"\"\n\n        self.req_id += 1\n        ws_resp = WsResponse(self.req_id, self.read_timeout)\n        self.waiter[self.req_id] = ws_resp\n        return ws_resp\n\n    def set_done(self, req_id: int, data: bytes) -> None:\n        \"\"\"\n        将req_id对应的响应Future设置为已完成\n\n        Args:\n            req_id (int): 请求id\n            data (bytes): 填入的数据\n        \"\"\"\n\n        ws_resp: WsResponse = self.waiter.get(req_id, None)\n        if ws_resp is None:\n            return\n        ws_resp.future.set_result(data)\n\n\n@dcs.dataclass\nclass WsCore:\n    \"\"\"\n    保存websocket接口相关状态的核心容器\n    \"\"\"\n\n    account: Account\n    net_core: NetCore\n    waiter: WsWaiter\n    callbacks: dict[int, TypeWebsocketCallback]\n    websocket: aiohttp.ClientWebSocketResponse\n    ws_dispatcher: asyncio.Task\n    mid_manager: MsgIDManager\n    _status: WsStatus\n    loop: asyncio.AbstractEventLoop\n\n    def __init__(self, account: Account, net_core: NetCore) -> None:\n        self.set_account(account)\n        self.net_core = net_core\n\n        self.callbacks: dict[int, TypeWebsocketCallback] = {}\n        self.websocket: aiohttp.ClientWebSocketResponse = None\n        self.ws_dispatcher: asyncio.Task = None\n\n        self._status = WsStatus.CLOSED\n\n        self.loop = asyncio.get_running_loop()\n\n    def set_account(self, new_account: Account) -> None:\n        self.account = new_account\n\n    async def connect(self) -> None:\n        \"\"\"\n        建立weboscket连接\n\n        Raises:\n            aiohttp.WSServerHandshakeError: websocket握手失败\n        \"\"\"\n\n        self._status = WsStatus.CONNECTING\n\n        self.waiter = WsWaiter(self.net_core.timeout.ws_read)\n        self.mid_manager = MsgIDManager()\n\n        from aiohttp import hdrs\n\n        ws_url = yarl.URL.build(scheme=\"ws\", host=\"im.tieba.baidu.com\", port=8000)\n        sec_key_bytes = binascii.b2a_base64(random.randbytes(16), newline=False)\n        headers = {\n            hdrs.UPGRADE: \"websocket\",\n            hdrs.CONNECTION: \"upgrade\",\n            hdrs.SEC_WEBSOCKET_EXTENSIONS: \"im_version=2.3\",\n            hdrs.SEC_WEBSOCKET_VERSION: \"13\",\n            hdrs.SEC_WEBSOCKET_KEY: sec_key_bytes.decode(\"ascii\"),\n            hdrs.ACCEPT_ENCODING: \"gzip\",\n            hdrs.HOST: \"im.tieba.baidu.com:8000\",\n        }\n        request = aiohttp.ClientRequest(\n            hdrs.METH_GET,\n            ws_url,\n            headers=headers,\n            proxy=self.net_core.proxy.url,\n            proxy_auth=self.net_core.proxy.auth,\n            ssl=False,\n        )\n\n        response = await self.net_core.req2res(request, False, 2 * 1024)\n\n        if response.status != 101:\n            raise HTTPStatusError(response.status, response.reason)\n\n        try:\n            conn = response.connection\n            conn_proto = conn.protocol\n            transport = conn.transport\n            reader = aiohttp.client.WebSocketDataQueue(conn_proto, 1 << 16, loop=self.loop)\n            conn_proto.set_parser(aiohttp.client.WebSocketReader(reader, 4 * 1024 * 1024), reader)\n            writer = aiohttp.client.WebSocketWriter(conn_proto, transport, use_mask=True)\n        except BaseException:\n            response.close()\n            raise\n        else:\n            self.websocket = aiohttp.ClientWebSocketResponse(\n                reader,\n                writer,\n                \"chat\",\n                response,\n                self.net_core.timeout.ws_timeout,\n                True,\n                True,\n                self.loop,\n                heartbeat=self.net_core.timeout.ws_heartbeat,\n            )\n\n        if self.ws_dispatcher is not None and not self.ws_dispatcher.done():\n            self.ws_dispatcher.cancel()\n        self.ws_dispatcher = self.loop.create_task(self.__ws_dispatch(), name=\"ws_dispatcher\")\n\n    async def close(self) -> None:\n        if self.status == WsStatus.OPEN:\n            await self.websocket.close()\n            self.ws_dispatcher.cancel()\n        self._status = WsStatus.CLOSED\n\n    def __default_callback(self, req_id: int, data: bytes) -> None:\n        self.waiter.set_done(req_id, data)\n\n    async def __ws_dispatch(self) -> None:\n        try:\n            async for msg in self.websocket:\n                data, cmd, req_id = parse_ws_bytes(self.account, msg.data)\n                res_callback = self.callbacks.get(cmd, None)\n                if res_callback is None:\n                    self.__default_callback(req_id, data)\n                else:\n                    self.loop.create_task(res_callback(self, data, req_id))\n\n        except asyncio.CancelledError:\n            self._status = WsStatus.CLOSED\n        except Exception:\n            self._status = WsStatus.CLOSED\n\n    @property\n    def status(self) -> WsStatus:\n        \"\"\"\n        websocket状态\n        \"\"\"\n\n        if self._status != WsStatus.CLOSED and self.websocket._writer.transport.is_closing():\n            self._status = WsStatus.CLOSED\n        return self._status\n\n    async def send(self, data: bytes, cmd: int, *, compress: bool = False, encrypt: bool = True) -> WsResponse:\n        \"\"\"\n        将protobuf序列化结果打包发送\n\n        Args:\n            data (bytes): 待发送的数据\n            cmd (int): 请求的cmd类型\n            compress (bool, optional): 是否需要gzip压缩. Defaults to False.\n            encrypt (bool, optional): 是否需要aes加密. Defaults to True.\n\n        Returns:\n            WsResponse: websocket响应对象\n\n        Raises:\n            asyncio.TimeoutError: 发送超时\n        \"\"\"\n\n        response = self.waiter.new()\n        req_data = pack_ws_bytes(self.account, data, cmd, response.req_id, compress=compress, encrypt=encrypt)\n\n        try:\n            async with timeout(self.net_core.timeout.ws_send, self.loop):\n                await self.websocket.send_bytes(req_data)\n        except asyncio.TimeoutError as err:\n            response.future.cancel()\n            raise asyncio.TimeoutError(\"Timeout to send\") from err\n        except BaseException:\n            response.future.cancel()\n        else:\n            return response\n"
  },
  {
    "path": "src/aiotieba/enums.py",
    "content": "from __future__ import annotations\n\nimport enum\nimport sys\n\nif sys.version_info >= (3, 11):\n    from enum import StrEnum\nelse:\n    from strenum import StrEnum\n\n\nclass Gender(enum.IntEnum):\n    \"\"\"\n    用户性别\n\n    Note:\n        UNKNOWN 未知\\n\n        MALE 男性\\n\n        FEMALE 女性\n    \"\"\"\n\n    UNKNOWN = 0\n    MALE = 1\n    FEMALE = 2\n\n\nclass PrivLike(enum.IntEnum):\n    \"\"\"\n    关注吧列表的公开状态\n\n    Note:\n        UNKNOWN 未知\\n\n        PUBLIC 所有人可见\\n\n        FRIEND 好友可见\\n\n        HIDE 完全隐藏\n    \"\"\"\n\n    UNKNOWN = 0\n    PUBLIC = 1\n    FRIEND = 2\n    HIDE = 3\n\n    @classmethod\n    def __missing__(cls, _: int) -> PrivLike:\n        return PrivLike.UNKNOWN\n\n\nclass PrivReply(enum.IntEnum):\n    \"\"\"\n    帖子评论权限\n\n    Note:\n        UNKNOWN 未知\\n\n        ALL 允许所有人\\n\n        FANS 仅允许我的粉丝\\n\n        FOLLOW 仅允许我的关注\n    \"\"\"\n\n    UNKNOWN = 0\n    ALL = 1\n    FANS = 5\n    FOLLOW = 6\n\n    @classmethod\n    def __missing__(cls, _: int) -> PrivReply:\n        return PrivReply.UNKNOWN\n\n\nclass ThreadType(enum.IntEnum):\n    \"\"\"\n    主题帖类型\n\n    Note:\n        UNKNOWN 未知\\n\n        ARTICLE 图文帖\\n\n        ALBUM 相册帖\\n\n        VOICE 语音帖\\n\n        STORY 会员小说帖\\n\n        VIDEO 视频帖\\n\n        LIVE 直播帖\\n\n        HELP 求助帖\\n\n        VOTE 打分帖\\n\n        LOTTERY 抽奖帖\n    \"\"\"\n\n    UNKNOWN = -1\n    ARTICLE = 0\n    ALBUM = 1\n    VOICE = 11\n    STORY = 31\n    VIDEO = 40\n    LIVE = 50\n    HELP = 71\n    VOTE = 75\n    LOTTERY = 76\n\n    @classmethod\n    def __missing__(cls, _: int) -> ThreadType:\n        return ThreadType.UNKNOWN\n\n\nclass ReqUInfo(enum.Flag):\n    \"\"\"\n    使用该枚举类指定待获取的用户信息字段\n\n    Note:\n        各bit位的含义由高到低分别为 OTHER, TIEBA_UID, NICK_NAME, USER_NAME, PORTRAIT, USER_ID\\n\n        其中BASIC = USER_ID | PORTRAIT | USER_NAME\n    \"\"\"\n\n    USER_ID = enum.auto()\n    PORTRAIT = enum.auto()\n    USER_NAME = enum.auto()\n    NICK_NAME = enum.auto()\n    TIEBA_UID = enum.auto()\n    OTHER = enum.auto()\n    BASIC = USER_ID | PORTRAIT | USER_NAME\n    ALL = BASIC | NICK_NAME | TIEBA_UID | OTHER\n\n\nclass ThreadSortType(enum.IntEnum):\n    \"\"\"\n    主题帖排序\n\n    Note:\n        对于有热门分区的贴吧 0热门排序(HOT) 1按发布时间(CREATE) 2关注的人(FOLLOW) 34热门排序(HOT) >=6是按回复时间(REPLY)\\n\n        对于无热门分区的贴吧 0按回复时间(REPLY) 1按发布时间(CREATE) 2关注的人(FOLLOW) >=3按回复时间(REPLY)\n    \"\"\"\n\n    REPLY = 6\n    CREATE = 1\n    HOT = 3\n    FOLLOW = 2\n\n\nclass PostSortType(enum.IntEnum):\n    \"\"\"\n    回复排序\n\n    Note:\n        ASC 时间顺序\\n\n        DESC 时间倒序\\n\n        HOT 热门序\n    \"\"\"\n\n    ASC = 0\n    DESC = 1\n    HOT = 2\n\n\nclass BawuSearchType(enum.IntEnum):\n    \"\"\"\n    吧务后台搜索类型\n\n    Note:\n        USER 搜索用户\\n\n        OP 搜索操作者\n    \"\"\"\n\n    USER = 0\n    OP = 1\n\n\nclass SearchType(enum.IntEnum):\n    \"\"\"\n    搜索类型\n\n    Note:\n        ALL 搜索全部\\n\n        TIME app时间倒序\\n\n        RELATION app相关性排序\n    \"\"\"\n\n    ALL = 0\n    TIME = 1\n    RELATION = 2\n\n\nclass BawuType(StrEnum):\n    \"\"\"\n    吧务类型\n\n    Note:\n        MANAGER 小吧\\n\n        IMAGE_EDITOR 图片小编\\n\n        VOICE_EDITOR 语音小编\n    \"\"\"\n\n    MANAGER = \"assist\"\n    IMAGE_EDITOR = \"picadmin\"\n    VOICE_EDITOR = \"voiceadmin\"\n\n\nclass BawuPermType(enum.Flag):\n    \"\"\"\n    吧务已分配的权限\n\n    Note:\n        NULL 无权限\\n\n        UNBLOCK 解除封禁\\n\n        UNBLOCK_APPEAL 封禁申诉处理\\n\n        RECOVER 恢复删帖\\n\n        RECOVER_APPEAL 删帖申诉处理\\n\n        ALL 所有权限\n    \"\"\"\n\n    NULL = 0\n    UNBLOCK = enum.auto()\n    UNBLOCK_APPEAL = enum.auto()\n    RECOVER = enum.auto()\n    RECOVER_APPEAL = enum.auto()\n    ALL = UNBLOCK | UNBLOCK_APPEAL | RECOVER | RECOVER_APPEAL\n\n\nclass RankForumType(enum.IntEnum):\n    \"\"\"\n    吧签到排行榜类别\n\n    Note:\n        TODAY 今日排行\\n\n        YESTERDAY 昨日排行\\n\n        WEEKLY 周排行\\n\n        MONTHLY 月排行\n    \"\"\"\n\n    TODAY = 0\n    YESTERDAY = 1\n    WEEKLY = 2\n    MONTHLY = 3\n\n\nclass BlacklistType(enum.Flag):\n    \"\"\"\n    用户黑名单类型\n\n    Note:\n        NULL 正常状态\\n\n        FOLLOW 禁止关注\\n\n        INTERACT 禁止互动\\n\n        CHAT 禁止私信\\n\n        ALL 全屏蔽\n    \"\"\"\n\n    NULL = 0\n    FOLLOW = enum.auto()\n    INTERACT = enum.auto()\n    CHAT = enum.auto()\n    ALL = FOLLOW | INTERACT | CHAT\n\n\nclass WsStatus(enum.IntEnum):\n    \"\"\"\n    回复排序\n\n    Note:\n        CLOSED 已关闭\\n\n        CONNECTING 正在连接\\n\n        OPEN 可用\n    \"\"\"\n\n    CLOSED = 0\n    CONNECTING = enum.auto()\n    OPEN = enum.auto()\n\n\nclass GroupType(enum.IntEnum):\n    \"\"\"\n    消息组类型\n    \"\"\"\n\n    PRIVATE_MSG = 6\n    MISC = 8\n\n\nclass MsgType(enum.IntEnum):\n    \"\"\"\n    消息类型\n    \"\"\"\n\n    PRIVATE_MSG = 1\n    MISC = 10\n    READED = 22\n"
  },
  {
    "path": "src/aiotieba/exception.py",
    "content": "from __future__ import annotations\n\nimport dataclasses as dcs\n\n\n@dcs.dataclass\nclass TbErrorExt:\n    \"\"\"\n    为类型添加一个`err`项 用于保存捕获到的异常\n    \"\"\"\n\n    err: Exception | None = dcs.field(default=None, init=False, repr=False)\n\n\n@dcs.dataclass\nclass BoolResponse(TbErrorExt):\n    \"\"\"\n    bool返回值\n    不是内置bool的子类 可能不支持部分bool操作\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n    \"\"\"\n\n    def __bool__(self) -> bool:\n        return self.err is None\n\n    def __int__(self) -> int:\n        return int(bool(self))\n\n    def __repr__(self) -> str:\n        return str(bool(self))\n\n    def __hash__(self) -> int:\n        return hash(bool(self))\n\n\n@dcs.dataclass\nclass IntResponse(TbErrorExt, int):\n    \"\"\"\n    int返回值\n    是内置int的子类\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n    \"\"\"\n\n    def __new__(cls, i: int = 0) -> IntResponse:\n        obj = super().__new__(cls, i)\n        return obj\n\n    def __init__(self, i: int = 0) -> None:\n        pass\n\n    def __repr__(self) -> str:\n        return str(int(self))\n\n    def __hash__(self) -> int:\n        return hash(int(self))\n\n\n@dcs.dataclass\nclass StrResponse(TbErrorExt, str):\n    \"\"\"\n    str返回值\n    是内置str的子类\n\n    Attributes:\n        err (Exception | None): 捕获的异常\n    \"\"\"\n\n    __slots__ = []\n\n    def __new__(cls, s: str = \"\") -> StrResponse:\n        obj = super().__new__(cls, s)\n        return obj\n\n    def __init__(self, s: str = \"\") -> None:\n        pass\n\n    def __hash__(self) -> int:\n        return hash(str(self))\n\n\nclass TiebaServerError(RuntimeError):\n    \"\"\"\n    贴吧服务器异常\n    \"\"\"\n\n    __slots__ = [\"code\", \"msg\"]\n\n    def __init__(self, code: int, msg: str) -> None:\n        super().__init__(code, msg)\n        self.code = code\n        self.msg = msg\n\n\nclass HTTPStatusError(RuntimeError):\n    \"\"\"\n    错误的状态码\n    \"\"\"\n\n    __slots__ = [\"code\", \"msg\"]\n\n    def __init__(self, code: int, msg: str) -> None:\n        super().__init__(code, msg)\n        self.code = code\n        self.msg = msg\n\n\nclass TiebaValueError(RuntimeError):\n    \"\"\"\n    意外的字段值\n    \"\"\"\n\n\nclass ContentTypeError(RuntimeError):\n    \"\"\"\n    无法解析响应头中的content-type\n    \"\"\"\n"
  },
  {
    "path": "src/aiotieba/helper/__init__.py",
    "content": "from ..helper import cache, crypto, utils\nfrom .utils import (\n    default_datetime,\n    deprecated,\n    handle_exception,\n    is_portrait,\n    is_user_name,\n    jsonlib,\n    pack_json,\n    parse_json,\n    timeout,\n)\n"
  },
  {
    "path": "src/aiotieba/helper/cache.py",
    "content": "from __future__ import annotations\n\nfrom collections import OrderedDict\nfrom typing import ClassVar\n\n\nclass ForumInfoCache:\n    \"\"\"\n    吧信息缓存\n    \"\"\"\n\n    _fname2fid: ClassVar[OrderedDict] = OrderedDict()\n    _fid2fname: ClassVar[OrderedDict] = OrderedDict()\n\n    @classmethod\n    def get_fid(cls, fname: str) -> int:\n        \"\"\"\n        通过贴吧名获取forum_id\n\n        Args:\n            fname (str): 贴吧名\n\n        Returns:\n            int: 该贴吧的forum_id\n        \"\"\"\n\n        return cls._fname2fid.get(fname, \"\")\n\n    @classmethod\n    def get_fname(cls, fid: int) -> str:\n        \"\"\"\n        通过forum_id获取贴吧名\n\n        Args:\n            fid (int): forum_id\n\n        Returns:\n            str: 该贴吧的贴吧名\n        \"\"\"\n\n        return cls._fid2fname.get(fid, \"\")\n\n    @classmethod\n    def add_forum(cls, fname: str, fid: int) -> None:\n        \"\"\"\n        将贴吧名与forum_id的映射关系添加到缓存\n\n        Args:\n            fname (str): 贴吧名\n            fid (int): 贴吧id\n        \"\"\"\n\n        if len(cls._fname2fid) == 128:\n            cls._fname2fid.popitem(last=False)\n            cls._fid2fname.popitem(last=False)\n\n        cls._fname2fid[fname] = fid\n        cls._fid2fname[fid] = fname\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/CMakeLists.txt",
    "content": "set(TBC_INCLUDE_DIRS ./include)\nfile(GLOB_RECURSE TBC_SOURCES *.c)\n\nPython3_add_library(crypto MODULE ${TBC_SOURCES})\ntarget_include_directories(crypto PUBLIC ${TBC_INCLUDE_DIRS})\n\ninstall(TARGETS crypto DESTINATION ${SKBUILD_PROJECT_NAME}/helper/crypto)\ninstall(TARGETS crypto DESTINATION ${PROJECT_SOURCE_DIR}/src/${SKBUILD_PROJECT_NAME}/helper/crypto)\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/__init__.py",
    "content": "from __future__ import annotations\n\nfrom .crypto import c3_aid, cuid_galaxy2, enuid, rc4_42\nfrom .crypto import sign as _sign\n\n\ndef sign(data: list[tuple[str, str | int]]) -> list[tuple[str, str | int]]:\n    \"\"\"\n    为参数元组列表添加贴吧客户端签名\n\n    Args:\n        data (list[tuple[str, str | int]]): 参数元组列表\n\n    Returns:\n        list[tuple[str, str | int]]: 签名后的form参数元组列表\n    \"\"\"\n\n    data.append((\"sign\", _sign(data)))\n    return data\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/crypto.pyi",
    "content": "def cuid_galaxy2(android_id: str) -> str:\n    \"\"\"\n    使用给定的android_id生成cuid_galaxy2\n\n    Args:\n        android_id (str): 长度为16的16进制字符串 包含8字节信息\n\n    Returns:\n        str: cuid_galaxy2 长度为42的字符串\n\n    Examples:\n        A3ED2D7B9CFC28E8934A3FBD3A9579C7|VZ5FKB5XS\n\n    Note:\n        此实现与12.x版本及以前的官方实现一致\n    \"\"\"\n\ndef c3_aid(android_id: str, uuid: str) -> str:\n    \"\"\"\n    使用给定的android_id和uuid生成c3_aid\n\n    Args:\n        android_id (str): 长度为16的16进制字符串 包含8字节信息\n        uuid (str): 包含16字节信息\n\n    Returns:\n        str: c3_aid 长度为45的字符串\n\n    Examples:\n        A00-ZNU3O3EP74D727LMQY745CZSGZQJQZGP-3JXCKC7X\n\n    Note:\n        此实现与12.x版本及以前的官方实现一致\n    \"\"\"\n\ndef rc4_42(xyus_md5_str: str, aes_cbc_sec_key: bytes) -> bytes:\n    \"\"\"\n    RC4加密的变体 一次额外的42异或\n\n    Args:\n        xyus_md5_str (str): 32字节长小写字符串 作为RC4密钥\n        aes_cbc_sec_key (bytes): 贴吧AES-CBC加密使用的随机密码\n\n    Returns:\n        bytes\n    \"\"\"\n\ndef sign(data: list[tuple[str, str | int]]) -> str:\n    \"\"\"\n    为参数元组列表计算贴吧客户端签名\n\n    Args:\n        data (list[tuple[str, str | int]]): 参数元组列表\n\n    Returns:\n        str: 签名\n    \"\"\"\n\ndef enuid(cuid_galaxy2: str) -> str:\n    \"\"\"\n    生成EnUid\n\n    Args:\n        cuid_galaxy2 (str)\n\n    Returns:\n        str: 变种base64编码后的enuid\n    \"\"\"\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/base32/base32.h",
    "content": "// Base32 implementation\n//\n// Copyright 2010 Google Inc.\n// Author: Markus Gutschke\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n// Encode and decode from base32 encoding using the following alphabet:\n//   ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\n// This alphabet is documented in RFC 4648/3548\n//\n// We allow white-space and hyphens, but all other characters are considered\n// invalid.\n//\n// All functions return the number of output bytes or -1 on error. If the\n// output buffer is too small, the result will silently be truncated.\n\n#pragma once\n\n#define BASE32_LEN(len) (((len) / 5) * 8 + ((len) % 5 ? 8 : 0))\n\nvoid tbc_base32_encode(const unsigned char* src, int srcLen, unsigned char* dst);\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/crc/crc32.h",
    "content": "/*\n** The crc32 is licensed under the Apache License, Version 2.0, and a copy of the license is included in this file.\n**\n** Author: Wang Yaofu voipman@qq.com\n** Description: The source file of class crc32.\n**  CRC32 implementation according to IEEE standards.\n**  Polynomials are represented in LSB-first form\n**  following parameters:\n**    Width                      : 32 bit\n**    Poly                       : 0xEDB88320\n**    Output for \"123456789\"     : 0xCBF43926\n*/\n\n#pragma once\n\n#include <stddef.h> // size_t\n#include <stdint.h> // uint32_t\n\nuint32_t tbc_crc32(const unsigned char* src, size_t srcLen, uint32_t prev_val);\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/mbedtls/alignment.h",
    "content": "/**\n * \\file alignment.h\n *\n * \\brief Utility code for dealing with unaligned memory accesses\n */\n/*\n *  Copyright The Mbed TLS Contributors\n *  SPDX-License-Identifier: Apache-2.0\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n *  not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef MBEDTLS_LIBRARY_ALIGNMENT_H\n#define MBEDTLS_LIBRARY_ALIGNMENT_H\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\n/*\n * Define MBEDTLS_EFFICIENT_UNALIGNED_ACCESS for architectures where unaligned memory\n * accesses are known to be efficient.\n *\n * All functions defined here will behave correctly regardless, but might be less\n * efficient when this is not defined.\n */\n#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386__) || defined(__amd64__) || defined(__x86_64__)\n/*\n * __ARM_FEATURE_UNALIGNED is defined where appropriate by armcc, gcc 7, clang 9\n * (and later versions) for Arm v7 and later; all x86 platforms should have\n * efficient unaligned access.\n */\n#define MBEDTLS_EFFICIENT_UNALIGNED_ACCESS\n#endif\n\n/**\n * Read the unsigned 16 bits integer from the given address, which need not\n * be aligned.\n *\n * \\param   p pointer to 2 bytes of data\n * \\return  Data at the given address\n */\ninline uint16_t mbedtls_get_unaligned_uint16(const void* p)\n{\n    uint16_t r;\n    memcpy(&r, p, sizeof(r));\n    return r;\n}\n\n/**\n * Write the unsigned 16 bits integer to the given address, which need not\n * be aligned.\n *\n * \\param   p pointer to 2 bytes of data\n * \\param   x data to write\n */\ninline void mbedtls_put_unaligned_uint16(void* p, uint16_t x) { memcpy(p, &x, sizeof(x)); }\n\n/**\n * Read the unsigned 32 bits integer from the given address, which need not\n * be aligned.\n *\n * \\param   p pointer to 4 bytes of data\n * \\return  Data at the given address\n */\ninline uint32_t mbedtls_get_unaligned_uint32(const void* p)\n{\n    uint32_t r;\n    memcpy(&r, p, sizeof(r));\n    return r;\n}\n\n/**\n * Write the unsigned 32 bits integer to the given address, which need not\n * be aligned.\n *\n * \\param   p pointer to 4 bytes of data\n * \\param   x data to write\n */\ninline void mbedtls_put_unaligned_uint32(void* p, uint32_t x) { memcpy(p, &x, sizeof(x)); }\n\n/**\n * Read the unsigned 64 bits integer from the given address, which need not\n * be aligned.\n *\n * \\param   p pointer to 8 bytes of data\n * \\return  Data at the given address\n */\ninline uint64_t mbedtls_get_unaligned_uint64(const void* p)\n{\n    uint64_t r;\n    memcpy(&r, p, sizeof(r));\n    return r;\n}\n\n/**\n * Write the unsigned 64 bits integer to the given address, which need not\n * be aligned.\n *\n * \\param   p pointer to 8 bytes of data\n * \\param   x data to write\n */\ninline void mbedtls_put_unaligned_uint64(void* p, uint64_t x) { memcpy(p, &x, sizeof(x)); }\n\n/** Byte Reading Macros\n *\n * Given a multi-byte integer \\p x, MBEDTLS_BYTE_n retrieves the n-th\n * byte from x, where byte 0 is the least significant byte.\n */\n#define MBEDTLS_BYTE_0(x) ((uint8_t)((x)&0xff))\n#define MBEDTLS_BYTE_1(x) ((uint8_t)(((x) >> 8) & 0xff))\n#define MBEDTLS_BYTE_2(x) ((uint8_t)(((x) >> 16) & 0xff))\n#define MBEDTLS_BYTE_3(x) ((uint8_t)(((x) >> 24) & 0xff))\n#define MBEDTLS_BYTE_4(x) ((uint8_t)(((x) >> 32) & 0xff))\n#define MBEDTLS_BYTE_5(x) ((uint8_t)(((x) >> 40) & 0xff))\n#define MBEDTLS_BYTE_6(x) ((uint8_t)(((x) >> 48) & 0xff))\n#define MBEDTLS_BYTE_7(x) ((uint8_t)(((x) >> 56) & 0xff))\n\n/*\n * Detect GCC built-in byteswap routines\n */\n#if defined(__GNUC__) && defined(__GNUC_PREREQ)\n#if __GNUC_PREREQ(4, 8)\n#define MBEDTLS_BSWAP16 __builtin_bswap16\n#endif /* __GNUC_PREREQ(4,8) */\n#if __GNUC_PREREQ(4, 3)\n#define MBEDTLS_BSWAP32 __builtin_bswap32\n#define MBEDTLS_BSWAP64 __builtin_bswap64\n#endif /* __GNUC_PREREQ(4,3) */\n#endif /* defined(__GNUC__) && defined(__GNUC_PREREQ) */\n\n/*\n * Detect Clang built-in byteswap routines\n */\n#if defined(__clang__) && defined(__has_builtin)\n#if __has_builtin(__builtin_bswap16) && !defined(MBEDTLS_BSWAP16)\n#define MBEDTLS_BSWAP16 __builtin_bswap16\n#endif /* __has_builtin(__builtin_bswap16) */\n#if __has_builtin(__builtin_bswap32) && !defined(MBEDTLS_BSWAP32)\n#define MBEDTLS_BSWAP32 __builtin_bswap32\n#endif /* __has_builtin(__builtin_bswap32) */\n#if __has_builtin(__builtin_bswap64) && !defined(MBEDTLS_BSWAP64)\n#define MBEDTLS_BSWAP64 __builtin_bswap64\n#endif /* __has_builtin(__builtin_bswap64) */\n#endif /* defined(__clang__) && defined(__has_builtin) */\n\n/*\n * Detect MSVC built-in byteswap routines\n */\n#if defined(_MSC_VER)\n#if !defined(MBEDTLS_BSWAP16)\n#define MBEDTLS_BSWAP16 _byteswap_ushort\n#endif\n#if !defined(MBEDTLS_BSWAP32)\n#define MBEDTLS_BSWAP32 _byteswap_ulong\n#endif\n#if !defined(MBEDTLS_BSWAP64)\n#define MBEDTLS_BSWAP64 _byteswap_uint64\n#endif\n#endif /* defined(_MSC_VER) */\n\n/* Detect armcc built-in byteswap routine */\n#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 410000) && !defined(MBEDTLS_BSWAP32)\n#define MBEDTLS_BSWAP32 __rev\n#endif\n\n/*\n * Where compiler built-ins are not present, fall back to C code that the\n * compiler may be able to detect and transform into the relevant bswap or\n * similar instruction.\n */\n#if !defined(MBEDTLS_BSWAP16)\nstatic inline uint16_t mbedtls_bswap16(uint16_t x) { return (x & 0x00ff) << 8 | (x & 0xff00) >> 8; }\n#define MBEDTLS_BSWAP16 mbedtls_bswap16\n#endif /* !defined(MBEDTLS_BSWAP16) */\n\n#if !defined(MBEDTLS_BSWAP32)\nstatic inline uint32_t mbedtls_bswap32(uint32_t x)\n{\n    return (x & 0x000000ff) << 24 | (x & 0x0000ff00) << 8 | (x & 0x00ff0000) >> 8 | (x & 0xff000000) >> 24;\n}\n#define MBEDTLS_BSWAP32 mbedtls_bswap32\n#endif /* !defined(MBEDTLS_BSWAP32) */\n\n#if !defined(MBEDTLS_BSWAP64)\nstatic inline uint64_t mbedtls_bswap64(uint64_t x)\n{\n    return (x & 0x00000000000000ffULL) << 56 | (x & 0x000000000000ff00ULL) << 40 | (x & 0x0000000000ff0000ULL) << 24 |\n           (x & 0x00000000ff000000ULL) << 8 | (x & 0x000000ff00000000ULL) >> 8 | (x & 0x0000ff0000000000ULL) >> 24 |\n           (x & 0x00ff000000000000ULL) >> 40 | (x & 0xff00000000000000ULL) >> 56;\n}\n#define MBEDTLS_BSWAP64 mbedtls_bswap64\n#endif /* !defined(MBEDTLS_BSWAP64) */\n\n#if !defined(__BYTE_ORDER__)\nstatic const uint16_t mbedtls_byte_order_detector = {0x100};\n#define MBEDTLS_IS_BIG_ENDIAN (*((unsigned char*)(&mbedtls_byte_order_detector)) == 0x01)\n#else\n#define MBEDTLS_IS_BIG_ENDIAN ((__BYTE_ORDER__) == (__ORDER_BIG_ENDIAN__))\n#endif /* !defined(__BYTE_ORDER__) */\n\n/**\n * Get the unsigned 32 bits integer corresponding to four bytes in\n * big-endian order (MSB first).\n *\n * \\param   data    Base address of the memory to get the four bytes from.\n * \\param   offset  Offset from \\p data of the first and most significant\n *                  byte of the four bytes to build the 32 bits unsigned\n *                  integer from.\n */\n#define MBEDTLS_GET_UINT32_BE(data, offset)                                                                            \\\n    ((MBEDTLS_IS_BIG_ENDIAN) ? mbedtls_get_unaligned_uint32((data) + (offset))                                         \\\n                             : MBEDTLS_BSWAP32(mbedtls_get_unaligned_uint32((data) + (offset))))\n\n/**\n * Put in memory a 32 bits unsigned integer in big-endian order.\n *\n * \\param   n       32 bits unsigned integer to put in memory.\n * \\param   data    Base address of the memory where to put the 32\n *                  bits unsigned integer in.\n * \\param   offset  Offset from \\p data where to put the most significant\n *                  byte of the 32 bits unsigned integer \\p n.\n */\n#define MBEDTLS_PUT_UINT32_BE(n, data, offset)                                                                         \\\n    {                                                                                                                  \\\n        if (MBEDTLS_IS_BIG_ENDIAN) {                                                                                   \\\n            mbedtls_put_unaligned_uint32((data) + (offset), (uint32_t)(n));                                            \\\n        } else {                                                                                                       \\\n            mbedtls_put_unaligned_uint32((data) + (offset), MBEDTLS_BSWAP32((uint32_t)(n)));                           \\\n        }                                                                                                              \\\n    }\n\n/**\n * Get the unsigned 32 bits integer corresponding to four bytes in\n * little-endian order (LSB first).\n *\n * \\param   data    Base address of the memory to get the four bytes from.\n * \\param   offset  Offset from \\p data of the first and least significant\n *                  byte of the four bytes to build the 32 bits unsigned\n *                  integer from.\n */\n#define MBEDTLS_GET_UINT32_LE(data, offset)                                                                            \\\n    ((MBEDTLS_IS_BIG_ENDIAN) ? MBEDTLS_BSWAP32(mbedtls_get_unaligned_uint32((data) + (offset)))                        \\\n                             : mbedtls_get_unaligned_uint32((data) + (offset)))\n\n/**\n * Put in memory a 32 bits unsigned integer in little-endian order.\n *\n * \\param   n       32 bits unsigned integer to put in memory.\n * \\param   data    Base address of the memory where to put the 32\n *                  bits unsigned integer in.\n * \\param   offset  Offset from \\p data where to put the least significant\n *                  byte of the 32 bits unsigned integer \\p n.\n */\n#define MBEDTLS_PUT_UINT32_LE(n, data, offset)                                                                         \\\n    {                                                                                                                  \\\n        if (MBEDTLS_IS_BIG_ENDIAN) {                                                                                   \\\n            mbedtls_put_unaligned_uint32((data) + (offset), MBEDTLS_BSWAP32((uint32_t)(n)));                           \\\n        } else {                                                                                                       \\\n            mbedtls_put_unaligned_uint32((data) + (offset), ((uint32_t)(n)));                                          \\\n        }                                                                                                              \\\n    }\n\n/**\n * Get the unsigned 16 bits integer corresponding to two bytes in\n * little-endian order (LSB first).\n *\n * \\param   data    Base address of the memory to get the two bytes from.\n * \\param   offset  Offset from \\p data of the first and least significant\n *                  byte of the two bytes to build the 16 bits unsigned\n *                  integer from.\n */\n#define MBEDTLS_GET_UINT16_LE(data, offset)                                                                            \\\n    ((MBEDTLS_IS_BIG_ENDIAN) ? MBEDTLS_BSWAP16(mbedtls_get_unaligned_uint16((data) + (offset)))                        \\\n                             : mbedtls_get_unaligned_uint16((data) + (offset)))\n\n/**\n * Put in memory a 16 bits unsigned integer in little-endian order.\n *\n * \\param   n       16 bits unsigned integer to put in memory.\n * \\param   data    Base address of the memory where to put the 16\n *                  bits unsigned integer in.\n * \\param   offset  Offset from \\p data where to put the least significant\n *                  byte of the 16 bits unsigned integer \\p n.\n */\n#define MBEDTLS_PUT_UINT16_LE(n, data, offset)                                                                         \\\n    {                                                                                                                  \\\n        if (MBEDTLS_IS_BIG_ENDIAN) {                                                                                   \\\n            mbedtls_put_unaligned_uint16((data) + (offset), MBEDTLS_BSWAP16((uint16_t)(n)));                           \\\n        } else {                                                                                                       \\\n            mbedtls_put_unaligned_uint16((data) + (offset), (uint16_t)(n));                                            \\\n        }                                                                                                              \\\n    }\n\n/**\n * Get the unsigned 16 bits integer corresponding to two bytes in\n * big-endian order (MSB first).\n *\n * \\param   data    Base address of the memory to get the two bytes from.\n * \\param   offset  Offset from \\p data of the first and most significant\n *                  byte of the two bytes to build the 16 bits unsigned\n *                  integer from.\n */\n#define MBEDTLS_GET_UINT16_BE(data, offset)                                                                            \\\n    ((MBEDTLS_IS_BIG_ENDIAN) ? mbedtls_get_unaligned_uint16((data) + (offset))                                         \\\n                             : MBEDTLS_BSWAP16(mbedtls_get_unaligned_uint16((data) + (offset))))\n\n/**\n * Put in memory a 16 bits unsigned integer in big-endian order.\n *\n * \\param   n       16 bits unsigned integer to put in memory.\n * \\param   data    Base address of the memory where to put the 16\n *                  bits unsigned integer in.\n * \\param   offset  Offset from \\p data where to put the most significant\n *                  byte of the 16 bits unsigned integer \\p n.\n */\n#define MBEDTLS_PUT_UINT16_BE(n, data, offset)                                                                         \\\n    {                                                                                                                  \\\n        if (MBEDTLS_IS_BIG_ENDIAN) {                                                                                   \\\n            mbedtls_put_unaligned_uint16((data) + (offset), (uint16_t)(n));                                            \\\n        } else {                                                                                                       \\\n            mbedtls_put_unaligned_uint16((data) + (offset), MBEDTLS_BSWAP16((uint16_t)(n)));                           \\\n        }                                                                                                              \\\n    }\n\n/**\n * Get the unsigned 24 bits integer corresponding to three bytes in\n * big-endian order (MSB first).\n *\n * \\param   data    Base address of the memory to get the three bytes from.\n * \\param   offset  Offset from \\p data of the first and most significant\n *                  byte of the three bytes to build the 24 bits unsigned\n *                  integer from.\n */\n#define MBEDTLS_GET_UINT24_BE(data, offset)                                                                            \\\n    (((uint32_t)(data)[(offset)] << 16) | ((uint32_t)(data)[(offset) + 1] << 8) | ((uint32_t)(data)[(offset) + 2]))\n\n/**\n * Put in memory a 24 bits unsigned integer in big-endian order.\n *\n * \\param   n       24 bits unsigned integer to put in memory.\n * \\param   data    Base address of the memory where to put the 24\n *                  bits unsigned integer in.\n * \\param   offset  Offset from \\p data where to put the most significant\n *                  byte of the 24 bits unsigned integer \\p n.\n */\n#define MBEDTLS_PUT_UINT24_BE(n, data, offset)                                                                         \\\n    {                                                                                                                  \\\n        (data)[(offset)] = MBEDTLS_BYTE_2(n);                                                                          \\\n        (data)[(offset) + 1] = MBEDTLS_BYTE_1(n);                                                                      \\\n        (data)[(offset) + 2] = MBEDTLS_BYTE_0(n);                                                                      \\\n    }\n\n/**\n * Get the unsigned 24 bits integer corresponding to three bytes in\n * little-endian order (LSB first).\n *\n * \\param   data    Base address of the memory to get the three bytes from.\n * \\param   offset  Offset from \\p data of the first and least significant\n *                  byte of the three bytes to build the 24 bits unsigned\n *                  integer from.\n */\n#define MBEDTLS_GET_UINT24_LE(data, offset)                                                                            \\\n    (((uint32_t)(data)[(offset)]) | ((uint32_t)(data)[(offset) + 1] << 8) | ((uint32_t)(data)[(offset) + 2] << 16))\n\n/**\n * Put in memory a 24 bits unsigned integer in little-endian order.\n *\n * \\param   n       24 bits unsigned integer to put in memory.\n * \\param   data    Base address of the memory where to put the 24\n *                  bits unsigned integer in.\n * \\param   offset  Offset from \\p data where to put the least significant\n *                  byte of the 24 bits unsigned integer \\p n.\n */\n#define MBEDTLS_PUT_UINT24_LE(n, data, offset)                                                                         \\\n    {                                                                                                                  \\\n        (data)[(offset)] = MBEDTLS_BYTE_0(n);                                                                          \\\n        (data)[(offset) + 1] = MBEDTLS_BYTE_1(n);                                                                      \\\n        (data)[(offset) + 2] = MBEDTLS_BYTE_2(n);                                                                      \\\n    }\n\n/**\n * Get the unsigned 64 bits integer corresponding to eight bytes in\n * big-endian order (MSB first).\n *\n * \\param   data    Base address of the memory to get the eight bytes from.\n * \\param   offset  Offset from \\p data of the first and most significant\n *                  byte of the eight bytes to build the 64 bits unsigned\n *                  integer from.\n */\n#define MBEDTLS_GET_UINT64_BE(data, offset)                                                                            \\\n    ((MBEDTLS_IS_BIG_ENDIAN) ? mbedtls_get_unaligned_uint64((data) + (offset))                                         \\\n                             : MBEDTLS_BSWAP64(mbedtls_get_unaligned_uint64((data) + (offset))))\n\n/**\n * Put in memory a 64 bits unsigned integer in big-endian order.\n *\n * \\param   n       64 bits unsigned integer to put in memory.\n * \\param   data    Base address of the memory where to put the 64\n *                  bits unsigned integer in.\n * \\param   offset  Offset from \\p data where to put the most significant\n *                  byte of the 64 bits unsigned integer \\p n.\n */\n#define MBEDTLS_PUT_UINT64_BE(n, data, offset)                                                                         \\\n    {                                                                                                                  \\\n        if (MBEDTLS_IS_BIG_ENDIAN) {                                                                                   \\\n            mbedtls_put_unaligned_uint64((data) + (offset), (uint64_t)(n));                                            \\\n        } else {                                                                                                       \\\n            mbedtls_put_unaligned_uint64((data) + (offset), MBEDTLS_BSWAP64((uint64_t)(n)));                           \\\n        }                                                                                                              \\\n    }\n\n/**\n * Get the unsigned 64 bits integer corresponding to eight bytes in\n * little-endian order (LSB first).\n *\n * \\param   data    Base address of the memory to get the eight bytes from.\n * \\param   offset  Offset from \\p data of the first and least significant\n *                  byte of the eight bytes to build the 64 bits unsigned\n *                  integer from.\n */\n#define MBEDTLS_GET_UINT64_LE(data, offset)                                                                            \\\n    ((MBEDTLS_IS_BIG_ENDIAN) ? MBEDTLS_BSWAP64(mbedtls_get_unaligned_uint64((data) + (offset)))                        \\\n                             : mbedtls_get_unaligned_uint64((data) + (offset)))\n\n/**\n * Put in memory a 64 bits unsigned integer in little-endian order.\n *\n * \\param   n       64 bits unsigned integer to put in memory.\n * \\param   data    Base address of the memory where to put the 64\n *                  bits unsigned integer in.\n * \\param   offset  Offset from \\p data where to put the least significant\n *                  byte of the 64 bits unsigned integer \\p n.\n */\n#define MBEDTLS_PUT_UINT64_LE(n, data, offset)                                                                         \\\n    {                                                                                                                  \\\n        if (MBEDTLS_IS_BIG_ENDIAN) {                                                                                   \\\n            mbedtls_put_unaligned_uint64((data) + (offset), MBEDTLS_BSWAP64((uint64_t)(n)));                           \\\n        } else {                                                                                                       \\\n            mbedtls_put_unaligned_uint64((data) + (offset), (uint64_t)(n));                                            \\\n        }                                                                                                              \\\n    }\n\n#endif /* MBEDTLS_LIBRARY_ALIGNMENT_H */\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/mbedtls/common.h",
    "content": "/**\n * \\file common.h\n *\n * \\brief Utility macros for internal use in the library\n */\n/*\n *  Copyright The Mbed TLS Contributors\n *  SPDX-License-Identifier: Apache-2.0\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n *  not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef MBEDTLS_LIBRARY_COMMON_H\n#define MBEDTLS_LIBRARY_COMMON_H\n\n#include \"mbedtls/alignment.h\"\n\n#include <stddef.h>\n\n/** Helper to define a function as static except when building invasive tests.\n *\n * If a function is only used inside its own source file and should be\n * declared `static` to allow the compiler to optimize for code size,\n * but that function has unit tests, define it with\n * ```\n * MBEDTLS_STATIC_TESTABLE int mbedtls_foo(...) { ... }\n * ```\n * and declare it in a header in the `library/` directory with\n * ```\n * #if defined(MBEDTLS_TEST_HOOKS)\n * int mbedtls_foo(...);\n * #endif\n * ```\n */\n#if defined(MBEDTLS_TEST_HOOKS)\n#define MBEDTLS_STATIC_TESTABLE\n#else\n#define MBEDTLS_STATIC_TESTABLE static\n#endif\n\n#if defined(MBEDTLS_TEST_HOOKS)\nextern void (*mbedtls_test_hook_test_fail)(const char* test, int line, const char* file);\n#define MBEDTLS_TEST_HOOK_TEST_ASSERT(TEST)                                                                            \\\n    do {                                                                                                               \\\n        if ((!(TEST)) && ((*mbedtls_test_hook_test_fail) != NULL)) {                                                   \\\n            (*mbedtls_test_hook_test_fail)(#TEST, __LINE__, __FILE__);                                                 \\\n        }                                                                                                              \\\n    } while (0)\n#else\n#define MBEDTLS_TEST_HOOK_TEST_ASSERT(TEST)\n#endif /* defined(MBEDTLS_TEST_HOOKS) */\n\n/** Allow library to access its structs' private members.\n *\n * Although structs defined in header files are publicly available,\n * their members are private and should not be accessed by the user.\n */\n#define MBEDTLS_ALLOW_PRIVATE_ACCESS\n\n/** Return an offset into a buffer.\n *\n * This is just the addition of an offset to a pointer, except that this\n * function also accepts an offset of 0 into a buffer whose pointer is null.\n * (`p + n` has undefined behavior when `p` is null, even when `n == 0`.\n * A null pointer is a valid buffer pointer when the size is 0, for example\n * as the result of `malloc(0)` on some platforms.)\n *\n * \\param p     Pointer to a buffer of at least n bytes.\n *              This may be \\p NULL if \\p n is zero.\n * \\param n     An offset in bytes.\n * \\return      Pointer to offset \\p n in the buffer \\p p.\n *              Note that this is only a valid pointer if the size of the\n *              buffer is at least \\p n + 1.\n */\nstatic inline unsigned char* mbedtls_buffer_offset(unsigned char* p, size_t n) { return p == NULL ? NULL : p + n; }\n\n/** Return an offset into a read-only buffer.\n *\n * Similar to mbedtls_buffer_offset(), but for const pointers.\n *\n * \\param p     Pointer to a buffer of at least n bytes.\n *              This may be \\p NULL if \\p n is zero.\n * \\param n     An offset in bytes.\n * \\return      Pointer to offset \\p n in the buffer \\p p.\n *              Note that this is only a valid pointer if the size of the\n *              buffer is at least \\p n + 1.\n */\nstatic inline const unsigned char* mbedtls_buffer_offset_const(const unsigned char* p, size_t n)\n{\n    return p == NULL ? NULL : p + n;\n}\n\n/**\n * Perform a fast block XOR operation, such that\n * r[i] = a[i] ^ b[i] where 0 <= i < n\n *\n * \\param   r Pointer to result (buffer of at least \\p n bytes). \\p r\n *            may be equal to either \\p a or \\p b, but behaviour when\n *            it overlaps in other ways is undefined.\n * \\param   a Pointer to input (buffer of at least \\p n bytes)\n * \\param   b Pointer to input (buffer of at least \\p n bytes)\n * \\param   n Number of bytes to process.\n */\ninline void mbedtls_xor(unsigned char* r, const unsigned char* a, const unsigned char* b, size_t n)\n{\n    size_t i = 0;\n#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)\n    for (; (i + 4) <= n; i += 4) {\n        uint32_t x = mbedtls_get_unaligned_uint32(a + i) ^ mbedtls_get_unaligned_uint32(b + i);\n        mbedtls_put_unaligned_uint32(r + i, x);\n    }\n#endif\n    for (; i < n; i++) {\n        r[i] = a[i] ^ b[i];\n    }\n}\n\n/* Fix MSVC C99 compatible issue\n *      MSVC support __func__ from visual studio 2015( 1900 )\n *      Use MSVC predefine macro to avoid name check fail.\n */\n#if (defined(_MSC_VER) && (_MSC_VER <= 1900))\n#define /*no-check-names*/ __func__ __FUNCTION__\n#endif\n\n/* Define `asm` for compilers which don't define it. */\n/* *INDENT-OFF* */\n#ifndef asm\n#define asm __asm__\n#endif\n/* *INDENT-ON* */\n\n#endif /* MBEDTLS_LIBRARY_COMMON_H */\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/mbedtls/md5.h",
    "content": "/**\n * \\file md5.h\n *\n * \\brief MD5 message digest algorithm (hash function)\n *\n * \\warning   MD5 is considered a weak message digest and its use constitutes a\n *            security risk. We recommend considering stronger message\n *            digests instead.\n */\n/*\n *  Copyright The Mbed TLS Contributors\n *  SPDX-License-Identifier: Apache-2.0\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n *  not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n#ifndef MBEDTLS_MD5_H\n#define MBEDTLS_MD5_H\n#include \"mbedtls/private_access.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * \\brief          MD5 context structure\n *\n * \\warning        MD5 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n */\ntypedef struct mbedtls_md5_context {\n    uint32_t MBEDTLS_PRIVATE(total)[2];        /*!< number of bytes processed  */\n    uint32_t MBEDTLS_PRIVATE(state)[4];        /*!< intermediate digest state  */\n    unsigned char MBEDTLS_PRIVATE(buffer)[64]; /*!< data block being processed */\n} mbedtls_md5_context;\n\n/**\n * \\brief          Initialize MD5 context\n *\n * \\param ctx      MD5 context to be initialized\n *\n * \\warning        MD5 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n */\nvoid mbedtls_md5_init(mbedtls_md5_context* ctx);\n\n/**\n * \\brief          MD5 context setup\n *\n * \\param ctx      context to be initialized\n *\n * \\warning        MD5 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n */\nvoid mbedtls_md5_starts(mbedtls_md5_context* ctx);\n\n/**\n * \\brief          MD5 process buffer\n *\n * \\param ctx      MD5 context\n * \\param input    buffer holding the data\n * \\param ilen     length of the input data\n *\n * \\warning        MD5 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n */\nvoid mbedtls_md5_update(mbedtls_md5_context* ctx, const unsigned char* input, size_t ilen);\n\n/**\n * \\brief          MD5 final digest\n *\n * \\param ctx      MD5 context\n * \\param output   MD5 checksum result\n *\n * \\warning        MD5 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n */\nvoid mbedtls_md5_finish(mbedtls_md5_context* ctx, unsigned char output[16]);\n\n/**\n * \\brief          MD5 process data block (internal use only)\n *\n * \\param ctx      MD5 context\n * \\param data     buffer holding one block of data\n *\n * \\warning        MD5 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n */\nvoid mbedtls_internal_md5_process(mbedtls_md5_context* ctx, const unsigned char data[64]);\n\n/**\n * \\brief          Output = MD5( input buffer )\n *\n * \\param input    buffer holding the data\n * \\param ilen     length of the input data\n * \\param output   MD5 checksum result\n *\n * \\warning        MD5 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n */\nvoid mbedtls_md5(const unsigned char* input, size_t ilen, unsigned char output[16]);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* mbedtls_md5.h */\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/mbedtls/private_access.h",
    "content": "/**\n * \\file private_access.h\n *\n * \\brief Macro wrapper for struct's members.\n */\n/*\n *  Copyright The Mbed TLS Contributors\n *  SPDX-License-Identifier: Apache-2.0\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n *  not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef MBEDTLS_PRIVATE_ACCESS_H\n#define MBEDTLS_PRIVATE_ACCESS_H\n\n#ifndef MBEDTLS_ALLOW_PRIVATE_ACCESS\n#define MBEDTLS_PRIVATE(member) private_##member\n#else\n#define MBEDTLS_PRIVATE(member) member\n#endif\n\n#endif /* MBEDTLS_PRIVATE_ACCESS_H */\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/mbedtls/sha1.h",
    "content": "/**\n * \\file sha1.h\n *\n * \\brief This file contains SHA-1 definitions and functions.\n *\n * The Secure Hash Algorithm 1 (SHA-1) cryptographic hash function is defined in\n * <em>FIPS 180-4: Secure Hash Standard (SHS)</em>.\n *\n * \\warning   SHA-1 is considered a weak message digest and its use constitutes\n *            a security risk. We recommend considering stronger message\n *            digests instead.\n */\n/*\n *  Copyright The Mbed TLS Contributors\n *  SPDX-License-Identifier: Apache-2.0\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n *  not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n#ifndef MBEDTLS_SHA1_H\n#define MBEDTLS_SHA1_H\n#include \"mbedtls/private_access.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * \\brief          The SHA-1 context structure.\n *\n * \\warning        SHA-1 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n */\ntypedef struct mbedtls_sha1_context {\n    uint32_t MBEDTLS_PRIVATE(total)[2];        /*!< The number of Bytes processed.  */\n    uint32_t MBEDTLS_PRIVATE(state)[5];        /*!< The intermediate digest state.  */\n    unsigned char MBEDTLS_PRIVATE(buffer)[64]; /*!< The data block being processed. */\n} mbedtls_sha1_context;\n\n/**\n * \\brief          This function initializes a SHA-1 context.\n *\n * \\warning        SHA-1 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n * \\param ctx      The SHA-1 context to initialize.\n *                 This must not be \\c NULL.\n *\n */\nvoid mbedtls_sha1_init(mbedtls_sha1_context* ctx);\n\n/**\n * \\brief          This function starts a SHA-1 checksum calculation.\n *\n * \\warning        SHA-1 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n * \\param ctx      The SHA-1 context to initialize. This must be initialized.\n *\n */\nvoid mbedtls_sha1_starts(mbedtls_sha1_context* ctx);\n\n/**\n * \\brief          This function feeds an input buffer into an ongoing SHA-1\n *                 checksum calculation.\n *\n * \\warning        SHA-1 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n * \\param ctx      The SHA-1 context. This must be initialized\n *                 and have a hash operation started.\n * \\param input    The buffer holding the input data.\n *                 This must be a readable buffer of length \\p ilen Bytes.\n * \\param ilen     The length of the input data \\p input in Bytes.\n *\n */\nvoid mbedtls_sha1_update(mbedtls_sha1_context* ctx, const unsigned char* input, size_t ilen);\n\n/**\n * \\brief          This function finishes the SHA-1 operation, and writes\n *                 the result to the output buffer.\n *\n * \\warning        SHA-1 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n * \\param ctx      The SHA-1 context to use. This must be initialized and\n *                 have a hash operation started.\n * \\param output   The SHA-1 checksum result. This must be a writable\n *                 buffer of length \\c 20 Bytes.\n */\nvoid mbedtls_sha1_finish(mbedtls_sha1_context* ctx, unsigned char output[20]);\n\n/**\n * \\brief          SHA-1 process data block (internal use only).\n *\n * \\warning        SHA-1 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n * \\param ctx      The SHA-1 context to use. This must be initialized.\n * \\param data     The data block being processed. This must be a\n *                 readable buffer of length \\c 64 Bytes.\n *\n */\nvoid mbedtls_internal_sha1_process(mbedtls_sha1_context* ctx, const unsigned char data[64]);\n\n/**\n * \\brief          This function calculates the SHA-1 checksum of a buffer.\n *\n *                 The function allocates the context, performs the\n *                 calculation, and frees the context.\n *\n *                 The SHA-1 result is calculated as\n *                 output = SHA-1(input buffer).\n *\n * \\warning        SHA-1 is considered a weak message digest and its use\n *                 constitutes a security risk. We recommend considering\n *                 stronger message digests instead.\n *\n * \\param input    The buffer holding the input data.\n *                 This must be a readable buffer of length \\p ilen Bytes.\n * \\param ilen     The length of the input data \\p input in Bytes.\n * \\param output   The SHA-1 checksum result.\n *                 This must be a writable buffer of length \\c 20 Bytes.\n *\n */\nvoid mbedtls_sha1(const unsigned char* input, size_t ilen, unsigned char output[20]);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* mbedtls_sha1.h */\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/rapidjson/itoa.h",
    "content": "// Tencent is pleased to support the open source community by making RapidJSON available.\n//\n// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.\n//\n// Licensed under the MIT License (the \"License\"); you may not use this file except\n// in compliance with the License. You may obtain a copy of the License at\n//\n// http://opensource.org/licenses/MIT\n//\n// Unless required by applicable law or agreed to in writing, software distributed\n// under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n// CONDITIONS OF ANY KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n\nstatic const char itoaTable[200] = {\n    '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1',\n    '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2',\n    '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3',\n    '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5',\n    '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5',\n    '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8',\n    '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '8',\n    '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0', '9', '1',\n    '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'};\n\nstatic inline char* u64toa(uint64_t value, char* buffer)\n{\n\n    const uint64_t kTen8 = 100000000;\n    const uint64_t kTen9 = kTen8 * 10;\n    const uint64_t kTen10 = kTen8 * 100;\n    const uint64_t kTen11 = kTen8 * 1000;\n    const uint64_t kTen12 = kTen8 * 10000;\n    const uint64_t kTen13 = kTen8 * 100000;\n    const uint64_t kTen14 = kTen8 * 1000000;\n    const uint64_t kTen15 = kTen8 * 10000000;\n    const uint64_t kTen16 = kTen8 * kTen8;\n\n    if (value < kTen8) {\n        uint32_t v = (uint32_t)value;\n        if (v < 10000) {\n            const uint32_t d1 = (v / 100) << 1;\n            const uint32_t d2 = (v % 100) << 1;\n\n            if (v >= 1000)\n                *buffer++ = itoaTable[d1];\n            if (v >= 100)\n                *buffer++ = itoaTable[d1 + 1];\n            if (v >= 10)\n                *buffer++ = itoaTable[d2];\n            *buffer++ = itoaTable[d2 + 1];\n        } else {\n            // value = bbbbcccc\n            const uint32_t b = v / 10000;\n            const uint32_t c = v % 10000;\n\n            const uint32_t d1 = (b / 100) << 1;\n            const uint32_t d2 = (b % 100) << 1;\n\n            const uint32_t d3 = (c / 100) << 1;\n            const uint32_t d4 = (c % 100) << 1;\n\n            if (value >= 10000000)\n                *buffer++ = itoaTable[d1];\n            if (value >= 1000000)\n                *buffer++ = itoaTable[d1 + 1];\n            if (value >= 100000)\n                *buffer++ = itoaTable[d2];\n            *buffer++ = itoaTable[d2 + 1];\n\n            *buffer++ = itoaTable[d3];\n            *buffer++ = itoaTable[d3 + 1];\n            *buffer++ = itoaTable[d4];\n            *buffer++ = itoaTable[d4 + 1];\n        }\n    } else if (value < kTen16) {\n        const uint32_t v0 = (uint32_t)(value / kTen8);\n        const uint32_t v1 = (uint32_t)(value % kTen8);\n\n        const uint32_t b0 = v0 / 10000;\n        const uint32_t c0 = v0 % 10000;\n\n        const uint32_t d1 = (b0 / 100) << 1;\n        const uint32_t d2 = (b0 % 100) << 1;\n\n        const uint32_t d3 = (c0 / 100) << 1;\n        const uint32_t d4 = (c0 % 100) << 1;\n\n        const uint32_t b1 = v1 / 10000;\n        const uint32_t c1 = v1 % 10000;\n\n        const uint32_t d5 = (b1 / 100) << 1;\n        const uint32_t d6 = (b1 % 100) << 1;\n\n        const uint32_t d7 = (c1 / 100) << 1;\n        const uint32_t d8 = (c1 % 100) << 1;\n\n        if (value >= kTen15)\n            *buffer++ = itoaTable[d1];\n        if (value >= kTen14)\n            *buffer++ = itoaTable[d1 + 1];\n        if (value >= kTen13)\n            *buffer++ = itoaTable[d2];\n        if (value >= kTen12)\n            *buffer++ = itoaTable[d2 + 1];\n        if (value >= kTen11)\n            *buffer++ = itoaTable[d3];\n        if (value >= kTen10)\n            *buffer++ = itoaTable[d3 + 1];\n        if (value >= kTen9)\n            *buffer++ = itoaTable[d4];\n\n        *buffer++ = itoaTable[d4 + 1];\n        *buffer++ = itoaTable[d5];\n        *buffer++ = itoaTable[d5 + 1];\n        *buffer++ = itoaTable[d6];\n        *buffer++ = itoaTable[d6 + 1];\n        *buffer++ = itoaTable[d7];\n        *buffer++ = itoaTable[d7 + 1];\n        *buffer++ = itoaTable[d8];\n        *buffer++ = itoaTable[d8 + 1];\n    } else {\n        const uint32_t a = (uint32_t)(value / kTen16); // 1 to 1844\n        value %= kTen16;\n\n        if (a < 10)\n            *buffer++ = (char)('0' + (char)(a));\n        else if (a < 100) {\n            const uint32_t i = a << 1;\n            *buffer++ = itoaTable[i];\n            *buffer++ = itoaTable[i + 1];\n        } else if (a < 1000) {\n            *buffer++ = (char)('0' + (char)(a / 100));\n\n            const uint32_t i = (a % 100) << 1;\n            *buffer++ = itoaTable[i];\n            *buffer++ = itoaTable[i + 1];\n        } else {\n            const uint32_t i = (a / 100) << 1;\n            const uint32_t j = (a % 100) << 1;\n            *buffer++ = itoaTable[i];\n            *buffer++ = itoaTable[i + 1];\n            *buffer++ = itoaTable[j];\n            *buffer++ = itoaTable[j + 1];\n        }\n\n        const uint32_t v0 = (uint32_t)(value / kTen8);\n        const uint32_t v1 = (uint32_t)(value % kTen8);\n\n        const uint32_t b0 = v0 / 10000;\n        const uint32_t c0 = v0 % 10000;\n\n        const uint32_t d1 = (b0 / 100) << 1;\n        const uint32_t d2 = (b0 % 100) << 1;\n\n        const uint32_t d3 = (c0 / 100) << 1;\n        const uint32_t d4 = (c0 % 100) << 1;\n\n        const uint32_t b1 = v1 / 10000;\n        const uint32_t c1 = v1 % 10000;\n\n        const uint32_t d5 = (b1 / 100) << 1;\n        const uint32_t d6 = (b1 % 100) << 1;\n\n        const uint32_t d7 = (c1 / 100) << 1;\n        const uint32_t d8 = (c1 % 100) << 1;\n\n        *buffer++ = itoaTable[d1];\n        *buffer++ = itoaTable[d1 + 1];\n        *buffer++ = itoaTable[d2];\n        *buffer++ = itoaTable[d2 + 1];\n        *buffer++ = itoaTable[d3];\n        *buffer++ = itoaTable[d3 + 1];\n        *buffer++ = itoaTable[d4];\n        *buffer++ = itoaTable[d4 + 1];\n        *buffer++ = itoaTable[d5];\n        *buffer++ = itoaTable[d5 + 1];\n        *buffer++ = itoaTable[d6];\n        *buffer++ = itoaTable[d6 + 1];\n        *buffer++ = itoaTable[d7];\n        *buffer++ = itoaTable[d7 + 1];\n        *buffer++ = itoaTable[d8];\n        *buffer++ = itoaTable[d8 + 1];\n    }\n\n    return buffer;\n}\n\nchar* i64toa(int64_t value, char* buffer)\n{\n\n    uint64_t u = (uint64_t)value;\n    if (value < 0) {\n        *buffer++ = '-';\n        u = ~u + 1;\n    }\n\n    return u64toa(u, buffer);\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/tbcrypto/bb64.h",
    "content": "#pragma once\n\nvoid tbc_BB64Encode(const unsigned char *inputArray, int srcLen, int mode, unsigned char *dst);\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/tbcrypto/const.h",
    "content": "#pragma once\n\n#include \"base32/base32.h\"\n\n#define TBC_UUID_SIZE 36\n#define TBC_ANDROID_ID_SIZE 16\n\n#define TBC_MD5_HASH_SIZE 16\n#define TBC_MD5_STR_SIZE (TBC_MD5_HASH_SIZE * 2)\n\n#define TBC_SHA1_HASH_SIZE 20\n#define TBC_SHA1_HEX_SIZE (TBC_SHA1_HASH_SIZE * 2)\n#define TBC_SHA1_BASE32_SIZE (BASE32_LEN(TBC_SHA1_HASH_SIZE))\n\n#define TBC_HELIOS_HASH_SIZE 5\n#define TBC_HELIOS_BASE32_SIZE (BASE32_LEN(TBC_HELIOS_HASH_SIZE))\n\n#define TBC_CUID_GALAXY2_SIZE (TBC_MD5_STR_SIZE + 2 + TBC_HELIOS_BASE32_SIZE)\n#define TBC_C3_AID_SIZE (4 + TBC_SHA1_BASE32_SIZE + 1 + TBC_HELIOS_BASE32_SIZE)\n#define TBC_ENUID_SIZE (4 * ((TBC_CUID_GALAXY2_SIZE + 2) / 3) + 1)\n\nstatic const unsigned char HEX_UPPERCASE_TABLE[] = {'0', '1', '2', '3', '4', '5', '6', '7',\n                                                    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};\nstatic const unsigned char HEX_LOWERCASE_TABLE[] = {'0', '1', '2', '3', '4', '5', '6', '7',\n                                                    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/tbcrypto/cuid.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n/**\n * @brief impl of TiebaLite tieba/post/utils/helios\n *\n * @param src alloc and free by user\n * @param srcSize size of src. must >= 1\n * @param dst 5 bytes. alloc and free by user\n *\n * @note 12.x loc: com.baidu.tieba.l40.a / com.baidu.tieba.pz.a\n */\nvoid tbc_heliosHash(const unsigned char* src, size_t srcSize, unsigned char* dst);\n\n/**\n * @brief generate `cuid_galaxy2`\n *\n * @param androidID 16 bytes. alloc and free by user\n * @param dst 42 bytes. alloc and free by user\n *\n * @note 12.x loc: com.baidu.tieba.oz.m\n */\nvoid tbc_cuid_galaxy2(const unsigned char* androidID, unsigned char* dst);\n\n/**\n * @brief generate `c3_aid`\n *\n * @param androidID 16 bytes. alloc and free by user\n * @param uuid 36 bytes. alloc and free by user\n * @param dst 45 bytes. alloc and free by user\n *\n * @note 12.x loc: com.baidu.tieba.r50.f\n */\nvoid tbc_c3_aid(const unsigned char* androidID, const unsigned char* uuid, unsigned char* dst);\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/tbcrypto/error.h",
    "content": "#pragma once\n\n#define TBC_OK 0x0000\n\n#define TBC_MEMORY_ERROR 0x0001\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/tbcrypto/pywrap.h",
    "content": "#pragma once\n\n#define PY_SSIZE_T_CLEAN // use Py_ssize_t instead of int\n\n#ifdef TBC_PYTHON_DEBUG\n#    include <Python.h>\n#else\n#    ifdef _DEBUG\n#        undef _DEBUG // use these steps to avoid linking with python_d.lib\n#        define __TBC_RESTORE_DEBUG\n#    endif\n#    include <Python.h>\n#    ifdef __TBC_RESTORE_DEBUG\n#        define _DEBUG\n#        undef __TBC_RESTORE_DEBUG\n#    endif\n#endif\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/tbcrypto/rc442.h",
    "content": "#pragma once\n\n#define TBC_CBC_SECKEY_SIZE 16\n#define TBC_RC4_SIZE 16\n\n/**\n * @brief RC4 includes an extra XOR against 42\n *\n * @param xyusMd5Str 32 bytes. alloc and free by user\n * @param cbcSecKey 16 bytes. alloc and free by user\n * @param dst 16 bytes. alloc and free by user\n *\n * @return non 0 if any error\n *\n * @note 12.x loc: com.baidu.sofire.x6.oCOCcooCCoC.ocOOCCoOOCcC.CcooOoocOOo\n */\nvoid tbc_rc4_42(const unsigned char* xyusMd5Str, const unsigned char* cbcSecKey, unsigned char* dst);\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/tbcrypto/sign.h",
    "content": "#pragma once\n\n#include \"tbcrypto/pywrap.h\"\n\nPyObject* sign(PyObject* Py_UNUSED(self), PyObject* args);\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/include/xxHash/xxhash.h",
    "content": "/*\n * xxHash - Extremely Fast Hash algorithm\n * Header File\n * Copyright (C) 2012-2023 Yann Collet\n *\n * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above\n *      copyright notice, this list of conditions and the following disclaimer\n *      in the documentation and/or other materials provided with the\n *      distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * You can contact the author at:\n *   - xxHash homepage: https://www.xxhash.com\n *   - xxHash source repository: https://github.com/Cyan4973/xxHash\n */\n\n/*!\n * @mainpage xxHash\n *\n * xxHash is an extremely fast non-cryptographic hash algorithm, working at RAM speed\n * limits.\n *\n * It is proposed in four flavors, in three families:\n * 1. @ref XXH32_family\n *   - Classic 32-bit hash function. Simple, compact, and runs on almost all\n *     32-bit and 64-bit systems.\n * 2. @ref XXH64_family\n *   - Classic 64-bit adaptation of XXH32. Just as simple, and runs well on most\n *     64-bit systems (but _not_ 32-bit systems).\n * 3. @ref XXH3_family\n *   - Modern 64-bit and 128-bit hash function family which features improved\n *     strength and performance across the board, especially on smaller data.\n *     It benefits greatly from SIMD and 64-bit without requiring it.\n *\n * Benchmarks\n * ---\n * The reference system uses an Intel i7-9700K CPU, and runs Ubuntu x64 20.04.\n * The open source benchmark program is compiled with clang v10.0 using -O3 flag.\n *\n * | Hash Name            | ISA ext | Width | Large Data Speed | Small Data Velocity |\n * | -------------------- | ------- | ----: | ---------------: | ------------------: |\n * | XXH3_64bits()        | @b AVX2 |    64 |        59.4 GB/s |               133.1 |\n * | MeowHash             | AES-NI  |   128 |        58.2 GB/s |                52.5 |\n * | XXH3_128bits()       | @b AVX2 |   128 |        57.9 GB/s |               118.1 |\n * | CLHash               | PCLMUL  |    64 |        37.1 GB/s |                58.1 |\n * | XXH3_64bits()        | @b SSE2 |    64 |        31.5 GB/s |               133.1 |\n * | XXH3_128bits()       | @b SSE2 |   128 |        29.6 GB/s |               118.1 |\n * | RAM sequential read  |         |   N/A |        28.0 GB/s |                 N/A |\n * | ahash                | AES-NI  |    64 |        22.5 GB/s |               107.2 |\n * | City64               |         |    64 |        22.0 GB/s |                76.6 |\n * | T1ha2                |         |    64 |        22.0 GB/s |                99.0 |\n * | City128              |         |   128 |        21.7 GB/s |                57.7 |\n * | FarmHash             | AES-NI  |    64 |        21.3 GB/s |                71.9 |\n * | XXH64()              |         |    64 |        19.4 GB/s |                71.0 |\n * | SpookyHash           |         |    64 |        19.3 GB/s |                53.2 |\n * | Mum                  |         |    64 |        18.0 GB/s |                67.0 |\n * | CRC32C               | SSE4.2  |    32 |        13.0 GB/s |                57.9 |\n * | XXH32()              |         |    32 |         9.7 GB/s |                71.9 |\n * | City32               |         |    32 |         9.1 GB/s |                66.0 |\n * | Blake3*              | @b AVX2 |   256 |         4.4 GB/s |                 8.1 |\n * | Murmur3              |         |    32 |         3.9 GB/s |                56.1 |\n * | SipHash*             |         |    64 |         3.0 GB/s |                43.2 |\n * | Blake3*              | @b SSE2 |   256 |         2.4 GB/s |                 8.1 |\n * | HighwayHash          |         |    64 |         1.4 GB/s |                 6.0 |\n * | FNV64                |         |    64 |         1.2 GB/s |                62.7 |\n * | Blake2*              |         |   256 |         1.1 GB/s |                 5.1 |\n * | SHA1*                |         |   160 |         0.8 GB/s |                 5.6 |\n * | MD5*                 |         |   128 |         0.6 GB/s |                 7.8 |\n * @note\n *   - Hashes which require a specific ISA extension are noted. SSE2 is also noted,\n *     even though it is mandatory on x64.\n *   - Hashes with an asterisk are cryptographic. Note that MD5 is non-cryptographic\n *     by modern standards.\n *   - Small data velocity is a rough average of algorithm's efficiency for small\n *     data. For more accurate information, see the wiki.\n *   - More benchmarks and strength tests are found on the wiki:\n *         https://github.com/Cyan4973/xxHash/wiki\n *\n * Usage\n * ------\n * All xxHash variants use a similar API. Changing the algorithm is a trivial\n * substitution.\n *\n * @pre\n *    For functions which take an input and length parameter, the following\n *    requirements are assumed:\n *    - The range from [`input`, `input + length`) is valid, readable memory.\n *      - The only exception is if the `length` is `0`, `input` may be `NULL`.\n *    - For C++, the objects must have the *TriviallyCopyable* property, as the\n *      functions access bytes directly as if it was an array of `unsigned char`.\n *\n * @anchor single_shot_example\n * **Single Shot**\n *\n * These functions are stateless functions which hash a contiguous block of memory,\n * immediately returning the result. They are the easiest and usually the fastest\n * option.\n *\n * XXH32(), XXH64(), XXH3_64bits(), XXH3_128bits()\n *\n * @code{.c}\n *   #include <string.h>\n *   #include \"xxhash.h\"\n *\n *   // Example for a function which hashes a null terminated string with XXH32().\n *   XXH32_hash_t hash_string(const char* string, XXH32_hash_t seed)\n *   {\n *       // NULL pointers are only valid if the length is zero\n *       size_t length = (string == NULL) ? 0 : strlen(string);\n *       return XXH32(string, length, seed);\n *   }\n * @endcode\n *\n *\n * @anchor streaming_example\n * **Streaming**\n *\n * These groups of functions allow incremental hashing of unknown size, even\n * more than what would fit in a size_t.\n *\n * XXH32_reset(), XXH64_reset(), XXH3_64bits_reset(), XXH3_128bits_reset()\n *\n * @code{.c}\n *   #include <stdio.h>\n *   #include <assert.h>\n *   #include \"xxhash.h\"\n *   // Example for a function which hashes a FILE incrementally with XXH3_64bits().\n *   XXH64_hash_t hashFile(FILE* f)\n *   {\n *       // Allocate a state struct. Do not just use malloc() or new.\n *       XXH3_state_t* state = XXH3_createState();\n *       assert(state != NULL && \"Out of memory!\");\n *       // Reset the state to start a new hashing session.\n *       XXH3_64bits_reset(state);\n *       char buffer[4096];\n *       size_t count;\n *       // Read the file in chunks\n *       while ((count = fread(buffer, 1, sizeof(buffer), f)) != 0) {\n *           // Run update() as many times as necessary to process the data\n *           XXH3_64bits_update(state, buffer, count);\n *       }\n *       // Retrieve the finalized hash. This will not change the state.\n *       XXH64_hash_t result = XXH3_64bits_digest(state);\n *       // Free the state. Do not use free().\n *       XXH3_freeState(state);\n *       return result;\n *   }\n * @endcode\n *\n * Streaming functions generate the xxHash value from an incremental input.\n * This method is slower than single-call functions, due to state management.\n * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized.\n *\n * An XXH state must first be allocated using `XXH*_createState()`.\n *\n * Start a new hash by initializing the state with a seed using `XXH*_reset()`.\n *\n * Then, feed the hash state by calling `XXH*_update()` as many times as necessary.\n *\n * The function returns an error code, with 0 meaning OK, and any other value\n * meaning there is an error.\n *\n * Finally, a hash value can be produced anytime, by using `XXH*_digest()`.\n * This function returns the nn-bits hash as an int or long long.\n *\n * It's still possible to continue inserting input into the hash state after a\n * digest, and generate new hash values later on by invoking `XXH*_digest()`.\n *\n * When done, release the state using `XXH*_freeState()`.\n *\n *\n * @anchor canonical_representation_example\n * **Canonical Representation**\n *\n * The default return values from XXH functions are unsigned 32, 64 and 128 bit\n * integers.\n * This the simplest and fastest format for further post-processing.\n *\n * However, this leaves open the question of what is the order on the byte level,\n * since little and big endian conventions will store the same number differently.\n *\n * The canonical representation settles this issue by mandating big-endian\n * convention, the same convention as human-readable numbers (large digits first).\n *\n * When writing hash values to storage, sending them over a network, or printing\n * them, it's highly recommended to use the canonical representation to ensure\n * portability across a wider range of systems, present and future.\n *\n * The following functions allow transformation of hash values to and from\n * canonical format.\n *\n * XXH32_canonicalFromHash(), XXH32_hashFromCanonical(),\n * XXH64_canonicalFromHash(), XXH64_hashFromCanonical(),\n * XXH128_canonicalFromHash(), XXH128_hashFromCanonical(),\n *\n * @code{.c}\n *   #include <stdio.h>\n *   #include \"xxhash.h\"\n *\n *   // Example for a function which prints XXH32_hash_t in human readable format\n *   void printXxh32(XXH32_hash_t hash)\n *   {\n *       XXH32_canonical_t cano;\n *       XXH32_canonicalFromHash(&cano, hash);\n *       size_t i;\n *       for(i = 0; i < sizeof(cano.digest); ++i) {\n *           printf(\"%02x\", cano.digest[i]);\n *       }\n *       printf(\"\\n\");\n *   }\n *\n *   // Example for a function which converts XXH32_canonical_t to XXH32_hash_t\n *   XXH32_hash_t convertCanonicalToXxh32(XXH32_canonical_t cano)\n *   {\n *       XXH32_hash_t hash = XXH32_hashFromCanonical(&cano);\n *       return hash;\n *   }\n * @endcode\n *\n *\n * @file xxhash.h\n * xxHash prototypes and implementation\n */\n\n#if defined(__cplusplus) && !defined(XXH_NO_EXTERNC_GUARD)\nextern \"C\" {\n#endif\n\n/* ****************************\n *  INLINE mode\n ******************************/\n/*!\n * @defgroup public Public API\n * Contains details on the public xxHash functions.\n * @{\n */\n#ifdef XXH_DOXYGEN\n/*!\n * @brief Gives access to internal state declaration, required for static allocation.\n *\n * Incompatible with dynamic linking, due to risks of ABI changes.\n *\n * Usage:\n * @code{.c}\n *     #define XXH_STATIC_LINKING_ONLY\n *     #include \"xxhash.h\"\n * @endcode\n */\n#  define XXH_STATIC_LINKING_ONLY\n/* Do not undef XXH_STATIC_LINKING_ONLY for Doxygen */\n\n/*!\n * @brief Gives access to internal definitions.\n *\n * Usage:\n * @code{.c}\n *     #define XXH_STATIC_LINKING_ONLY\n *     #define XXH_IMPLEMENTATION\n *     #include \"xxhash.h\"\n * @endcode\n */\n#  define XXH_IMPLEMENTATION\n/* Do not undef XXH_IMPLEMENTATION for Doxygen */\n\n/*!\n * @brief Exposes the implementation and marks all functions as `inline`.\n *\n * Use these build macros to inline xxhash into the target unit.\n * Inlining improves performance on small inputs, especially when the length is\n * expressed as a compile-time constant:\n *\n *  https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html\n *\n * It also keeps xxHash symbols private to the unit, so they are not exported.\n *\n * Usage:\n * @code{.c}\n *     #define XXH_INLINE_ALL\n *     #include \"xxhash.h\"\n * @endcode\n * Do not compile and link xxhash.o as a separate object, as it is not useful.\n */\n#  define XXH_INLINE_ALL\n#  undef XXH_INLINE_ALL\n/*!\n * @brief Exposes the implementation without marking functions as inline.\n */\n#  define XXH_PRIVATE_API\n#  undef XXH_PRIVATE_API\n/*!\n * @brief Emulate a namespace by transparently prefixing all symbols.\n *\n * If you want to include _and expose_ xxHash functions from within your own\n * library, but also want to avoid symbol collisions with other libraries which\n * may also include xxHash, you can use @ref XXH_NAMESPACE to automatically prefix\n * any public symbol from xxhash library with the value of @ref XXH_NAMESPACE\n * (therefore, avoid empty or numeric values).\n *\n * Note that no change is required within the calling program as long as it\n * includes `xxhash.h`: Regular symbol names will be automatically translated\n * by this header.\n */\n#  define XXH_NAMESPACE /* YOUR NAME HERE */\n#  undef XXH_NAMESPACE\n#endif\n\n#define XXH_CAT(A,B) A##B\n#define XXH_NAME2(A,B) XXH_CAT(A,B)\n#define XXH_IPREF(Id) XXH_NAME2(XXH_NAMESPACE, Id)\n\n#if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \\\n    && !defined(XXH_INLINE_ALL_31684351384)\n   /* this section should be traversed only once */\n#  define XXH_INLINE_ALL_31684351384\n   /* give access to the advanced API, required to compile implementations */\n#  undef XXH_STATIC_LINKING_ONLY   /* avoid macro redef */\n#  define XXH_STATIC_LINKING_ONLY\n   /* make all functions private */\n#  undef XXH_PUBLIC_API\n#  if defined(__GNUC__)\n#    define XXH_PUBLIC_API static __inline __attribute__((__unused__))\n#  elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n#    define XXH_PUBLIC_API static inline\n#  elif defined(_MSC_VER)\n#    define XXH_PUBLIC_API static __inline\n#  else\n     /* note: this version may generate warnings for unused static functions */\n#    define XXH_PUBLIC_API static\n#  endif\n\n   /*\n    * This part deals with the special case where a unit wants to inline xxHash,\n    * but \"xxhash.h\" has previously been included without XXH_INLINE_ALL,\n    * such as part of some previously included *.h header file.\n    * Without further action, the new include would just be ignored,\n    * and functions would effectively _not_ be inlined (silent failure).\n    * The following macros solve this situation by prefixing all inlined names,\n    * avoiding naming collision with previous inclusions.\n    */\n   /* Before that, we unconditionally #undef all symbols,\n    * in case they were already defined with XXH_NAMESPACE.\n    * They will then be redefined for XXH_INLINE_ALL\n    */\n#  undef XXH_versionNumber\n    /* XXH32 */\n#  undef XXH32\n#  undef XXH32_createState\n#  undef XXH32_freeState\n#  undef XXH32_reset\n#  undef XXH32_update\n#  undef XXH32_digest\n#  undef XXH32_copyState\n#  undef XXH32_canonicalFromHash\n#  undef XXH32_hashFromCanonical\n    /* XXH64 */\n#  undef XXH64\n#  undef XXH64_createState\n#  undef XXH64_freeState\n#  undef XXH64_reset\n#  undef XXH64_update\n#  undef XXH64_digest\n#  undef XXH64_copyState\n#  undef XXH64_canonicalFromHash\n#  undef XXH64_hashFromCanonical\n    /* XXH3_64bits */\n#  undef XXH3_64bits\n#  undef XXH3_64bits_withSecret\n#  undef XXH3_64bits_withSeed\n#  undef XXH3_64bits_withSecretandSeed\n#  undef XXH3_createState\n#  undef XXH3_freeState\n#  undef XXH3_copyState\n#  undef XXH3_64bits_reset\n#  undef XXH3_64bits_reset_withSeed\n#  undef XXH3_64bits_reset_withSecret\n#  undef XXH3_64bits_update\n#  undef XXH3_64bits_digest\n#  undef XXH3_generateSecret\n    /* XXH3_128bits */\n#  undef XXH128\n#  undef XXH3_128bits\n#  undef XXH3_128bits_withSeed\n#  undef XXH3_128bits_withSecret\n#  undef XXH3_128bits_reset\n#  undef XXH3_128bits_reset_withSeed\n#  undef XXH3_128bits_reset_withSecret\n#  undef XXH3_128bits_reset_withSecretandSeed\n#  undef XXH3_128bits_update\n#  undef XXH3_128bits_digest\n#  undef XXH128_isEqual\n#  undef XXH128_cmp\n#  undef XXH128_canonicalFromHash\n#  undef XXH128_hashFromCanonical\n    /* Finally, free the namespace itself */\n#  undef XXH_NAMESPACE\n\n    /* employ the namespace for XXH_INLINE_ALL */\n#  define XXH_NAMESPACE XXH_INLINE_\n   /*\n    * Some identifiers (enums, type names) are not symbols,\n    * but they must nonetheless be renamed to avoid redeclaration.\n    * Alternative solution: do not redeclare them.\n    * However, this requires some #ifdefs, and has a more dispersed impact.\n    * Meanwhile, renaming can be achieved in a single place.\n    */\n#  define XXH_OK XXH_IPREF(XXH_OK)\n#  define XXH_ERROR XXH_IPREF(XXH_ERROR)\n#  define XXH_errorcode XXH_IPREF(XXH_errorcode)\n#  define XXH32_canonical_t  XXH_IPREF(XXH32_canonical_t)\n#  define XXH64_canonical_t  XXH_IPREF(XXH64_canonical_t)\n#  define XXH128_canonical_t XXH_IPREF(XXH128_canonical_t)\n#  define XXH32_state_s XXH_IPREF(XXH32_state_s)\n#  define XXH32_state_t XXH_IPREF(XXH32_state_t)\n#  define XXH64_state_s XXH_IPREF(XXH64_state_s)\n#  define XXH64_state_t XXH_IPREF(XXH64_state_t)\n#  define XXH3_state_s  XXH_IPREF(XXH3_state_s)\n#  define XXH3_state_t  XXH_IPREF(XXH3_state_t)\n#  define XXH128_hash_t XXH_IPREF(XXH128_hash_t)\n   /* Ensure the header is parsed again, even if it was previously included */\n#  undef XXHASH_H_5627135585666179\n#  undef XXHASH_H_STATIC_13879238742\n#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */\n\n/* ****************************************************************\n *  Stable API\n *****************************************************************/\n#ifndef XXHASH_H_5627135585666179\n#define XXHASH_H_5627135585666179 1\n\n/*! @brief Marks a global symbol. */\n#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API)\n#  if defined(_WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT))\n#    ifdef XXH_EXPORT\n#      define XXH_PUBLIC_API __declspec(dllexport)\n#    elif XXH_IMPORT\n#      define XXH_PUBLIC_API __declspec(dllimport)\n#    endif\n#  else\n#    define XXH_PUBLIC_API   /* do nothing */\n#  endif\n#endif\n\n#ifdef XXH_NAMESPACE\n#  define XXH_versionNumber XXH_IPREF(XXH_versionNumber)\n/* XXH32 */\n#  define XXH32 XXH_IPREF(XXH32)\n#  define XXH32_createState XXH_IPREF(XXH32_createState)\n#  define XXH32_freeState XXH_IPREF(XXH32_freeState)\n#  define XXH32_reset XXH_IPREF(XXH32_reset)\n#  define XXH32_update XXH_IPREF(XXH32_update)\n#  define XXH32_digest XXH_IPREF(XXH32_digest)\n#  define XXH32_copyState XXH_IPREF(XXH32_copyState)\n#  define XXH32_canonicalFromHash XXH_IPREF(XXH32_canonicalFromHash)\n#  define XXH32_hashFromCanonical XXH_IPREF(XXH32_hashFromCanonical)\n/* XXH64 */\n#  define XXH64 XXH_IPREF(XXH64)\n#  define XXH64_createState XXH_IPREF(XXH64_createState)\n#  define XXH64_freeState XXH_IPREF(XXH64_freeState)\n#  define XXH64_reset XXH_IPREF(XXH64_reset)\n#  define XXH64_update XXH_IPREF(XXH64_update)\n#  define XXH64_digest XXH_IPREF(XXH64_digest)\n#  define XXH64_copyState XXH_IPREF(XXH64_copyState)\n#  define XXH64_canonicalFromHash XXH_IPREF(XXH64_canonicalFromHash)\n#  define XXH64_hashFromCanonical XXH_IPREF(XXH64_hashFromCanonical)\n/* XXH3_64bits */\n#  define XXH3_64bits XXH_IPREF(XXH3_64bits)\n#  define XXH3_64bits_withSecret XXH_IPREF(XXH3_64bits_withSecret)\n#  define XXH3_64bits_withSeed XXH_IPREF(XXH3_64bits_withSeed)\n#  define XXH3_64bits_withSecretandSeed XXH_IPREF(XXH3_64bits_withSecretandSeed)\n#  define XXH3_createState XXH_IPREF(XXH3_createState)\n#  define XXH3_freeState XXH_IPREF(XXH3_freeState)\n#  define XXH3_copyState XXH_IPREF(XXH3_copyState)\n#  define XXH3_64bits_reset XXH_IPREF(XXH3_64bits_reset)\n#  define XXH3_64bits_reset_withSeed XXH_IPREF(XXH3_64bits_reset_withSeed)\n#  define XXH3_64bits_reset_withSecret XXH_IPREF(XXH3_64bits_reset_withSecret)\n#  define XXH3_64bits_reset_withSecretandSeed XXH_IPREF(XXH3_64bits_reset_withSecretandSeed)\n#  define XXH3_64bits_update XXH_IPREF(XXH3_64bits_update)\n#  define XXH3_64bits_digest XXH_IPREF(XXH3_64bits_digest)\n#  define XXH3_generateSecret XXH_IPREF(XXH3_generateSecret)\n#  define XXH3_generateSecret_fromSeed XXH_IPREF(XXH3_generateSecret_fromSeed)\n/* XXH3_128bits */\n#  define XXH128 XXH_IPREF(XXH128)\n#  define XXH3_128bits XXH_IPREF(XXH3_128bits)\n#  define XXH3_128bits_withSeed XXH_IPREF(XXH3_128bits_withSeed)\n#  define XXH3_128bits_withSecret XXH_IPREF(XXH3_128bits_withSecret)\n#  define XXH3_128bits_withSecretandSeed XXH_IPREF(XXH3_128bits_withSecretandSeed)\n#  define XXH3_128bits_reset XXH_IPREF(XXH3_128bits_reset)\n#  define XXH3_128bits_reset_withSeed XXH_IPREF(XXH3_128bits_reset_withSeed)\n#  define XXH3_128bits_reset_withSecret XXH_IPREF(XXH3_128bits_reset_withSecret)\n#  define XXH3_128bits_reset_withSecretandSeed XXH_IPREF(XXH3_128bits_reset_withSecretandSeed)\n#  define XXH3_128bits_update XXH_IPREF(XXH3_128bits_update)\n#  define XXH3_128bits_digest XXH_IPREF(XXH3_128bits_digest)\n#  define XXH128_isEqual XXH_IPREF(XXH128_isEqual)\n#  define XXH128_cmp     XXH_IPREF(XXH128_cmp)\n#  define XXH128_canonicalFromHash XXH_IPREF(XXH128_canonicalFromHash)\n#  define XXH128_hashFromCanonical XXH_IPREF(XXH128_hashFromCanonical)\n#endif\n\n\n/* *************************************\n*  Compiler specifics\n***************************************/\n\n/* specific declaration modes for Windows */\n#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API)\n#  if defined(_WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT))\n#    ifdef XXH_EXPORT\n#      define XXH_PUBLIC_API __declspec(dllexport)\n#    elif XXH_IMPORT\n#      define XXH_PUBLIC_API __declspec(dllimport)\n#    endif\n#  else\n#    define XXH_PUBLIC_API   /* do nothing */\n#  endif\n#endif\n\n#if defined (__GNUC__)\n# define XXH_CONSTF  __attribute__((__const__))\n# define XXH_PUREF   __attribute__((__pure__))\n# define XXH_MALLOCF __attribute__((__malloc__))\n#else\n# define XXH_CONSTF  /* disable */\n# define XXH_PUREF\n# define XXH_MALLOCF\n#endif\n\n/* *************************************\n*  Version\n***************************************/\n#define XXH_VERSION_MAJOR    0\n#define XXH_VERSION_MINOR    8\n#define XXH_VERSION_RELEASE  3\n/*! @brief Version number, encoded as two digits each */\n#define XXH_VERSION_NUMBER  (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)\n\n/*!\n * @brief Obtains the xxHash version.\n *\n * This is mostly useful when xxHash is compiled as a shared library,\n * since the returned value comes from the library, as opposed to header file.\n *\n * @return @ref XXH_VERSION_NUMBER of the invoked library.\n */\nXXH_PUBLIC_API XXH_CONSTF unsigned XXH_versionNumber (void);\n\n\n/* ****************************\n*  Common basic types\n******************************/\n#include <stddef.h>   /* size_t */\n/*!\n * @brief Exit code for the streaming API.\n */\ntypedef enum {\n    XXH_OK = 0, /*!< OK */\n    XXH_ERROR   /*!< Error */\n} XXH_errorcode;\n\n\n/*-**********************************************************************\n*  32-bit hash\n************************************************************************/\n#if defined(XXH_DOXYGEN) /* Don't show <stdint.h> include */\n/*!\n * @brief An unsigned 32-bit integer.\n *\n * Not necessarily defined to `uint32_t` but functionally equivalent.\n */\ntypedef uint32_t XXH32_hash_t;\n\n#elif !defined (__VMS) \\\n  && (defined (__cplusplus) \\\n  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )\n#   ifdef _AIX\n#     include <inttypes.h>\n#   else\n#     include <stdint.h>\n#   endif\n    typedef uint32_t XXH32_hash_t;\n\n#else\n#   include <limits.h>\n#   if UINT_MAX == 0xFFFFFFFFUL\n      typedef unsigned int XXH32_hash_t;\n#   elif ULONG_MAX == 0xFFFFFFFFUL\n      typedef unsigned long XXH32_hash_t;\n#   else\n#     error \"unsupported platform: need a 32-bit type\"\n#   endif\n#endif\n\n/*!\n * @}\n *\n * @defgroup XXH32_family XXH32 family\n * @ingroup public\n * Contains functions used in the classic 32-bit xxHash algorithm.\n *\n * @note\n *   XXH32 is useful for older platforms, with no or poor 64-bit performance.\n *   Note that the @ref XXH3_family provides competitive speed for both 32-bit\n *   and 64-bit systems, and offers true 64/128 bit hash results.\n *\n * @see @ref XXH64_family, @ref XXH3_family : Other xxHash families\n * @see @ref XXH32_impl for implementation details\n * @{\n */\n\n/*!\n * @brief Calculates the 32-bit hash of @p input using xxHash32.\n *\n * @param input The block of data to be hashed, at least @p length bytes in size.\n * @param length The length of @p input, in bytes.\n * @param seed The 32-bit seed to alter the hash's output predictably.\n *\n * @pre\n *   The memory between @p input and @p input + @p length must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p input may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * @return The calculated 32-bit xxHash32 value.\n *\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed);\n\n#ifndef XXH_NO_STREAM\n/*!\n * @typedef struct XXH32_state_s XXH32_state_t\n * @brief The opaque state struct for the XXH32 streaming API.\n *\n * @see XXH32_state_s for details.\n * @see @ref streaming_example \"Streaming Example\"\n */\ntypedef struct XXH32_state_s XXH32_state_t;\n\n/*!\n * @brief Allocates an @ref XXH32_state_t.\n *\n * @return An allocated pointer of @ref XXH32_state_t on success.\n * @return `NULL` on failure.\n *\n * @note Must be freed with XXH32_freeState().\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_MALLOCF XXH32_state_t* XXH32_createState(void);\n/*!\n * @brief Frees an @ref XXH32_state_t.\n *\n * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState().\n *\n * @return @ref XXH_OK.\n *\n * @note @p statePtr must be allocated with XXH32_createState().\n *\n * @see @ref streaming_example \"Streaming Example\"\n *\n */\nXXH_PUBLIC_API XXH_errorcode  XXH32_freeState(XXH32_state_t* statePtr);\n/*!\n * @brief Copies one @ref XXH32_state_t to another.\n *\n * @param dst_state The state to copy to.\n * @param src_state The state to copy from.\n * @pre\n *   @p dst_state and @p src_state must not be `NULL` and must not overlap.\n */\nXXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);\n\n/*!\n * @brief Resets an @ref XXH32_state_t to begin a new hash.\n *\n * @param statePtr The state struct to reset.\n * @param seed The 32-bit seed to alter the hash result predictably.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note This function resets and seeds a state. Call it before @ref XXH32_update().\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH32_reset  (XXH32_state_t* statePtr, XXH32_hash_t seed);\n\n/*!\n * @brief Consumes a block of @p input to an @ref XXH32_state_t.\n *\n * @param statePtr The state struct to update.\n * @param input The block of data to be hashed, at least @p length bytes in size.\n * @param length The length of @p input, in bytes.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n * @pre\n *   The memory between @p input and @p input + @p length must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p input may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note Call this to incrementally consume blocks of data.\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);\n\n/*!\n * @brief Returns the calculated hash value from an @ref XXH32_state_t.\n *\n * @param statePtr The state struct to calculate the hash from.\n *\n * @pre\n *  @p statePtr must not be `NULL`.\n *\n * @return The calculated 32-bit xxHash32 value from that state.\n *\n * @note\n *   Calling XXH32_digest() will not affect @p statePtr, so you can update,\n *   digest, and update again.\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);\n#endif /* !XXH_NO_STREAM */\n\n/*******   Canonical representation   *******/\n\n/*!\n * @brief Canonical (big endian) representation of @ref XXH32_hash_t.\n */\ntypedef struct {\n    unsigned char digest[4]; /*!< Hash bytes, big endian */\n} XXH32_canonical_t;\n\n/*!\n * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t.\n *\n * @param dst  The @ref XXH32_canonical_t pointer to be stored to.\n * @param hash The @ref XXH32_hash_t to be converted.\n *\n * @pre\n *   @p dst must not be `NULL`.\n *\n * @see @ref canonical_representation_example \"Canonical Representation Example\"\n */\nXXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);\n\n/*!\n * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t.\n *\n * @param src The @ref XXH32_canonical_t to convert.\n *\n * @pre\n *   @p src must not be `NULL`.\n *\n * @return The converted hash.\n *\n * @see @ref canonical_representation_example \"Canonical Representation Example\"\n */\nXXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);\n\n\n/*! @cond Doxygen ignores this part */\n#ifdef __has_attribute\n# define XXH_HAS_ATTRIBUTE(x) __has_attribute(x)\n#else\n# define XXH_HAS_ATTRIBUTE(x) 0\n#endif\n/*! @endcond */\n\n/*! @cond Doxygen ignores this part */\n/* C-language Attributes are added in C23. */\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L) && defined(__has_c_attribute)\n# define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x)\n#else\n# define XXH_HAS_C_ATTRIBUTE(x) 0\n#endif\n/*! @endcond */\n\n/*! @cond Doxygen ignores this part */\n#if defined(__cplusplus) && defined(__has_cpp_attribute)\n# define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)\n#else\n# define XXH_HAS_CPP_ATTRIBUTE(x) 0\n#endif\n/*! @endcond */\n\n/*! @cond Doxygen ignores this part */\n/*\n * Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute\n * introduced in CPP17 and C23.\n * CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough\n * C23   : https://en.cppreference.com/w/c/language/attributes/fallthrough\n */\n#if XXH_HAS_C_ATTRIBUTE(fallthrough) || XXH_HAS_CPP_ATTRIBUTE(fallthrough)\n# define XXH_FALLTHROUGH [[fallthrough]]\n#elif XXH_HAS_ATTRIBUTE(__fallthrough__)\n# define XXH_FALLTHROUGH __attribute__ ((__fallthrough__))\n#else\n# define XXH_FALLTHROUGH /* fallthrough */\n#endif\n/*! @endcond */\n\n/*! @cond Doxygen ignores this part */\n/*\n * Define XXH_NOESCAPE for annotated pointers in public API.\n * https://clang.llvm.org/docs/AttributeReference.html#noescape\n * As of writing this, only supported by clang.\n */\n#if XXH_HAS_ATTRIBUTE(noescape)\n# define XXH_NOESCAPE __attribute__((__noescape__))\n#else\n# define XXH_NOESCAPE\n#endif\n/*! @endcond */\n\n\n/*!\n * @}\n * @ingroup public\n * @{\n */\n\n#ifndef XXH_NO_LONG_LONG\n/*-**********************************************************************\n*  64-bit hash\n************************************************************************/\n#if defined(XXH_DOXYGEN) /* don't include <stdint.h> */\n/*!\n * @brief An unsigned 64-bit integer.\n *\n * Not necessarily defined to `uint64_t` but functionally equivalent.\n */\ntypedef uint64_t XXH64_hash_t;\n#elif !defined (__VMS) \\\n  && (defined (__cplusplus) \\\n  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )\n#   ifdef _AIX\n#     include <inttypes.h>\n#   else\n#     include <stdint.h>\n#   endif\n   typedef uint64_t XXH64_hash_t;\n#else\n#  include <limits.h>\n#  if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL\n     /* LP64 ABI says uint64_t is unsigned long */\n     typedef unsigned long XXH64_hash_t;\n#  else\n     /* the following type must have a width of 64-bit */\n     typedef unsigned long long XXH64_hash_t;\n#  endif\n#endif\n\n/*!\n * @}\n *\n * @defgroup XXH64_family XXH64 family\n * @ingroup public\n * @{\n * Contains functions used in the classic 64-bit xxHash algorithm.\n *\n * @note\n *   XXH3 provides competitive speed for both 32-bit and 64-bit systems,\n *   and offers true 64/128 bit hash results.\n *   It provides better speed for systems with vector processing capabilities.\n */\n\n/*!\n * @brief Calculates the 64-bit hash of @p input using xxHash64.\n *\n * @param input The block of data to be hashed, at least @p length bytes in size.\n * @param length The length of @p input, in bytes.\n * @param seed The 64-bit seed to alter the hash's output predictably.\n *\n * @pre\n *   The memory between @p input and @p input + @p length must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p input may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * @return The calculated 64-bit xxHash64 value.\n *\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed);\n\n/*******   Streaming   *******/\n#ifndef XXH_NO_STREAM\n/*!\n * @brief The opaque state struct for the XXH64 streaming API.\n *\n * @see XXH64_state_s for details.\n * @see @ref streaming_example \"Streaming Example\"\n */\ntypedef struct XXH64_state_s XXH64_state_t;   /* incomplete type */\n\n/*!\n * @brief Allocates an @ref XXH64_state_t.\n *\n * @return An allocated pointer of @ref XXH64_state_t on success.\n * @return `NULL` on failure.\n *\n * @note Must be freed with XXH64_freeState().\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_MALLOCF XXH64_state_t* XXH64_createState(void);\n\n/*!\n * @brief Frees an @ref XXH64_state_t.\n *\n * @param statePtr A pointer to an @ref XXH64_state_t allocated with @ref XXH64_createState().\n *\n * @return @ref XXH_OK.\n *\n * @note @p statePtr must be allocated with XXH64_createState().\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode  XXH64_freeState(XXH64_state_t* statePtr);\n\n/*!\n * @brief Copies one @ref XXH64_state_t to another.\n *\n * @param dst_state The state to copy to.\n * @param src_state The state to copy from.\n * @pre\n *   @p dst_state and @p src_state must not be `NULL` and must not overlap.\n */\nXXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dst_state, const XXH64_state_t* src_state);\n\n/*!\n * @brief Resets an @ref XXH64_state_t to begin a new hash.\n *\n * @param statePtr The state struct to reset.\n * @param seed The 64-bit seed to alter the hash result predictably.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note This function resets and seeds a state. Call it before @ref XXH64_update().\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH64_reset  (XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed);\n\n/*!\n * @brief Consumes a block of @p input to an @ref XXH64_state_t.\n *\n * @param statePtr The state struct to update.\n * @param input The block of data to be hashed, at least @p length bytes in size.\n * @param length The length of @p input, in bytes.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n * @pre\n *   The memory between @p input and @p input + @p length must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p input may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note Call this to incrementally consume blocks of data.\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH64_update (XXH_NOESCAPE XXH64_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length);\n\n/*!\n * @brief Returns the calculated hash value from an @ref XXH64_state_t.\n *\n * @param statePtr The state struct to calculate the hash from.\n *\n * @pre\n *  @p statePtr must not be `NULL`.\n *\n * @return The calculated 64-bit xxHash64 value from that state.\n *\n * @note\n *   Calling XXH64_digest() will not affect @p statePtr, so you can update,\n *   digest, and update again.\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_digest (XXH_NOESCAPE const XXH64_state_t* statePtr);\n#endif /* !XXH_NO_STREAM */\n/*******   Canonical representation   *******/\n\n/*!\n * @brief Canonical (big endian) representation of @ref XXH64_hash_t.\n */\ntypedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t;\n\n/*!\n * @brief Converts an @ref XXH64_hash_t to a big endian @ref XXH64_canonical_t.\n *\n * @param dst The @ref XXH64_canonical_t pointer to be stored to.\n * @param hash The @ref XXH64_hash_t to be converted.\n *\n * @pre\n *   @p dst must not be `NULL`.\n *\n * @see @ref canonical_representation_example \"Canonical Representation Example\"\n */\nXXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash);\n\n/*!\n * @brief Converts an @ref XXH64_canonical_t to a native @ref XXH64_hash_t.\n *\n * @param src The @ref XXH64_canonical_t to convert.\n *\n * @pre\n *   @p src must not be `NULL`.\n *\n * @return The converted hash.\n *\n * @see @ref canonical_representation_example \"Canonical Representation Example\"\n */\nXXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src);\n\n#ifndef XXH_NO_XXH3\n\n/*!\n * @}\n * ************************************************************************\n * @defgroup XXH3_family XXH3 family\n * @ingroup public\n * @{\n *\n * XXH3 is a more recent hash algorithm featuring:\n *  - Improved speed for both small and large inputs\n *  - True 64-bit and 128-bit outputs\n *  - SIMD acceleration\n *  - Improved 32-bit viability\n *\n * Speed analysis methodology is explained here:\n *\n *    https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html\n *\n * Compared to XXH64, expect XXH3 to run approximately\n * ~2x faster on large inputs and >3x faster on small ones,\n * exact differences vary depending on platform.\n *\n * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic,\n * but does not require it.\n * Most 32-bit and 64-bit targets that can run XXH32 smoothly can run XXH3\n * at competitive speeds, even without vector support. Further details are\n * explained in the implementation.\n *\n * XXH3 has a fast scalar implementation, but it also includes accelerated SIMD\n * implementations for many common platforms:\n *   - AVX512\n *   - AVX2\n *   - SSE2\n *   - ARM NEON\n *   - WebAssembly SIMD128\n *   - POWER8 VSX\n *   - s390x ZVector\n * This can be controlled via the @ref XXH_VECTOR macro, but it automatically\n * selects the best version according to predefined macros. For the x86 family, an\n * automatic runtime dispatcher is included separately in @ref xxh_x86dispatch.c.\n *\n * XXH3 implementation is portable:\n * it has a generic C90 formulation that can be compiled on any platform,\n * all implementations generate exactly the same hash value on all platforms.\n * Starting from v0.8.0, it's also labelled \"stable\", meaning that\n * any future version will also generate the same hash value.\n *\n * XXH3 offers 2 variants, _64bits and _128bits.\n *\n * When only 64 bits are needed, prefer invoking the _64bits variant, as it\n * reduces the amount of mixing, resulting in faster speed on small inputs.\n * It's also generally simpler to manipulate a scalar return type than a struct.\n *\n * The API supports one-shot hashing, streaming mode, and custom secrets.\n */\n\n/*!\n * @ingroup tuning\n * @brief Possible values for @ref XXH_VECTOR.\n *\n * Unless set explicitly, determined automatically.\n */\n#  define XXH_SCALAR 0 /*!< Portable scalar version */\n#  define XXH_SSE2   1 /*!< SSE2 for Pentium 4, Opteron, all x86_64. */\n#  define XXH_AVX2   2 /*!< AVX2 for Haswell and Bulldozer */\n#  define XXH_AVX512 3 /*!< AVX512 for Skylake and Icelake */\n#  define XXH_NEON   4 /*!< NEON for most ARMv7-A, all AArch64, and WASM SIMD128 */\n#  define XXH_VSX    5 /*!< VSX and ZVector for POWER8/z13 (64-bit) */\n#  define XXH_SVE    6 /*!< SVE for some ARMv8-A and ARMv9-A */\n#  define XXH_LSX    7 /*!< LSX (128-bit SIMD) for LoongArch64 */\n#  define XXH_LASX   8 /*!< LASX (256-bit SIMD) for LoongArch64 */\n#  define XXH_RVV    9 /*!< RVV (RISC-V Vector) for RISC-V */\n\n/*-**********************************************************************\n*  XXH3 64-bit variant\n************************************************************************/\n\n/*!\n * @brief Calculates 64-bit unseeded variant of XXH3 hash of @p input.\n *\n * @param input  The block of data to be hashed, at least @p length bytes in size.\n * @param length The length of @p input, in bytes.\n *\n * @pre\n *   The memory between @p input and @p input + @p length must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p input may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * @return The calculated 64-bit XXH3 hash value.\n *\n * @note\n *   This is equivalent to @ref XXH3_64bits_withSeed() with a seed of `0`, however\n *   it may have slightly better performance due to constant propagation of the\n *   defaults.\n *\n * @see\n *    XXH3_64bits_withSeed(), XXH3_64bits_withSecret(): other seeding variants\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length);\n\n/*!\n * @brief Calculates 64-bit seeded variant of XXH3 hash of @p input.\n *\n * @param input  The block of data to be hashed, at least @p length bytes in size.\n * @param length The length of @p input, in bytes.\n * @param seed   The 64-bit seed to alter the hash result predictably.\n *\n * @pre\n *   The memory between @p input and @p input + @p length must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p input may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * @return The calculated 64-bit XXH3 hash value.\n *\n * @note\n *    seed == 0 produces the same results as @ref XXH3_64bits().\n *\n * This variant generates a custom secret on the fly based on default secret\n * altered using the @p seed value.\n *\n * While this operation is decently fast, note that it's not completely free.\n *\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed);\n\n/*!\n * The bare minimum size for a custom secret.\n *\n * @see\n *  XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(),\n *  XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret().\n */\n#define XXH3_SECRET_SIZE_MIN 136\n\n/*!\n * @brief Calculates 64-bit variant of XXH3 with a custom \"secret\".\n *\n * @param data       The block of data to be hashed, at least @p len bytes in size.\n * @param len        The length of @p data, in bytes.\n * @param secret     The secret data.\n * @param secretSize The length of @p secret, in bytes.\n *\n * @return The calculated 64-bit XXH3 hash value.\n *\n * @pre\n *   The memory between @p data and @p data + @p len must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p data may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * It's possible to provide any blob of bytes as a \"secret\" to generate the hash.\n * This makes it more difficult for an external actor to prepare an intentional collision.\n * The main condition is that @p secretSize *must* be large enough (>= @ref XXH3_SECRET_SIZE_MIN).\n * However, the quality of the secret impacts the dispersion of the hash algorithm.\n * Therefore, the secret _must_ look like a bunch of random bytes.\n * Avoid \"trivial\" or structured data such as repeated sequences or a text document.\n * Whenever in doubt about the \"randomness\" of the blob of bytes,\n * consider employing @ref XXH3_generateSecret() instead (see below).\n * It will generate a proper high entropy secret derived from the blob of bytes.\n * Another advantage of using XXH3_generateSecret() is that\n * it guarantees that all bits within the initial blob of bytes\n * will impact every bit of the output.\n * This is not necessarily the case when using the blob of bytes directly\n * because, when hashing _small_ inputs, only a portion of the secret is employed.\n *\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize);\n\n\n/*******   Streaming   *******/\n#ifndef XXH_NO_STREAM\n/*\n * Streaming requires state maintenance.\n * This operation costs memory and CPU.\n * As a consequence, streaming is slower than one-shot hashing.\n * For better performance, prefer one-shot functions whenever applicable.\n */\n\n/*!\n * @brief The opaque state struct for the XXH3 streaming API.\n *\n * @see XXH3_state_s for details.\n * @see @ref streaming_example \"Streaming Example\"\n */\ntypedef struct XXH3_state_s XXH3_state_t;\nXXH_PUBLIC_API XXH_MALLOCF XXH3_state_t* XXH3_createState(void);\nXXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr);\n\n/*!\n * @brief Copies one @ref XXH3_state_t to another.\n *\n * @param dst_state The state to copy to.\n * @param src_state The state to copy from.\n * @pre\n *   @p dst_state and @p src_state must not be `NULL` and must not overlap.\n */\nXXH_PUBLIC_API void XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state);\n\n/*!\n * @brief Resets an @ref XXH3_state_t to begin a new hash.\n *\n * @param statePtr The state struct to reset.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note\n *   - This function resets `statePtr` and generate a secret with default parameters.\n *   - Call this function before @ref XXH3_64bits_update().\n *   - Digest will be equivalent to `XXH3_64bits()`.\n *\n * @see @ref streaming_example \"Streaming Example\"\n *\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr);\n\n/*!\n * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash.\n *\n * @param statePtr The state struct to reset.\n * @param seed     The 64-bit seed to alter the hash result predictably.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note\n *   - This function resets `statePtr` and generate a secret from `seed`.\n *   - Call this function before @ref XXH3_64bits_update().\n *   - Digest will be equivalent to `XXH3_64bits_withSeed()`.\n *\n * @see @ref streaming_example \"Streaming Example\"\n *\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed);\n\n/*!\n * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash.\n *\n * @param statePtr The state struct to reset.\n * @param secret     The secret data.\n * @param secretSize The length of @p secret, in bytes.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note\n *   `secret` is referenced, it _must outlive_ the hash streaming session.\n *\n * Similar to one-shot API, `secretSize` must be >= @ref XXH3_SECRET_SIZE_MIN,\n * and the quality of produced hash values depends on secret's entropy\n * (secret's content should look like a bunch of random bytes).\n * When in doubt about the randomness of a candidate `secret`,\n * consider employing `XXH3_generateSecret()` instead (see below).\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize);\n\n/*!\n * @brief Consumes a block of @p input to an @ref XXH3_state_t.\n *\n * @param statePtr The state struct to update.\n * @param input The block of data to be hashed, at least @p length bytes in size.\n * @param length The length of @p input, in bytes.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n * @pre\n *   The memory between @p input and @p input + @p length must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p input may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note Call this to incrementally consume blocks of data.\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length);\n\n/*!\n * @brief Returns the calculated XXH3 64-bit hash value from an @ref XXH3_state_t.\n *\n * @param statePtr The state struct to calculate the hash from.\n *\n * @pre\n *  @p statePtr must not be `NULL`.\n *\n * @return The calculated XXH3 64-bit hash value from that state.\n *\n * @note\n *   Calling XXH3_64bits_digest() will not affect @p statePtr, so you can update,\n *   digest, and update again.\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr);\n#endif /* !XXH_NO_STREAM */\n\n/* note : canonical representation of XXH3 is the same as XXH64\n * since they both produce XXH64_hash_t values */\n\n\n/*-**********************************************************************\n*  XXH3 128-bit variant\n************************************************************************/\n\n/*!\n * @brief The return value from 128-bit hashes.\n *\n * Stored in little endian order, although the fields themselves are in native\n * endianness.\n */\ntypedef struct {\n    XXH64_hash_t low64;   /*!< `value & 0xFFFFFFFFFFFFFFFF` */\n    XXH64_hash_t high64;  /*!< `value >> 64` */\n} XXH128_hash_t;\n\n/*!\n * @brief Calculates 128-bit unseeded variant of XXH3 of @p data.\n *\n * @param data The block of data to be hashed, at least @p length bytes in size.\n * @param len  The length of @p data, in bytes.\n *\n * @return The calculated 128-bit variant of XXH3 value.\n *\n * The 128-bit variant of XXH3 has more strength, but it has a bit of overhead\n * for shorter inputs.\n *\n * This is equivalent to @ref XXH3_128bits_withSeed() with a seed of `0`, however\n * it may have slightly better performance due to constant propagation of the\n * defaults.\n *\n * @see XXH3_128bits_withSeed(), XXH3_128bits_withSecret(): other seeding variants\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* data, size_t len);\n/*! @brief Calculates 128-bit seeded variant of XXH3 hash of @p data.\n *\n * @param data The block of data to be hashed, at least @p length bytes in size.\n * @param len  The length of @p data, in bytes.\n * @param seed The 64-bit seed to alter the hash result predictably.\n *\n * @return The calculated 128-bit variant of XXH3 value.\n *\n * @note\n *    seed == 0 produces the same results as @ref XXH3_64bits().\n *\n * This variant generates a custom secret on the fly based on default secret\n * altered using the @p seed value.\n *\n * While this operation is decently fast, note that it's not completely free.\n *\n * @see XXH3_128bits(), XXH3_128bits_withSecret(): other seeding variants\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSeed(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed);\n/*!\n * @brief Calculates 128-bit variant of XXH3 with a custom \"secret\".\n *\n * @param data       The block of data to be hashed, at least @p len bytes in size.\n * @param len        The length of @p data, in bytes.\n * @param secret     The secret data.\n * @param secretSize The length of @p secret, in bytes.\n *\n * @return The calculated 128-bit variant of XXH3 value.\n *\n * It's possible to provide any blob of bytes as a \"secret\" to generate the hash.\n * This makes it more difficult for an external actor to prepare an intentional collision.\n * The main condition is that @p secretSize *must* be large enough (>= @ref XXH3_SECRET_SIZE_MIN).\n * However, the quality of the secret impacts the dispersion of the hash algorithm.\n * Therefore, the secret _must_ look like a bunch of random bytes.\n * Avoid \"trivial\" or structured data such as repeated sequences or a text document.\n * Whenever in doubt about the \"randomness\" of the blob of bytes,\n * consider employing @ref XXH3_generateSecret() instead (see below).\n * It will generate a proper high entropy secret derived from the blob of bytes.\n * Another advantage of using XXH3_generateSecret() is that\n * it guarantees that all bits within the initial blob of bytes\n * will impact every bit of the output.\n * This is not necessarily the case when using the blob of bytes directly\n * because, when hashing _small_ inputs, only a portion of the secret is employed.\n *\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize);\n\n/*******   Streaming   *******/\n#ifndef XXH_NO_STREAM\n/*\n * Streaming requires state maintenance.\n * This operation costs memory and CPU.\n * As a consequence, streaming is slower than one-shot hashing.\n * For better performance, prefer one-shot functions whenever applicable.\n *\n * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits().\n * Use already declared XXH3_createState() and XXH3_freeState().\n *\n * All reset and streaming functions have same meaning as their 64-bit counterpart.\n */\n\n/*!\n * @brief Resets an @ref XXH3_state_t to begin a new hash.\n *\n * @param statePtr The state struct to reset.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note\n *   - This function resets `statePtr` and generate a secret with default parameters.\n *   - Call it before @ref XXH3_128bits_update().\n *   - Digest will be equivalent to `XXH3_128bits()`.\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr);\n\n/*!\n * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash.\n *\n * @param statePtr The state struct to reset.\n * @param seed     The 64-bit seed to alter the hash result predictably.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note\n *   - This function resets `statePtr` and generate a secret from `seed`.\n *   - Call it before @ref XXH3_128bits_update().\n *   - Digest will be equivalent to `XXH3_128bits_withSeed()`.\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed);\n/*!\n * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash.\n *\n * @param statePtr   The state struct to reset.\n * @param secret     The secret data.\n * @param secretSize The length of @p secret, in bytes.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * `secret` is referenced, it _must outlive_ the hash streaming session.\n * Similar to one-shot API, `secretSize` must be >= @ref XXH3_SECRET_SIZE_MIN,\n * and the quality of produced hash values depends on secret's entropy\n * (secret's content should look like a bunch of random bytes).\n * When in doubt about the randomness of a candidate `secret`,\n * consider employing `XXH3_generateSecret()` instead (see below).\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize);\n\n/*!\n * @brief Consumes a block of @p input to an @ref XXH3_state_t.\n *\n * Call this to incrementally consume blocks of data.\n *\n * @param statePtr The state struct to update.\n * @param input The block of data to be hashed, at least @p length bytes in size.\n * @param length The length of @p input, in bytes.\n *\n * @pre\n *   @p statePtr must not be `NULL`.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @note\n *   The memory between @p input and @p input + @p length must be valid,\n *   readable, contiguous memory. However, if @p length is `0`, @p input may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length);\n\n/*!\n * @brief Returns the calculated XXH3 128-bit hash value from an @ref XXH3_state_t.\n *\n * @param statePtr The state struct to calculate the hash from.\n *\n * @pre\n *  @p statePtr must not be `NULL`.\n *\n * @return The calculated XXH3 128-bit hash value from that state.\n *\n * @note\n *   Calling XXH3_128bits_digest() will not affect @p statePtr, so you can update,\n *   digest, and update again.\n *\n */\nXXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr);\n#endif /* !XXH_NO_STREAM */\n\n/* Following helper functions make it possible to compare XXH128_hast_t values.\n * Since XXH128_hash_t is a structure, this capability is not offered by the language.\n * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */\n\n/*!\n * @brief Check equality of two XXH128_hash_t values\n *\n * @param h1 The 128-bit hash value.\n * @param h2 Another 128-bit hash value.\n *\n * @return `1` if `h1` and `h2` are equal.\n * @return `0` if they are not.\n */\nXXH_PUBLIC_API XXH_PUREF int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2);\n\n/*!\n * @brief Compares two @ref XXH128_hash_t\n *\n * This comparator is compatible with stdlib's `qsort()`/`bsearch()`.\n *\n * @param h128_1 Left-hand side value\n * @param h128_2 Right-hand side value\n *\n * @return >0 if @p h128_1  > @p h128_2\n * @return =0 if @p h128_1 == @p h128_2\n * @return <0 if @p h128_1  < @p h128_2\n */\nXXH_PUBLIC_API XXH_PUREF int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2);\n\n\n/*******   Canonical representation   *******/\ntypedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t;\n\n\n/*!\n * @brief Converts an @ref XXH128_hash_t to a big endian @ref XXH128_canonical_t.\n *\n * @param dst  The @ref XXH128_canonical_t pointer to be stored to.\n * @param hash The @ref XXH128_hash_t to be converted.\n *\n * @pre\n *   @p dst must not be `NULL`.\n * @see @ref canonical_representation_example \"Canonical Representation Example\"\n */\nXXH_PUBLIC_API void XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash);\n\n/*!\n * @brief Converts an @ref XXH128_canonical_t to a native @ref XXH128_hash_t.\n *\n * @param src The @ref XXH128_canonical_t to convert.\n *\n * @pre\n *   @p src must not be `NULL`.\n *\n * @return The converted hash.\n * @see @ref canonical_representation_example \"Canonical Representation Example\"\n */\nXXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src);\n\n\n#endif  /* !XXH_NO_XXH3 */\n#endif  /* XXH_NO_LONG_LONG */\n\n/*!\n * @}\n */\n#endif /* XXHASH_H_5627135585666179 */\n\n\n\n#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742)\n#define XXHASH_H_STATIC_13879238742\n/* ****************************************************************************\n * This section contains declarations which are not guaranteed to remain stable.\n * They may change in future versions, becoming incompatible with a different\n * version of the library.\n * These declarations should only be used with static linking.\n * Never use them in association with dynamic linking!\n ***************************************************************************** */\n\n/*\n * These definitions are only present to allow static allocation\n * of XXH states, on stack or in a struct, for example.\n * Never **ever** access their members directly.\n */\n\n/*!\n * @internal\n * @brief Structure for XXH32 streaming API.\n *\n * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,\n * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is\n * an opaque type. This allows fields to safely be changed.\n *\n * Typedef'd to @ref XXH32_state_t.\n * Do not access the members of this struct directly.\n * @see XXH64_state_s, XXH3_state_s\n */\nstruct XXH32_state_s {\n   XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */\n   XXH32_hash_t large_len;    /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */\n   XXH32_hash_t acc[4];       /*!< Accumulator lanes */\n   unsigned char buffer[16];  /*!< Internal buffer for partial reads. */\n   XXH32_hash_t bufferedSize; /*!< Amount of data in @ref buffer */\n   XXH32_hash_t reserved;     /*!< Reserved field. Do not read nor write to it. */\n};   /* typedef'd to XXH32_state_t */\n\n\n#ifndef XXH_NO_LONG_LONG  /* defined when there is no 64-bit support */\n\n/*!\n * @internal\n * @brief Structure for XXH64 streaming API.\n *\n * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,\n * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is\n * an opaque type. This allows fields to safely be changed.\n *\n * Typedef'd to @ref XXH64_state_t.\n * Do not access the members of this struct directly.\n * @see XXH32_state_s, XXH3_state_s\n */\nstruct XXH64_state_s {\n   XXH64_hash_t total_len;    /*!< Total length hashed. This is always 64-bit. */\n   XXH64_hash_t acc[4];       /*!< Accumulator lanes */\n   unsigned char buffer[32];  /*!< Internal buffer for partial reads.. */\n   XXH32_hash_t bufferedSize; /*!< Amount of data in @ref buffer */\n   XXH32_hash_t reserved32;   /*!< Reserved field, needed for padding anyways*/\n   XXH64_hash_t reserved64;   /*!< Reserved field. Do not read or write to it. */\n};   /* typedef'd to XXH64_state_t */\n\n#ifndef XXH_NO_XXH3\n\n#if defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */\n/* In C++ alignas() is a keyword */\n#  define XXH_ALIGN(n)      alignas(n)\n#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* > C11 */\n#  define XXH_ALIGN(n)      _Alignas(n)\n#elif defined(__GNUC__)\n#  define XXH_ALIGN(n)      __attribute__ ((aligned(n)))\n#elif defined(_MSC_VER)\n#  define XXH_ALIGN(n)      __declspec(align(n))\n#else\n#  define XXH_ALIGN(n)   /* disabled */\n#endif\n\n/* Old GCC versions only accept the attribute after the type in structures. */\n#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L))   /* C11+ */ \\\n    && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \\\n    && defined(__GNUC__)\n#   define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align)\n#else\n#   define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type\n#endif\n\n/*!\n * @internal\n * @brief The size of the internal XXH3 buffer.\n *\n * This is the optimal update size for incremental hashing.\n *\n * @see XXH3_64b_update(), XXH3_128b_update().\n */\n#define XXH3_INTERNALBUFFER_SIZE 256\n\n/*!\n * @def XXH3_SECRET_DEFAULT_SIZE\n * @brief Default Secret's size\n *\n * This is the size of internal XXH3_kSecret\n * and is needed by XXH3_generateSecret_fromSeed().\n *\n * Not to be confused with @ref XXH3_SECRET_SIZE_MIN.\n */\n#define XXH3_SECRET_DEFAULT_SIZE 192\n\n/*!\n * @internal\n * @brief Structure for XXH3 streaming API.\n *\n * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,\n * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined.\n * Otherwise it is an opaque type.\n * Never use this definition in combination with dynamic library.\n * This allows fields to safely be changed in the future.\n *\n * @note ** This structure has a strict alignment requirement of 64 bytes!! **\n * Do not allocate this with `malloc()` or `new`,\n * it will not be sufficiently aligned.\n * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation.\n *\n * Typedef'd to @ref XXH3_state_t.\n * Do never access the members of this struct directly.\n *\n * @see XXH3_INITSTATE() for stack initialization.\n * @see XXH3_createState(), XXH3_freeState().\n * @see XXH32_state_s, XXH64_state_s\n */\nstruct XXH3_state_s {\n   XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]);\n       /*!< The 8 accumulators. See @ref XXH32_state_s::acc and @ref XXH64_state_s::acc */\n   XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]);\n       /*!< Used to store a custom secret generated from a seed. */\n   XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]);\n       /*!< The internal buffer. @see XXH32_state_s::mem32 */\n   XXH32_hash_t bufferedSize;\n       /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */\n   XXH32_hash_t useSeed;\n       /*!< Reserved field. Needed for padding on 64-bit. */\n   size_t nbStripesSoFar;\n       /*!< Number or stripes processed. */\n   XXH64_hash_t totalLen;\n       /*!< Total length hashed. 64-bit even on 32-bit targets. */\n   size_t nbStripesPerBlock;\n       /*!< Number of stripes per block. */\n   size_t secretLimit;\n       /*!< Size of @ref customSecret or @ref extSecret */\n   XXH64_hash_t seed;\n       /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */\n   XXH64_hash_t reserved64;\n       /*!< Reserved field. */\n   const unsigned char* extSecret;\n       /*!< Reference to an external secret for the _withSecret variants, NULL\n        *   for other variants. */\n   /* note: there may be some padding at the end due to alignment on 64 bytes */\n}; /* typedef'd to XXH3_state_t */\n\n#undef XXH_ALIGN_MEMBER\n\n/*!\n * @brief Initializes a stack-allocated `XXH3_state_s`.\n *\n * When the @ref XXH3_state_t structure is merely emplaced on stack,\n * it should be initialized with XXH3_INITSTATE() or a memset()\n * in case its first reset uses XXH3_NNbits_reset_withSeed().\n * This init can be omitted if the first reset uses default or _withSecret mode.\n * This operation isn't necessary when the state is created with XXH3_createState().\n * Note that this doesn't prepare the state for a streaming operation,\n * it's still necessary to use XXH3_NNbits_reset*() afterwards.\n */\n#define XXH3_INITSTATE(XXH3_state_ptr)                       \\\n    do {                                                     \\\n        XXH3_state_t* tmp_xxh3_state_ptr = (XXH3_state_ptr); \\\n        tmp_xxh3_state_ptr->seed = 0;                        \\\n        tmp_xxh3_state_ptr->extSecret = NULL;                \\\n    } while(0)\n\n\n/*!\n * @brief Calculates the 128-bit hash of @p data using XXH3.\n *\n * @param data The block of data to be hashed, at least @p len bytes in size.\n * @param len  The length of @p data, in bytes.\n * @param seed The 64-bit seed to alter the hash's output predictably.\n *\n * @pre\n *   The memory between @p data and @p data + @p len must be valid,\n *   readable, contiguous memory. However, if @p len is `0`, @p data may be\n *   `NULL`. In C++, this also must be *TriviallyCopyable*.\n *\n * @return The calculated 128-bit XXH3 value.\n *\n * @see @ref single_shot_example \"Single Shot Example\" for an example.\n */\nXXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed);\n\n\n/* ===   Experimental API   === */\n/* Symbols defined below must be considered tied to a specific library version. */\n\n/*!\n * @brief Derive a high-entropy secret from any user-defined content, named customSeed.\n *\n * @param secretBuffer    A writable buffer for derived high-entropy secret data.\n * @param secretSize      Size of secretBuffer, in bytes.  Must be >= XXH3_SECRET_SIZE_MIN.\n * @param customSeed      A user-defined content.\n * @param customSeedSize  Size of customSeed, in bytes.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * The generated secret can be used in combination with `*_withSecret()` functions.\n * The `_withSecret()` variants are useful to provide a higher level of protection\n * than 64-bit seed, as it becomes much more difficult for an external actor to\n * guess how to impact the calculation logic.\n *\n * The function accepts as input a custom seed of any length and any content,\n * and derives from it a high-entropy secret of length @p secretSize into an\n * already allocated buffer @p secretBuffer.\n *\n * The generated secret can then be used with any `*_withSecret()` variant.\n * The functions @ref XXH3_128bits_withSecret(), @ref XXH3_64bits_withSecret(),\n * @ref XXH3_128bits_reset_withSecret() and @ref XXH3_64bits_reset_withSecret()\n * are part of this list. They all accept a `secret` parameter\n * which must be large enough for implementation reasons (>= @ref XXH3_SECRET_SIZE_MIN)\n * _and_ feature very high entropy (consist of random-looking bytes).\n * These conditions can be a high bar to meet, so @ref XXH3_generateSecret() can\n * be employed to ensure proper quality.\n *\n * @p customSeed can be anything. It can have any size, even small ones,\n * and its content can be anything, even \"poor entropy\" sources such as a bunch\n * of zeroes. The resulting `secret` will nonetheless provide all required qualities.\n *\n * @pre\n *   - @p secretSize must be >= @ref XXH3_SECRET_SIZE_MIN\n *   - When @p customSeedSize > 0, supplying NULL as customSeed is undefined behavior.\n *\n * Example code:\n * @code{.c}\n *    #include <stdio.h>\n *    #include <stdlib.h>\n *    #include <string.h>\n *    #define XXH_STATIC_LINKING_ONLY // expose unstable API\n *    #include \"xxhash.h\"\n *    // Hashes argv[2] using the entropy from argv[1].\n *    int main(int argc, char* argv[])\n *    {\n *        char secret[XXH3_SECRET_SIZE_MIN];\n *        if (argv != 3) { return 1; }\n *        XXH3_generateSecret(secret, sizeof(secret), argv[1], strlen(argv[1]));\n *        XXH64_hash_t h = XXH3_64bits_withSecret(\n *             argv[2], strlen(argv[2]),\n *             secret, sizeof(secret)\n *        );\n *        printf(\"%016llx\\n\", (unsigned long long) h);\n *    }\n * @endcode\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize);\n\n/*!\n * @brief Generate the same secret as the _withSeed() variants.\n *\n * @param secretBuffer A writable buffer of @ref XXH3_SECRET_DEFAULT_SIZE bytes\n * @param seed         The 64-bit seed to alter the hash result predictably.\n *\n * The generated secret can be used in combination with\n *`*_withSecret()` and `_withSecretandSeed()` variants.\n *\n * Example C++ `std::string` hash class:\n * @code{.cpp}\n *    #include <string>\n *    #define XXH_STATIC_LINKING_ONLY // expose unstable API\n *    #include \"xxhash.h\"\n *    // Slow, seeds each time\n *    class HashSlow {\n *        XXH64_hash_t seed;\n *    public:\n *        HashSlow(XXH64_hash_t s) : seed{s} {}\n *        size_t operator()(const std::string& x) const {\n *            return size_t{XXH3_64bits_withSeed(x.c_str(), x.length(), seed)};\n *        }\n *    };\n *    // Fast, caches the seeded secret for future uses.\n *    class HashFast {\n *        unsigned char secret[XXH3_SECRET_DEFAULT_SIZE];\n *    public:\n *        HashFast(XXH64_hash_t s) {\n *            XXH3_generateSecret_fromSeed(secret, seed);\n *        }\n *        size_t operator()(const std::string& x) const {\n *            return size_t{\n *                XXH3_64bits_withSecret(x.c_str(), x.length(), secret, sizeof(secret))\n *            };\n *        }\n *    };\n * @endcode\n */\nXXH_PUBLIC_API void XXH3_generateSecret_fromSeed(XXH_NOESCAPE void* secretBuffer, XXH64_hash_t seed);\n\n/*!\n * @brief Maximum size of \"short\" key in bytes.\n */\n#define XXH3_MIDSIZE_MAX 240\n\n/*!\n * @brief Calculates 64/128-bit seeded variant of XXH3 hash of @p data.\n *\n * @param data       The block of data to be hashed, at least @p len bytes in size.\n * @param len        The length of @p data, in bytes.\n * @param secret     The secret data.\n * @param secretSize The length of @p secret, in bytes.\n * @param seed       The 64-bit seed to alter the hash result predictably.\n *\n * These variants generate hash values using either:\n * - @p seed for \"short\" keys (< @ref XXH3_MIDSIZE_MAX = 240 bytes)\n * - @p secret for \"large\" keys (>= @ref XXH3_MIDSIZE_MAX).\n *\n * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`.\n * `_withSeed()` has to generate the secret on the fly for \"large\" keys.\n * It's fast, but can be perceptible for \"not so large\" keys (< 1 KB).\n * `_withSecret()` has to generate the masks on the fly for \"small\" keys,\n * which requires more instructions than _withSeed() variants.\n * Therefore, _withSecretandSeed variant combines the best of both worlds.\n *\n * When @p secret has been generated by XXH3_generateSecret_fromSeed(),\n * this variant produces *exactly* the same results as `_withSeed()` variant,\n * hence offering only a pure speed benefit on \"large\" input,\n * by skipping the need to regenerate the secret for every large input.\n *\n * Another usage scenario is to hash the secret to a 64-bit hash value,\n * for example with XXH3_64bits(), which then becomes the seed,\n * and then employ both the seed and the secret in _withSecretandSeed().\n * On top of speed, an added benefit is that each bit in the secret\n * has a 50% chance to swap each bit in the output, via its impact to the seed.\n *\n * This is not guaranteed when using the secret directly in \"small data\" scenarios,\n * because only portions of the secret are employed for small data.\n */\nXXH_PUBLIC_API XXH_PUREF XXH64_hash_t\nXXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* data, size_t len,\n                              XXH_NOESCAPE const void* secret, size_t secretSize,\n                              XXH64_hash_t seed);\n\n/*!\n * @brief Calculates 128-bit seeded variant of XXH3 hash of @p data.\n *\n * @param input      The memory segment to be hashed, at least @p len bytes in size.\n * @param length     The length of @p data, in bytes.\n * @param secret     The secret used to alter hash result predictably.\n * @param secretSize The length of @p secret, in bytes (must be >= XXH3_SECRET_SIZE_MIN)\n * @param seed64     The 64-bit seed to alter the hash result predictably.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @see XXH3_64bits_withSecretandSeed(): contract is the same.\n */\nXXH_PUBLIC_API XXH_PUREF XXH128_hash_t\nXXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length,\n                               XXH_NOESCAPE const void* secret, size_t secretSize,\n                               XXH64_hash_t seed64);\n\n#ifndef XXH_NO_STREAM\n/*!\n * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash.\n *\n * @param statePtr   A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState().\n * @param secret     The secret data.\n * @param secretSize The length of @p secret, in bytes.\n * @param seed64     The 64-bit seed to alter the hash result predictably.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @see XXH3_64bits_withSecretandSeed(). Contract is identical.\n */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr,\n                                    XXH_NOESCAPE const void* secret, size_t secretSize,\n                                    XXH64_hash_t seed64);\n\n/*!\n * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash.\n *\n * @param statePtr   A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState().\n * @param secret     The secret data.\n * @param secretSize The length of @p secret, in bytes.\n * @param seed64     The 64-bit seed to alter the hash result predictably.\n *\n * @return @ref XXH_OK on success.\n * @return @ref XXH_ERROR on failure.\n *\n * @see XXH3_64bits_withSecretandSeed(). Contract is identical.\n *\n * Note: there was a bug in an earlier version of this function (<= v0.8.2)\n * that would make it generate an incorrect hash value\n * when @p seed == 0 and @p length < XXH3_MIDSIZE_MAX\n * and @p secret is different from XXH3_generateSecret_fromSeed().\n * As stated in the contract, the correct hash result must be\n * the same as XXH3_128bits_withSeed() when @p length <= XXH3_MIDSIZE_MAX.\n * Results generated by this older version are wrong, hence not comparable.\n */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr,\n                                     XXH_NOESCAPE const void* secret, size_t secretSize,\n                                     XXH64_hash_t seed64);\n\n#endif /* !XXH_NO_STREAM */\n\n#endif  /* !XXH_NO_XXH3 */\n#endif  /* XXH_NO_LONG_LONG */\n#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)\n#  define XXH_IMPLEMENTATION\n#endif\n\n#endif  /* defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) */\n\n\n/* ======================================================================== */\n/* ======================================================================== */\n/* ======================================================================== */\n\n\n/*-**********************************************************************\n * xxHash implementation\n *-**********************************************************************\n * xxHash's implementation used to be hosted inside xxhash.c.\n *\n * However, inlining requires implementation to be visible to the compiler,\n * hence be included alongside the header.\n * Previously, implementation was hosted inside xxhash.c,\n * which was then #included when inlining was activated.\n * This construction created issues with a few build and install systems,\n * as it required xxhash.c to be stored in /include directory.\n *\n * xxHash implementation is now directly integrated within xxhash.h.\n * As a consequence, xxhash.c is no longer needed in /include.\n *\n * xxhash.c is still available and is still useful.\n * In a \"normal\" setup, when xxhash is not inlined,\n * xxhash.h only exposes the prototypes and public symbols,\n * while xxhash.c can be built into an object file xxhash.o\n * which can then be linked into the final binary.\n ************************************************************************/\n\n#if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \\\n   || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387)\n#  define XXH_IMPLEM_13a8737387\n\n/* *************************************\n*  Tuning parameters\n***************************************/\n\n/*!\n * @defgroup tuning Tuning parameters\n * @{\n *\n * Various macros to control xxHash's behavior.\n */\n#ifdef XXH_DOXYGEN\n/*!\n * @brief Define this to disable 64-bit code.\n *\n * Useful if only using the @ref XXH32_family and you have a strict C90 compiler.\n */\n#  define XXH_NO_LONG_LONG\n#  undef XXH_NO_LONG_LONG /* don't actually */\n/*!\n * @brief Controls how unaligned memory is accessed.\n *\n * By default, access to unaligned memory is controlled by `memcpy()`, which is\n * safe and portable.\n *\n * Unfortunately, on some target/compiler combinations, the generated assembly\n * is sub-optimal.\n *\n * The below switch allow selection of a different access method\n * in the search for improved performance.\n *\n * @par Possible options:\n *\n *  - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy`\n *   @par\n *     Use `memcpy()`. Safe and portable. Note that most modern compilers will\n *     eliminate the function call and treat it as an unaligned access.\n *\n *  - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((aligned(1)))`\n *   @par\n *     Depends on compiler extensions and is therefore not portable.\n *     This method is safe _if_ your compiler supports it,\n *     and *generally* as fast or faster than `memcpy`.\n *\n *  - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast\n *  @par\n *     Casts directly and dereferences. This method doesn't depend on the\n *     compiler, but it violates the C standard as it directly dereferences an\n *     unaligned pointer. It can generate buggy code on targets which do not\n *     support unaligned memory accesses, but in some circumstances, it's the\n *     only known way to get the most performance.\n *\n *  - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift\n *  @par\n *     Also portable. This can generate the best code on old compilers which don't\n *     inline small `memcpy()` calls, and it might also be faster on big-endian\n *     systems which lack a native byteswap instruction. However, some compilers\n *     will emit literal byteshifts even if the target supports unaligned access.\n *\n *\n * @warning\n *   Methods 1 and 2 rely on implementation-defined behavior. Use these with\n *   care, as what works on one compiler/platform/optimization level may cause\n *   another to read garbage data or even crash.\n *\n * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details.\n *\n * Prefer these methods in priority order (0 > 3 > 1 > 2)\n */\n#  define XXH_FORCE_MEMORY_ACCESS 0\n\n/*!\n * @def XXH_SIZE_OPT\n * @brief Controls how much xxHash optimizes for size.\n *\n * xxHash, when compiled, tends to result in a rather large binary size. This\n * is mostly due to heavy usage to forced inlining and constant folding of the\n * @ref XXH3_family to increase performance.\n *\n * However, some developers prefer size over speed. This option can\n * significantly reduce the size of the generated code. When using the `-Os`\n * or `-Oz` options on GCC or Clang, this is defined to 1 by default,\n * otherwise it is defined to 0.\n *\n * Most of these size optimizations can be controlled manually.\n *\n * This is a number from 0-2.\n *  - `XXH_SIZE_OPT` == 0: Default. xxHash makes no size optimizations. Speed\n *    comes first.\n *  - `XXH_SIZE_OPT` == 1: Default for `-Os` and `-Oz`. xxHash is more\n *    conservative and disables hacks that increase code size. It implies the\n *    options @ref XXH_NO_INLINE_HINTS == 1, @ref XXH_FORCE_ALIGN_CHECK == 0,\n *    and @ref XXH3_NEON_LANES == 8 if they are not already defined.\n *  - `XXH_SIZE_OPT` == 2: xxHash tries to make itself as small as possible.\n *    Performance may cry. For example, the single shot functions just use the\n *    streaming API.\n */\n#  define XXH_SIZE_OPT 0\n\n/*!\n * @def XXH_FORCE_ALIGN_CHECK\n * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32()\n * and XXH64() only).\n *\n * This is an important performance trick for architectures without decent\n * unaligned memory access performance.\n *\n * It checks for input alignment, and when conditions are met, uses a \"fast\n * path\" employing direct 32-bit/64-bit reads, resulting in _dramatically\n * faster_ read speed.\n *\n * The check costs one initial branch per hash, which is generally negligible,\n * but not zero.\n *\n * Moreover, it's not useful to generate an additional code path if memory\n * access uses the same instruction for both aligned and unaligned\n * addresses (e.g. x86 and aarch64).\n *\n * In these cases, the alignment check can be removed by setting this macro to 0.\n * Then the code will always use unaligned memory access.\n * Align check is automatically disabled on x86, x64, ARM64, and some ARM chips\n * which are platforms known to offer good unaligned memory accesses performance.\n *\n * It is also disabled by default when @ref XXH_SIZE_OPT >= 1.\n *\n * This option does not affect XXH3 (only XXH32 and XXH64).\n */\n#  define XXH_FORCE_ALIGN_CHECK 0\n\n/*!\n * @def XXH_NO_INLINE_HINTS\n * @brief When non-zero, sets all functions to `static`.\n *\n * By default, xxHash tries to force the compiler to inline almost all internal\n * functions.\n *\n * This can usually improve performance due to reduced jumping and improved\n * constant folding, but significantly increases the size of the binary which\n * might not be favorable.\n *\n * Additionally, sometimes the forced inlining can be detrimental to performance,\n * depending on the architecture.\n *\n * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the\n * compiler full control on whether to inline or not.\n *\n * When not optimizing (-O0), using `-fno-inline` with GCC or Clang, or if\n * @ref XXH_SIZE_OPT >= 1, this will automatically be defined.\n */\n#  define XXH_NO_INLINE_HINTS 0\n\n/*!\n * @def XXH3_INLINE_SECRET\n * @brief Determines whether to inline the XXH3 withSecret code.\n *\n * When the secret size is known, the compiler can improve the performance\n * of XXH3_64bits_withSecret() and XXH3_128bits_withSecret().\n *\n * However, if the secret size is not known, it doesn't have any benefit. This\n * happens when xxHash is compiled into a global symbol. Therefore, if\n * @ref XXH_INLINE_ALL is *not* defined, this will be defined to 0.\n *\n * Additionally, this defaults to 0 on GCC 12+, which has an issue with function pointers\n * that are *sometimes* force inline on -Og, and it is impossible to automatically\n * detect this optimization level.\n */\n#  define XXH3_INLINE_SECRET 0\n\n/*!\n * @def XXH32_ENDJMP\n * @brief Whether to use a jump for `XXH32_finalize`.\n *\n * For performance, `XXH32_finalize` uses multiple branches in the finalizer.\n * This is generally preferable for performance,\n * but depending on exact architecture, a jmp may be preferable.\n *\n * This setting is only possibly making a difference for very small inputs.\n */\n#  define XXH32_ENDJMP 0\n\n/*!\n * @internal\n * @brief Redefines old internal names.\n *\n * For compatibility with code that uses xxHash's internals before the names\n * were changed to improve namespacing. There is no other reason to use this.\n */\n#  define XXH_OLD_NAMES\n#  undef XXH_OLD_NAMES /* don't actually use, it is ugly. */\n\n/*!\n * @def XXH_NO_STREAM\n * @brief Disables the streaming API.\n *\n * When xxHash is not inlined and the streaming functions are not used, disabling\n * the streaming functions can improve code size significantly, especially with\n * the @ref XXH3_family which tends to make constant folded copies of itself.\n */\n#  define XXH_NO_STREAM\n#  undef XXH_NO_STREAM /* don't actually */\n#endif /* XXH_DOXYGEN */\n/*!\n * @}\n */\n\n#ifndef XXH_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */\n   /* prefer __packed__ structures (method 1) for GCC\n    * < ARMv7 with unaligned access (e.g. Raspbian armhf) still uses byte shifting, so we use memcpy\n    * which for some reason does unaligned loads. */\n#  if defined(__GNUC__) && !(defined(__ARM_ARCH) && __ARM_ARCH < 7 && defined(__ARM_FEATURE_UNALIGNED))\n#    define XXH_FORCE_MEMORY_ACCESS 1\n#  endif\n#endif\n\n#ifndef XXH_SIZE_OPT\n   /* default to 1 for -Os or -Oz */\n#  if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE_SIZE__)\n#    define XXH_SIZE_OPT 1\n#  else\n#    define XXH_SIZE_OPT 0\n#  endif\n#endif\n\n#ifndef XXH_FORCE_ALIGN_CHECK  /* can be defined externally */\n   /* don't check on sizeopt, x86, aarch64, or arm when unaligned access is available */\n#  if XXH_SIZE_OPT >= 1 || \\\n      defined(__i386)  || defined(__x86_64__) || defined(__aarch64__) || defined(__ARM_FEATURE_UNALIGNED) \\\n   || defined(_M_IX86) || defined(_M_X64)     || defined(_M_ARM64)    || defined(_M_ARM) /* visual */\n#    define XXH_FORCE_ALIGN_CHECK 0\n#  else\n#    define XXH_FORCE_ALIGN_CHECK 1\n#  endif\n#endif\n\n#ifndef XXH_NO_INLINE_HINTS\n#  if XXH_SIZE_OPT >= 1 || defined(__NO_INLINE__)  /* -O0, -fno-inline */\n#    define XXH_NO_INLINE_HINTS 1\n#  else\n#    define XXH_NO_INLINE_HINTS 0\n#  endif\n#endif\n\n#ifndef XXH3_INLINE_SECRET\n#  if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12) \\\n     || !defined(XXH_INLINE_ALL)\n#    define XXH3_INLINE_SECRET 0\n#  else\n#    define XXH3_INLINE_SECRET 1\n#  endif\n#endif\n\n#ifndef XXH32_ENDJMP\n/* generally preferable for performance */\n#  define XXH32_ENDJMP 0\n#endif\n\n/*!\n * @defgroup impl Implementation\n * @{\n */\n\n\n/* *************************************\n*  Includes & Memory related functions\n***************************************/\n#if defined(XXH_NO_STREAM)\n/* nothing */\n#elif defined(XXH_NO_STDLIB)\n\n/* When requesting to disable any mention of stdlib,\n * the library loses the ability to invoked malloc / free.\n * In practice, it means that functions like `XXH*_createState()`\n * will always fail, and return NULL.\n * This flag is useful in situations where\n * xxhash.h is integrated into some kernel, embedded or limited environment\n * without access to dynamic allocation.\n */\n\nstatic XXH_CONSTF void* XXH_malloc(size_t s) { (void)s; return NULL; }\nstatic void XXH_free(void* p) { (void)p; }\n\n#else\n\n/*\n * Modify the local functions below should you wish to use\n * different memory routines for malloc() and free()\n */\n#include <stdlib.h>\n\n/*!\n * @internal\n * @brief Modify this function to use a different routine than malloc().\n */\nstatic XXH_MALLOCF void* XXH_malloc(size_t s) { return malloc(s); }\n\n/*!\n * @internal\n * @brief Modify this function to use a different routine than free().\n */\nstatic void XXH_free(void* p) { free(p); }\n\n#endif  /* XXH_NO_STDLIB */\n\n#ifndef XXH_memcpy\n/*!\n * @internal\n * @brief XXH_memcpy() macro can be redirected at compile time\n */\n#  include <string.h>\n#  define XXH_memcpy memcpy\n#endif\n\n#ifndef XXH_memset\n/*!\n * @internal\n * @brief XXH_memset() macro can be redirected at compile time\n */\n#  include <string.h>\n#  define XXH_memset memset\n#endif\n\n#ifndef XXH_memcmp\n/*!\n * @internal\n * @brief XXH_memcmp() macro can be redirected at compile time\n * Note: only needed by XXH128.\n */\n#  include <string.h>\n#  define XXH_memcmp memcmp\n#endif\n\n\n\n#include <limits.h>   /* ULLONG_MAX */\n\n\n/* *************************************\n*  Compiler Specific Options\n***************************************/\n#ifdef _MSC_VER /* Visual Studio warning fix */\n#  pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */\n#endif\n\n#if XXH_NO_INLINE_HINTS  /* disable inlining hints */\n#  if defined(__GNUC__) || defined(__clang__)\n#    define XXH_FORCE_INLINE static __attribute__((__unused__))\n#  else\n#    define XXH_FORCE_INLINE static\n#  endif\n#  define XXH_NO_INLINE static\n/* enable inlining hints */\n#elif defined(__GNUC__) || defined(__clang__)\n#  define XXH_FORCE_INLINE static __inline__ __attribute__((__always_inline__, __unused__))\n#  define XXH_NO_INLINE static __attribute__((__noinline__))\n#elif defined(_MSC_VER)  /* Visual Studio */\n#  define XXH_FORCE_INLINE static __forceinline\n#  define XXH_NO_INLINE static __declspec(noinline)\n#elif defined (__cplusplus) \\\n  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L))   /* C99 */\n#  define XXH_FORCE_INLINE static inline\n#  define XXH_NO_INLINE static\n#else\n#  define XXH_FORCE_INLINE static\n#  define XXH_NO_INLINE static\n#endif\n\n#if defined(XXH_INLINE_ALL)\n#  define XXH_STATIC XXH_FORCE_INLINE\n#else\n#  define XXH_STATIC static\n#endif\n\n#if XXH3_INLINE_SECRET\n#  define XXH3_WITH_SECRET_INLINE XXH_FORCE_INLINE\n#else\n#  define XXH3_WITH_SECRET_INLINE XXH_NO_INLINE\n#endif\n\n/* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */\n#if ((defined(sun) || defined(__sun)) && defined(__cplusplus))\n#  define XXH_RESTRICT   /* disable */\n#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* >= C99 */\n#  define XXH_RESTRICT   restrict\n#elif (defined (__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \\\n   || (defined (__clang__)) \\\n   || (defined (_MSC_VER) && (_MSC_VER >= 1400)) \\\n   || (defined (__INTEL_COMPILER) && (__INTEL_COMPILER >= 1300))\n/*\n * There are a LOT more compilers that recognize __restrict but this\n * covers the major ones.\n */\n#  define XXH_RESTRICT   __restrict\n#else\n#  define XXH_RESTRICT   /* disable */\n#endif\n\n/* *************************************\n*  Debug\n***************************************/\n/*!\n * @ingroup tuning\n * @def XXH_DEBUGLEVEL\n * @brief Sets the debugging level.\n *\n * XXH_DEBUGLEVEL is expected to be defined externally, typically via the\n * compiler's command line options. The value must be a number.\n */\n#ifndef XXH_DEBUGLEVEL\n#  ifdef DEBUGLEVEL /* backwards compat */\n#    define XXH_DEBUGLEVEL DEBUGLEVEL\n#  else\n#    define XXH_DEBUGLEVEL 0\n#  endif\n#endif\n\n#if (XXH_DEBUGLEVEL>=1)\n#  include <assert.h>   /* note: can still be disabled with NDEBUG */\n#  define XXH_ASSERT(c)   assert(c)\n#else\n#  if defined(__INTEL_COMPILER)\n#    define XXH_ASSERT(c)   XXH_ASSUME((unsigned char) (c))\n#  else\n#    define XXH_ASSERT(c)   XXH_ASSUME(c)\n#  endif\n#endif\n\n/* note: use after variable declarations */\n#ifndef XXH_STATIC_ASSERT\n#  if defined(__cplusplus) && (__cplusplus >= 201103L)            /* C++11 */\n#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0)\n#  elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)    /* C11 */\n#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { _Static_assert((c),m); } while(0)\n#  else\n#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0)\n#  endif\n#  define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c)\n#endif\n\n/*!\n * @internal\n * @def XXH_COMPILER_GUARD(var)\n * @brief Used to prevent unwanted optimizations for @p var.\n *\n * It uses an empty GCC inline assembly statement with a register constraint\n * which forces @p var into a general purpose register (eg eax, ebx, ecx\n * on x86) and marks it as modified.\n *\n * This is used in a few places to avoid unwanted autovectorization (e.g.\n * XXH32_round()). All vectorization we want is explicit via intrinsics,\n * and _usually_ isn't wanted elsewhere.\n *\n * We also use it to prevent unwanted constant folding for AArch64 in\n * XXH3_initCustomSecret_scalar().\n */\n#if defined(__GNUC__) || defined(__clang__)\n#  define XXH_COMPILER_GUARD(var) __asm__(\"\" : \"+r\" (var))\n#else\n#  define XXH_COMPILER_GUARD(var) ((void)0)\n#endif\n\n/* Specifically for NEON vectors which use the \"w\" constraint, on\n * Clang. */\n#if defined(__clang__) && defined(__ARM_ARCH) && !defined(__wasm__)\n#  define XXH_COMPILER_GUARD_CLANG_NEON(var) __asm__(\"\" : \"+w\" (var))\n#else\n#  define XXH_COMPILER_GUARD_CLANG_NEON(var) ((void)0)\n#endif\n\n/* *************************************\n*  Basic Types\n***************************************/\n#if !defined (__VMS) \\\n && (defined (__cplusplus) \\\n || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )\n#   ifdef _AIX\n#     include <inttypes.h>\n#   else\n#     include <stdint.h>\n#   endif\n    typedef uint8_t xxh_u8;\n#else\n    typedef unsigned char xxh_u8;\n#endif\ntypedef XXH32_hash_t xxh_u32;\n\n#ifdef XXH_OLD_NAMES\n#  warning \"XXH_OLD_NAMES is planned to be removed starting v0.9. If the program depends on it, consider moving away from it by employing newer type names directly\"\n#  define BYTE xxh_u8\n#  define U8   xxh_u8\n#  define U32  xxh_u32\n#endif\n\n/* ***   Memory access   *** */\n\n/*!\n * @internal\n * @fn xxh_u32 XXH_read32(const void* ptr)\n * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness.\n *\n * Affected by @ref XXH_FORCE_MEMORY_ACCESS.\n *\n * @param ptr The pointer to read from.\n * @return The 32-bit native endian integer from the bytes at @p ptr.\n */\n\n/*!\n * @internal\n * @fn xxh_u32 XXH_readLE32(const void* ptr)\n * @brief Reads an unaligned 32-bit little endian integer from @p ptr.\n *\n * Affected by @ref XXH_FORCE_MEMORY_ACCESS.\n *\n * @param ptr The pointer to read from.\n * @return The 32-bit little endian integer from the bytes at @p ptr.\n */\n\n/*!\n * @internal\n * @fn xxh_u32 XXH_readBE32(const void* ptr)\n * @brief Reads an unaligned 32-bit big endian integer from @p ptr.\n *\n * Affected by @ref XXH_FORCE_MEMORY_ACCESS.\n *\n * @param ptr The pointer to read from.\n * @return The 32-bit big endian integer from the bytes at @p ptr.\n */\n\n/*!\n * @internal\n * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align)\n * @brief Like @ref XXH_readLE32(), but has an option for aligned reads.\n *\n * Affected by @ref XXH_FORCE_MEMORY_ACCESS.\n * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is\n * always @ref XXH_alignment::XXH_unaligned.\n *\n * @param ptr The pointer to read from.\n * @param align Whether @p ptr is aligned.\n * @pre\n *   If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte\n *   aligned.\n * @return The 32-bit little endian integer from the bytes at @p ptr.\n */\n\n#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))\n/*\n * Manual byteshift. Best for old compilers which don't inline memcpy.\n * We actually directly use XXH_readLE32 and XXH_readBE32.\n */\n#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))\n\n/*\n * Force direct memory access. Only works on CPU which support unaligned memory\n * access in hardware.\n */\nstatic xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; }\n\n#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))\n\n/*\n * __attribute__((aligned(1))) is supported by gcc and clang. Originally the\n * documentation claimed that it only increased the alignment, but actually it\n * can decrease it on gcc, clang, and icc:\n * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502,\n * https://gcc.godbolt.org/z/xYez1j67Y.\n */\n#ifdef XXH_OLD_NAMES\ntypedef union { xxh_u32 u32; } __attribute__((__packed__)) unalign;\n#endif\nstatic xxh_u32 XXH_read32(const void* ptr)\n{\n    typedef __attribute__((__aligned__(1))) __attribute__((__may_alias__)) xxh_u32 xxh_unalign32;\n    return *((const xxh_unalign32*)ptr);\n}\n\n#else\n\n/*\n * Portable and safe solution. Generally efficient.\n * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html\n */\nstatic xxh_u32 XXH_read32(const void* memPtr)\n{\n    xxh_u32 val;\n    XXH_memcpy(&val, memPtr, sizeof(val));\n    return val;\n}\n\n#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */\n\n\n/* ***   Endianness   *** */\n\n/*!\n * @ingroup tuning\n * @def XXH_CPU_LITTLE_ENDIAN\n * @brief Whether the target is little endian.\n *\n * Defined to 1 if the target is little endian, or 0 if it is big endian.\n * It can be defined externally, for example on the compiler command line.\n *\n * If it is not defined,\n * a runtime check (which is usually constant folded) is used instead.\n *\n * @note\n *   This is not necessarily defined to an integer constant.\n *\n * @see XXH_isLittleEndian() for the runtime check.\n */\n#ifndef XXH_CPU_LITTLE_ENDIAN\n/*\n * Try to detect endianness automatically, to avoid the nonstandard behavior\n * in `XXH_isLittleEndian()`\n */\n#  if defined(_WIN32) /* Windows is always little endian */ \\\n     || defined(__LITTLE_ENDIAN__) \\\n     || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)\n#    define XXH_CPU_LITTLE_ENDIAN 1\n#  elif defined(__BIG_ENDIAN__) \\\n     || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)\n#    define XXH_CPU_LITTLE_ENDIAN 0\n#  else\n/*!\n * @internal\n * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN.\n *\n * Most compilers will constant fold this.\n */\nstatic int XXH_isLittleEndian(void)\n{\n    /*\n     * Portable and well-defined behavior.\n     * Don't use static: it is detrimental to performance.\n     */\n    const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 };\n    return one.c[0];\n}\n#   define XXH_CPU_LITTLE_ENDIAN   XXH_isLittleEndian()\n#  endif\n#endif\n\n\n\n\n/* ****************************************\n*  Compiler-specific Functions and Macros\n******************************************/\n#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)\n\n#ifdef __has_builtin\n#  define XXH_HAS_BUILTIN(x) __has_builtin(x)\n#else\n#  define XXH_HAS_BUILTIN(x) 0\n#endif\n\n\n\n/*\n * C23 and future versions have standard \"unreachable()\".\n * Once it has been implemented reliably we can add it as an\n * additional case:\n *\n * ```\n * #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L)\n * #  include <stddef.h>\n * #  ifdef unreachable\n * #    define XXH_UNREACHABLE() unreachable()\n * #  endif\n * #endif\n * ```\n *\n * Note C++23 also has std::unreachable() which can be detected\n * as follows:\n * ```\n * #if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L)\n * #  include <utility>\n * #  define XXH_UNREACHABLE() std::unreachable()\n * #endif\n * ```\n * NB: `__cpp_lib_unreachable` is defined in the `<version>` header.\n * We don't use that as including `<utility>` in `extern \"C\"` blocks\n * doesn't work on GCC12\n */\n\n#if XXH_HAS_BUILTIN(__builtin_unreachable)\n#  define XXH_UNREACHABLE() __builtin_unreachable()\n\n#elif defined(_MSC_VER)\n#  define XXH_UNREACHABLE() __assume(0)\n\n#else\n#  define XXH_UNREACHABLE()\n#endif\n\n#if XXH_HAS_BUILTIN(__builtin_assume)\n#  define XXH_ASSUME(c) __builtin_assume(c)\n#else\n#  define XXH_ASSUME(c) if (!(c)) { XXH_UNREACHABLE(); }\n#endif\n\n/*!\n * @internal\n * @def XXH_rotl32(x,r)\n * @brief 32-bit rotate left.\n *\n * @param x The 32-bit integer to be rotated.\n * @param r The number of bits to rotate.\n * @pre\n *   @p r > 0 && @p r < 32\n * @note\n *   @p x and @p r may be evaluated multiple times.\n * @return The rotated result.\n */\n#if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \\\n                               && XXH_HAS_BUILTIN(__builtin_rotateleft64)\n#  define XXH_rotl32 __builtin_rotateleft32\n#  define XXH_rotl64 __builtin_rotateleft64\n#elif XXH_HAS_BUILTIN(__builtin_stdc_rotate_left)\n#  define XXH_rotl32 __builtin_stdc_rotate_left\n#  define XXH_rotl64 __builtin_stdc_rotate_left\n/* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */\n#elif defined(_MSC_VER)\n#  define XXH_rotl32(x,r) _rotl(x,r)\n#  define XXH_rotl64(x,r) _rotl64(x,r)\n#else\n#  define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))\n#  define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r))))\n#endif\n\n/*!\n * @internal\n * @fn xxh_u32 XXH_swap32(xxh_u32 x)\n * @brief A 32-bit byteswap.\n *\n * @param x The 32-bit integer to byteswap.\n * @return @p x, byteswapped.\n */\n#if defined(_MSC_VER)     /* Visual Studio */\n#  define XXH_swap32 _byteswap_ulong\n#elif XXH_GCC_VERSION >= 403\n#  define XXH_swap32 __builtin_bswap32\n#else\nstatic xxh_u32 XXH_swap32 (xxh_u32 x)\n{\n    return  ((x << 24) & 0xff000000 ) |\n            ((x <<  8) & 0x00ff0000 ) |\n            ((x >>  8) & 0x0000ff00 ) |\n            ((x >> 24) & 0x000000ff );\n}\n#endif\n\n\n/* ***************************\n*  Memory reads\n*****************************/\n\n/*!\n * @internal\n * @brief Enum to indicate whether a pointer is aligned.\n */\ntypedef enum {\n    XXH_aligned,  /*!< Aligned */\n    XXH_unaligned /*!< Possibly unaligned */\n} XXH_alignment;\n\n/*\n * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load.\n *\n * This is ideal for older compilers which don't inline memcpy.\n */\n#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))\n\nXXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr)\n{\n    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;\n    return bytePtr[0]\n         | ((xxh_u32)bytePtr[1] << 8)\n         | ((xxh_u32)bytePtr[2] << 16)\n         | ((xxh_u32)bytePtr[3] << 24);\n}\n\nXXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr)\n{\n    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;\n    return bytePtr[3]\n         | ((xxh_u32)bytePtr[2] << 8)\n         | ((xxh_u32)bytePtr[1] << 16)\n         | ((xxh_u32)bytePtr[0] << 24);\n}\n\n#else\nXXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr)\n{\n    return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));\n}\n\nstatic xxh_u32 XXH_readBE32(const void* ptr)\n{\n    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);\n}\n#endif\n\nXXH_FORCE_INLINE xxh_u32\nXXH_readLE32_align(const void* ptr, XXH_alignment align)\n{\n    if (align==XXH_unaligned) {\n        return XXH_readLE32(ptr);\n    } else {\n        return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr);\n    }\n}\n\n\n/* *************************************\n*  Misc\n***************************************/\n/*! @ingroup public */\nXXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }\n\n\n/* *******************************************************************\n*  32-bit hash functions\n*********************************************************************/\n/*!\n * @}\n * @defgroup XXH32_impl XXH32 implementation\n * @ingroup impl\n *\n * Details on the XXH32 implementation.\n * @{\n */\n /* #define instead of static const, to be used as initializers */\n#define XXH_PRIME32_1  0x9E3779B1U  /*!< 0b10011110001101110111100110110001 */\n#define XXH_PRIME32_2  0x85EBCA77U  /*!< 0b10000101111010111100101001110111 */\n#define XXH_PRIME32_3  0xC2B2AE3DU  /*!< 0b11000010101100101010111000111101 */\n#define XXH_PRIME32_4  0x27D4EB2FU  /*!< 0b00100111110101001110101100101111 */\n#define XXH_PRIME32_5  0x165667B1U  /*!< 0b00010110010101100110011110110001 */\n\n#ifdef XXH_OLD_NAMES\n#  define PRIME32_1 XXH_PRIME32_1\n#  define PRIME32_2 XXH_PRIME32_2\n#  define PRIME32_3 XXH_PRIME32_3\n#  define PRIME32_4 XXH_PRIME32_4\n#  define PRIME32_5 XXH_PRIME32_5\n#endif\n\n/*!\n * @internal\n * @brief Normal stripe processing routine.\n *\n * This shuffles the bits so that any bit from @p input impacts several bits in\n * @p acc.\n *\n * @param acc The accumulator lane.\n * @param input The stripe of input to mix.\n * @return The mixed accumulator lane.\n */\nstatic xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input)\n{\n    acc += input * XXH_PRIME32_2;\n    acc  = XXH_rotl32(acc, 13);\n    acc *= XXH_PRIME32_1;\n#if (defined(__SSE4_1__) || defined(__aarch64__) || defined(__wasm_simd128__)) && !defined(XXH_ENABLE_AUTOVECTORIZE)\n    /*\n     * UGLY HACK:\n     * A compiler fence is used to prevent GCC and Clang from\n     * autovectorizing the XXH32 loop (pragmas and attributes don't work for some\n     * reason) without globally disabling SSE4.1.\n     *\n     * The reason we want to avoid vectorization is because despite working on\n     * 4 integers at a time, there are multiple factors slowing XXH32 down on\n     * SSE4:\n     * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on\n     *   newer chips!) making it slightly slower to multiply four integers at\n     *   once compared to four integers independently. Even when pmulld was\n     *   fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE\n     *   just to multiply unless doing a long operation.\n     *\n     * - Four instructions are required to rotate,\n     *      movqda tmp,  v // not required with VEX encoding\n     *      pslld  tmp, 13 // tmp <<= 13\n     *      psrld  v,   19 // x >>= 19\n     *      por    v,  tmp // x |= tmp\n     *   compared to one for scalar:\n     *      roll   v, 13    // reliably fast across the board\n     *      shldl  v, v, 13 // Sandy Bridge and later prefer this for some reason\n     *\n     * - Instruction level parallelism is actually more beneficial here because\n     *   the SIMD actually serializes this operation: While v1 is rotating, v2\n     *   can load data, while v3 can multiply. SSE forces them to operate\n     *   together.\n     *\n     * This is also enabled on AArch64, as Clang is *very aggressive* in vectorizing\n     * the loop. NEON is only faster on the A53, and with the newer cores, it is less\n     * than half the speed.\n     *\n     * Additionally, this is used on WASM SIMD128 because it JITs to the same\n     * SIMD instructions and has the same issue.\n     */\n    XXH_COMPILER_GUARD(acc);\n#endif\n    return acc;\n}\n\n/*!\n * @internal\n * @brief Mixes all bits to finalize the hash.\n *\n * The final mix ensures that all input bits have a chance to impact any bit in\n * the output digest, resulting in an unbiased distribution.\n *\n * @param hash The hash to avalanche.\n * @return The avalanched hash.\n */\nstatic xxh_u32 XXH32_avalanche(xxh_u32 hash)\n{\n    hash ^= hash >> 15;\n    hash *= XXH_PRIME32_2;\n    hash ^= hash >> 13;\n    hash *= XXH_PRIME32_3;\n    hash ^= hash >> 16;\n    return hash;\n}\n\n#define XXH_get32bits(p) XXH_readLE32_align(p, align)\n\n/*!\n * @internal\n * @brief Sets up the initial accumulator state for XXH32().\n */\nXXH_FORCE_INLINE void\nXXH32_initAccs(xxh_u32 *acc, xxh_u32 seed)\n{\n    XXH_ASSERT(acc != NULL);\n    acc[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2;\n    acc[1] = seed + XXH_PRIME32_2;\n    acc[2] = seed + 0;\n    acc[3] = seed - XXH_PRIME32_1;\n}\n\n/*!\n * @internal\n * @brief Consumes a block of data for XXH32().\n *\n * @return the end input pointer.\n */\nXXH_FORCE_INLINE const xxh_u8 *\nXXH32_consumeLong(\n    xxh_u32 *XXH_RESTRICT acc,\n    xxh_u8 const *XXH_RESTRICT input,\n    size_t len,\n    XXH_alignment align\n)\n{\n    const xxh_u8* const bEnd = input + len;\n    const xxh_u8* const limit = bEnd - 15;\n    XXH_ASSERT(acc != NULL);\n    XXH_ASSERT(input != NULL);\n    XXH_ASSERT(len >= 16);\n    do {\n        acc[0] = XXH32_round(acc[0], XXH_get32bits(input)); input += 4;\n        acc[1] = XXH32_round(acc[1], XXH_get32bits(input)); input += 4;\n        acc[2] = XXH32_round(acc[2], XXH_get32bits(input)); input += 4;\n        acc[3] = XXH32_round(acc[3], XXH_get32bits(input)); input += 4;\n    } while (input < limit);\n\n    return input;\n}\n\n/*!\n * @internal\n * @brief Merges the accumulator lanes together for XXH32()\n */\nXXH_FORCE_INLINE XXH_PUREF xxh_u32\nXXH32_mergeAccs(const xxh_u32 *acc)\n{\n    XXH_ASSERT(acc != NULL);\n    return XXH_rotl32(acc[0], 1)  + XXH_rotl32(acc[1], 7)\n         + XXH_rotl32(acc[2], 12) + XXH_rotl32(acc[3], 18);\n}\n\n/*!\n * @internal\n * @brief Processes the last 0-15 bytes of @p ptr.\n *\n * There may be up to 15 bytes remaining to consume from the input.\n * This final stage will digest them to ensure that all input bytes are present\n * in the final mix.\n *\n * @param hash The hash to finalize.\n * @param ptr The pointer to the remaining input.\n * @param len The remaining length, modulo 16.\n * @param align Whether @p ptr is aligned.\n * @return The finalized hash.\n * @see XXH64_finalize().\n */\nstatic XXH_PUREF xxh_u32\nXXH32_finalize(xxh_u32 hash, const xxh_u8* ptr, size_t len, XXH_alignment align)\n{\n#define XXH_PROCESS1 do {                             \\\n    hash += (*ptr++) * XXH_PRIME32_5;                 \\\n    hash = XXH_rotl32(hash, 11) * XXH_PRIME32_1;      \\\n} while (0)\n\n#define XXH_PROCESS4 do {                             \\\n    hash += XXH_get32bits(ptr) * XXH_PRIME32_3;       \\\n    ptr += 4;                                         \\\n    hash  = XXH_rotl32(hash, 17) * XXH_PRIME32_4;     \\\n} while (0)\n\n    if (ptr==NULL) XXH_ASSERT(len == 0);\n\n    /* Compact rerolled version; generally faster */\n    if (!XXH32_ENDJMP) {\n        len &= 15;\n        while (len >= 4) {\n            XXH_PROCESS4;\n            len -= 4;\n        }\n        while (len > 0) {\n            XXH_PROCESS1;\n            --len;\n        }\n        return XXH32_avalanche(hash);\n    } else {\n         switch(len&15) /* or switch(bEnd - p) */ {\n           case 12:      XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 8:       XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 4:       XXH_PROCESS4;\n                         return XXH32_avalanche(hash);\n\n           case 13:      XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 9:       XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 5:       XXH_PROCESS4;\n                         XXH_PROCESS1;\n                         return XXH32_avalanche(hash);\n\n           case 14:      XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 10:      XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 6:       XXH_PROCESS4;\n                         XXH_PROCESS1;\n                         XXH_PROCESS1;\n                         return XXH32_avalanche(hash);\n\n           case 15:      XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 11:      XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 7:       XXH_PROCESS4;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 3:       XXH_PROCESS1;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 2:       XXH_PROCESS1;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 1:       XXH_PROCESS1;\n                         XXH_FALLTHROUGH;  /* fallthrough */\n           case 0:       return XXH32_avalanche(hash);\n        }\n        XXH_ASSERT(0);\n        return hash;   /* reaching this point is deemed impossible */\n    }\n}\n\n#ifdef XXH_OLD_NAMES\n#  define PROCESS1 XXH_PROCESS1\n#  define PROCESS4 XXH_PROCESS4\n#else\n#  undef XXH_PROCESS1\n#  undef XXH_PROCESS4\n#endif\n\n/*!\n * @internal\n * @brief The implementation for @ref XXH32().\n *\n * @param input , len , seed Directly passed from @ref XXH32().\n * @param align Whether @p input is aligned.\n * @return The calculated hash.\n */\nXXH_FORCE_INLINE XXH_PUREF xxh_u32\nXXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align)\n{\n    xxh_u32 h32;\n\n    if (input==NULL) XXH_ASSERT(len == 0);\n\n    if (len>=16) {\n        xxh_u32 acc[4];\n        XXH32_initAccs(acc, seed);\n\n        input = XXH32_consumeLong(acc, input, len, align);\n\n        h32 = XXH32_mergeAccs(acc);\n    } else {\n        h32  = seed + XXH_PRIME32_5;\n    }\n\n    h32 += (xxh_u32)len;\n\n    return XXH32_finalize(h32, input, len&15, align);\n}\n\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed)\n{\n#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2\n    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */\n    XXH32_state_t state;\n    XXH32_reset(&state, seed);\n    XXH32_update(&state, (const xxh_u8*)input, len);\n    return XXH32_digest(&state);\n#else\n    if (XXH_FORCE_ALIGN_CHECK) {\n        if ((((size_t)input) & 3) == 0) {   /* Input is 4-bytes aligned, leverage the speed benefit */\n            return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned);\n    }   }\n\n    return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned);\n#endif\n}\n\n\n\n/*******   Hash streaming   *******/\n#ifndef XXH_NO_STREAM\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)\n{\n    return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));\n}\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)\n{\n    XXH_free(statePtr);\n    return XXH_OK;\n}\n\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)\n{\n    XXH_memcpy(dstState, srcState, sizeof(*dstState));\n}\n\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed)\n{\n    XXH_ASSERT(statePtr != NULL);\n    XXH_memset(statePtr, 0, sizeof(*statePtr));\n    XXH32_initAccs(statePtr->acc, seed);\n    return XXH_OK;\n}\n\n\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH32_update(XXH32_state_t* state, const void* input, size_t len)\n{\n    if (input==NULL) {\n        XXH_ASSERT(len == 0);\n        return XXH_OK;\n    }\n\n    state->total_len_32 += (XXH32_hash_t)len;\n    state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16));\n\n    XXH_ASSERT(state->bufferedSize < sizeof(state->buffer));\n    if (len < sizeof(state->buffer) - state->bufferedSize)  {   /* fill in tmp buffer */\n        XXH_memcpy(state->buffer + state->bufferedSize, input, len);\n        state->bufferedSize += (XXH32_hash_t)len;\n        return XXH_OK;\n    }\n\n    {   const xxh_u8* xinput = (const xxh_u8*)input;\n        const xxh_u8* const bEnd = xinput + len;\n\n        if (state->bufferedSize) {   /* non-empty buffer: complete first */\n            XXH_memcpy(state->buffer + state->bufferedSize, xinput, sizeof(state->buffer) - state->bufferedSize);\n            xinput += sizeof(state->buffer) - state->bufferedSize;\n            /* then process one round */\n            (void)XXH32_consumeLong(state->acc, state->buffer, sizeof(state->buffer), XXH_aligned);\n            state->bufferedSize = 0;\n        }\n\n        XXH_ASSERT(xinput <= bEnd);\n        if ((size_t)(bEnd - xinput) >= sizeof(state->buffer)) {\n            /* Process the remaining data */\n            xinput = XXH32_consumeLong(state->acc, xinput, (size_t)(bEnd - xinput), XXH_unaligned);\n        }\n\n        if (xinput < bEnd) {\n            /* Copy the leftover to the tmp buffer */\n            XXH_memcpy(state->buffer, xinput, (size_t)(bEnd-xinput));\n            state->bufferedSize = (unsigned)(bEnd-xinput);\n        }\n    }\n\n    return XXH_OK;\n}\n\n\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state)\n{\n    xxh_u32 h32;\n\n    if (state->large_len) {\n        h32 = XXH32_mergeAccs(state->acc);\n    } else {\n        h32 = state->acc[2] /* == seed */ + XXH_PRIME32_5;\n    }\n\n    h32 += state->total_len_32;\n\n    return XXH32_finalize(h32, state->buffer, state->bufferedSize, XXH_aligned);\n}\n#endif /* !XXH_NO_STREAM */\n\n/*******   Canonical representation   *******/\n\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)\n{\n    XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));\n    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);\n    XXH_memcpy(dst, &hash, sizeof(*dst));\n}\n/*! @ingroup XXH32_family */\nXXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)\n{\n    return XXH_readBE32(src);\n}\n\n\n#ifndef XXH_NO_LONG_LONG\n\n/* *******************************************************************\n*  64-bit hash functions\n*********************************************************************/\n/*!\n * @}\n * @ingroup impl\n * @{\n */\n/*******   Memory access   *******/\n\ntypedef XXH64_hash_t xxh_u64;\n\n#ifdef XXH_OLD_NAMES\n#  define U64 xxh_u64\n#endif\n\n#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))\n/*\n * Manual byteshift. Best for old compilers which don't inline memcpy.\n * We actually directly use XXH_readLE64 and XXH_readBE64.\n */\n#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))\n\n/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */\nstatic xxh_u64 XXH_read64(const void* memPtr)\n{\n    return *(const xxh_u64*) memPtr;\n}\n\n#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))\n\n/*\n * __attribute__((aligned(1))) is supported by gcc and clang. Originally the\n * documentation claimed that it only increased the alignment, but actually it\n * can decrease it on gcc, clang, and icc:\n * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502,\n * https://gcc.godbolt.org/z/xYez1j67Y.\n */\n#ifdef XXH_OLD_NAMES\ntypedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((__packed__)) unalign64;\n#endif\nstatic xxh_u64 XXH_read64(const void* ptr)\n{\n    typedef __attribute__((__aligned__(1))) __attribute__((__may_alias__)) xxh_u64 xxh_unalign64;\n    return *((const xxh_unalign64*)ptr);\n}\n\n#else\n\n/*\n * Portable and safe solution. Generally efficient.\n * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html\n */\nstatic xxh_u64 XXH_read64(const void* memPtr)\n{\n    xxh_u64 val;\n    XXH_memcpy(&val, memPtr, sizeof(val));\n    return val;\n}\n\n#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */\n\n#if defined(_MSC_VER)     /* Visual Studio */\n#  define XXH_swap64 _byteswap_uint64\n#elif XXH_GCC_VERSION >= 403\n#  define XXH_swap64 __builtin_bswap64\n#else\nstatic xxh_u64 XXH_swap64(xxh_u64 x)\n{\n    return  ((x << 56) & 0xff00000000000000ULL) |\n            ((x << 40) & 0x00ff000000000000ULL) |\n            ((x << 24) & 0x0000ff0000000000ULL) |\n            ((x << 8)  & 0x000000ff00000000ULL) |\n            ((x >> 8)  & 0x00000000ff000000ULL) |\n            ((x >> 24) & 0x0000000000ff0000ULL) |\n            ((x >> 40) & 0x000000000000ff00ULL) |\n            ((x >> 56) & 0x00000000000000ffULL);\n}\n#endif\n\n\n/* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */\n#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))\n\nXXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr)\n{\n    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;\n    return bytePtr[0]\n         | ((xxh_u64)bytePtr[1] << 8)\n         | ((xxh_u64)bytePtr[2] << 16)\n         | ((xxh_u64)bytePtr[3] << 24)\n         | ((xxh_u64)bytePtr[4] << 32)\n         | ((xxh_u64)bytePtr[5] << 40)\n         | ((xxh_u64)bytePtr[6] << 48)\n         | ((xxh_u64)bytePtr[7] << 56);\n}\n\nXXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr)\n{\n    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;\n    return bytePtr[7]\n         | ((xxh_u64)bytePtr[6] << 8)\n         | ((xxh_u64)bytePtr[5] << 16)\n         | ((xxh_u64)bytePtr[4] << 24)\n         | ((xxh_u64)bytePtr[3] << 32)\n         | ((xxh_u64)bytePtr[2] << 40)\n         | ((xxh_u64)bytePtr[1] << 48)\n         | ((xxh_u64)bytePtr[0] << 56);\n}\n\n#else\nXXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr)\n{\n    return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));\n}\n\nstatic xxh_u64 XXH_readBE64(const void* ptr)\n{\n    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);\n}\n#endif\n\nXXH_FORCE_INLINE xxh_u64\nXXH_readLE64_align(const void* ptr, XXH_alignment align)\n{\n    if (align==XXH_unaligned)\n        return XXH_readLE64(ptr);\n    else\n        return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr);\n}\n\n\n/*******   xxh64   *******/\n/*!\n * @}\n * @defgroup XXH64_impl XXH64 implementation\n * @ingroup impl\n *\n * Details on the XXH64 implementation.\n * @{\n */\n/* #define rather that static const, to be used as initializers */\n#define XXH_PRIME64_1  0x9E3779B185EBCA87ULL  /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */\n#define XXH_PRIME64_2  0xC2B2AE3D27D4EB4FULL  /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */\n#define XXH_PRIME64_3  0x165667B19E3779F9ULL  /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */\n#define XXH_PRIME64_4  0x85EBCA77C2B2AE63ULL  /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */\n#define XXH_PRIME64_5  0x27D4EB2F165667C5ULL  /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */\n\n#ifdef XXH_OLD_NAMES\n#  define PRIME64_1 XXH_PRIME64_1\n#  define PRIME64_2 XXH_PRIME64_2\n#  define PRIME64_3 XXH_PRIME64_3\n#  define PRIME64_4 XXH_PRIME64_4\n#  define PRIME64_5 XXH_PRIME64_5\n#endif\n\n/*! @copydoc XXH32_round */\nstatic xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input)\n{\n    acc += input * XXH_PRIME64_2;\n    acc  = XXH_rotl64(acc, 31);\n    acc *= XXH_PRIME64_1;\n#if (defined(__AVX512F__)) && !defined(XXH_ENABLE_AUTOVECTORIZE)\n    /*\n     * DISABLE AUTOVECTORIZATION:\n     * A compiler fence is used to prevent GCC and Clang from\n     * autovectorizing the XXH64 loop (pragmas and attributes don't work for some\n     * reason) without globally disabling AVX512.\n     *\n     * Autovectorization of XXH64 tends to be detrimental,\n     * though the exact outcome may change depending on exact cpu and compiler version.\n     * For information, it has been reported as detrimental for Skylake-X,\n     * but possibly beneficial for Zen4.\n     *\n     * The default is to disable auto-vectorization,\n     * but you can select to enable it instead using `XXH_ENABLE_AUTOVECTORIZE` build variable.\n     */\n    XXH_COMPILER_GUARD(acc);\n#endif\n    return acc;\n}\n\nstatic xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val)\n{\n    val  = XXH64_round(0, val);\n    acc ^= val;\n    acc  = acc * XXH_PRIME64_1 + XXH_PRIME64_4;\n    return acc;\n}\n\n/*! @copydoc XXH32_avalanche */\nstatic xxh_u64 XXH64_avalanche(xxh_u64 hash)\n{\n    hash ^= hash >> 33;\n    hash *= XXH_PRIME64_2;\n    hash ^= hash >> 29;\n    hash *= XXH_PRIME64_3;\n    hash ^= hash >> 32;\n    return hash;\n}\n\n\n#define XXH_get64bits(p) XXH_readLE64_align(p, align)\n\n/*!\n * @internal\n * @brief Sets up the initial accumulator state for XXH64().\n */\nXXH_FORCE_INLINE void\nXXH64_initAccs(xxh_u64 *acc, xxh_u64 seed)\n{\n    XXH_ASSERT(acc != NULL);\n    acc[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2;\n    acc[1] = seed + XXH_PRIME64_2;\n    acc[2] = seed + 0;\n    acc[3] = seed - XXH_PRIME64_1;\n}\n\n/*!\n * @internal\n * @brief Consumes a block of data for XXH64().\n *\n * @return the end input pointer.\n */\nXXH_FORCE_INLINE const xxh_u8 *\nXXH64_consumeLong(\n    xxh_u64 *XXH_RESTRICT acc,\n    xxh_u8 const *XXH_RESTRICT input,\n    size_t len,\n    XXH_alignment align\n)\n{\n    const xxh_u8* const bEnd = input + len;\n    const xxh_u8* const limit = bEnd - 31;\n    XXH_ASSERT(acc != NULL);\n    XXH_ASSERT(input != NULL);\n    XXH_ASSERT(len >= 32);\n    do {\n        /* reroll on 32-bit */\n        if (sizeof(void *) < sizeof(xxh_u64)) {\n            size_t i;\n            for (i = 0; i < 4; i++) {\n                acc[i] = XXH64_round(acc[i], XXH_get64bits(input));\n                input += 8;\n            }\n        } else {\n            acc[0] = XXH64_round(acc[0], XXH_get64bits(input)); input += 8;\n            acc[1] = XXH64_round(acc[1], XXH_get64bits(input)); input += 8;\n            acc[2] = XXH64_round(acc[2], XXH_get64bits(input)); input += 8;\n            acc[3] = XXH64_round(acc[3], XXH_get64bits(input)); input += 8;\n        }\n    } while (input < limit);\n\n    return input;\n}\n\n/*!\n * @internal\n * @brief Merges the accumulator lanes together for XXH64()\n */\nXXH_FORCE_INLINE XXH_PUREF xxh_u64\nXXH64_mergeAccs(const xxh_u64 *acc)\n{\n    XXH_ASSERT(acc != NULL);\n    {\n        xxh_u64 h64 = XXH_rotl64(acc[0], 1) + XXH_rotl64(acc[1], 7)\n                    + XXH_rotl64(acc[2], 12) + XXH_rotl64(acc[3], 18);\n        /* reroll on 32-bit */\n        if (sizeof(void *) < sizeof(xxh_u64)) {\n            size_t i;\n            for (i = 0; i < 4; i++) {\n                h64 = XXH64_mergeRound(h64, acc[i]);\n            }\n        } else {\n            h64 = XXH64_mergeRound(h64, acc[0]);\n            h64 = XXH64_mergeRound(h64, acc[1]);\n            h64 = XXH64_mergeRound(h64, acc[2]);\n            h64 = XXH64_mergeRound(h64, acc[3]);\n        }\n        return h64;\n    }\n}\n\n/*!\n * @internal\n * @brief Processes the last 0-31 bytes of @p ptr.\n *\n * There may be up to 31 bytes remaining to consume from the input.\n * This final stage will digest them to ensure that all input bytes are present\n * in the final mix.\n *\n * @param hash The hash to finalize.\n * @param ptr The pointer to the remaining input.\n * @param len The remaining length, modulo 32.\n * @param align Whether @p ptr is aligned.\n * @return The finalized hash\n * @see XXH32_finalize().\n */\nXXH_STATIC XXH_PUREF xxh_u64\nXXH64_finalize(xxh_u64 hash, const xxh_u8* ptr, size_t len, XXH_alignment align)\n{\n    if (ptr==NULL) XXH_ASSERT(len == 0);\n    len &= 31;\n    while (len >= 8) {\n        xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr));\n        ptr += 8;\n        hash ^= k1;\n        hash  = XXH_rotl64(hash,27) * XXH_PRIME64_1 + XXH_PRIME64_4;\n        len -= 8;\n    }\n    if (len >= 4) {\n        hash ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1;\n        ptr += 4;\n        hash = XXH_rotl64(hash, 23) * XXH_PRIME64_2 + XXH_PRIME64_3;\n        len -= 4;\n    }\n    while (len > 0) {\n        hash ^= (*ptr++) * XXH_PRIME64_5;\n        hash = XXH_rotl64(hash, 11) * XXH_PRIME64_1;\n        --len;\n    }\n    return  XXH64_avalanche(hash);\n}\n\n#ifdef XXH_OLD_NAMES\n#  define PROCESS1_64 XXH_PROCESS1_64\n#  define PROCESS4_64 XXH_PROCESS4_64\n#  define PROCESS8_64 XXH_PROCESS8_64\n#else\n#  undef XXH_PROCESS1_64\n#  undef XXH_PROCESS4_64\n#  undef XXH_PROCESS8_64\n#endif\n\n/*!\n * @internal\n * @brief The implementation for @ref XXH64().\n *\n * @param input , len , seed Directly passed from @ref XXH64().\n * @param align Whether @p input is aligned.\n * @return The calculated hash.\n */\nXXH_FORCE_INLINE XXH_PUREF xxh_u64\nXXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align)\n{\n    xxh_u64 h64;\n    if (input==NULL) XXH_ASSERT(len == 0);\n\n    if (len>=32) {  /* Process a large block of data */\n        xxh_u64 acc[4];\n        XXH64_initAccs(acc, seed);\n\n        input = XXH64_consumeLong(acc, input, len, align);\n\n        h64 = XXH64_mergeAccs(acc);\n    } else {\n        h64  = seed + XXH_PRIME64_5;\n    }\n\n    h64 += (xxh_u64) len;\n\n    return XXH64_finalize(h64, input, len, align);\n}\n\n\n/*! @ingroup XXH64_family */\nXXH_PUBLIC_API XXH64_hash_t XXH64 (XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed)\n{\n#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2\n    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */\n    XXH64_state_t state;\n    XXH64_reset(&state, seed);\n    XXH64_update(&state, (const xxh_u8*)input, len);\n    return XXH64_digest(&state);\n#else\n    if (XXH_FORCE_ALIGN_CHECK) {\n        if ((((size_t)input) & 7)==0) {  /* Input is aligned, let's leverage the speed advantage */\n            return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned);\n    }   }\n\n    return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned);\n\n#endif\n}\n\n/*******   Hash Streaming   *******/\n#ifndef XXH_NO_STREAM\n/*! @ingroup XXH64_family*/\nXXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)\n{\n    return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));\n}\n/*! @ingroup XXH64_family */\nXXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)\n{\n    XXH_free(statePtr);\n    return XXH_OK;\n}\n\n/*! @ingroup XXH64_family */\nXXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dstState, const XXH64_state_t* srcState)\n{\n    XXH_memcpy(dstState, srcState, sizeof(*dstState));\n}\n\n/*! @ingroup XXH64_family */\nXXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed)\n{\n    XXH_ASSERT(statePtr != NULL);\n    XXH_memset(statePtr, 0, sizeof(*statePtr));\n    XXH64_initAccs(statePtr->acc, seed);\n    return XXH_OK;\n}\n\n/*! @ingroup XXH64_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH64_update (XXH_NOESCAPE XXH64_state_t* state, XXH_NOESCAPE const void* input, size_t len)\n{\n    if (input==NULL) {\n        XXH_ASSERT(len == 0);\n        return XXH_OK;\n    }\n\n    state->total_len += len;\n\n    XXH_ASSERT(state->bufferedSize <= sizeof(state->buffer));\n    if (len < sizeof(state->buffer) - state->bufferedSize)  {   /* fill in tmp buffer */\n        XXH_memcpy(state->buffer + state->bufferedSize, input, len);\n        state->bufferedSize += (XXH32_hash_t)len;\n        return XXH_OK;\n    }\n\n    {   const xxh_u8* xinput = (const xxh_u8*)input;\n        const xxh_u8* const bEnd = xinput + len;\n\n        if (state->bufferedSize) {   /* non-empty buffer => complete first */\n            XXH_memcpy(state->buffer + state->bufferedSize, xinput, sizeof(state->buffer) - state->bufferedSize);\n            xinput += sizeof(state->buffer) - state->bufferedSize;\n            /* and process one round */\n            (void)XXH64_consumeLong(state->acc, state->buffer, sizeof(state->buffer), XXH_aligned);\n            state->bufferedSize = 0;\n        }\n\n        XXH_ASSERT(xinput <= bEnd);\n        if ((size_t)(bEnd - xinput) >= sizeof(state->buffer)) {\n            /* Process the remaining data */\n            xinput = XXH64_consumeLong(state->acc, xinput, (size_t)(bEnd - xinput), XXH_unaligned);\n        }\n\n        if (xinput < bEnd) {\n            /* Copy the leftover to the tmp buffer */\n            XXH_memcpy(state->buffer, xinput, (size_t)(bEnd-xinput));\n            state->bufferedSize = (unsigned)(bEnd-xinput);\n        }\n    }\n\n    return XXH_OK;\n}\n\n\n/*! @ingroup XXH64_family */\nXXH_PUBLIC_API XXH64_hash_t XXH64_digest(XXH_NOESCAPE const XXH64_state_t* state)\n{\n    xxh_u64 h64;\n\n    if (state->total_len >= 32) {\n        h64 = XXH64_mergeAccs(state->acc);\n    } else {\n        h64  = state->acc[2] /*seed*/ + XXH_PRIME64_5;\n    }\n\n    h64 += (xxh_u64) state->total_len;\n\n    return XXH64_finalize(h64, state->buffer, (size_t)state->total_len, XXH_aligned);\n}\n#endif /* !XXH_NO_STREAM */\n\n/******* Canonical representation   *******/\n\n/*! @ingroup XXH64_family */\nXXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash)\n{\n    XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));\n    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);\n    XXH_memcpy(dst, &hash, sizeof(*dst));\n}\n\n/*! @ingroup XXH64_family */\nXXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src)\n{\n    return XXH_readBE64(src);\n}\n\n#ifndef XXH_NO_XXH3\n\n/* *********************************************************************\n*  XXH3\n*  New generation hash designed for speed on small keys and vectorization\n************************************************************************ */\n/*!\n * @}\n * @defgroup XXH3_impl XXH3 implementation\n * @ingroup impl\n * @{\n */\n\n/* ===   Compiler specifics   === */\n\n\n#if (defined(__GNUC__) && (__GNUC__ >= 3))  \\\n  || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \\\n  || defined(__clang__)\n#    define XXH_likely(x) __builtin_expect(x, 1)\n#    define XXH_unlikely(x) __builtin_expect(x, 0)\n#else\n#    define XXH_likely(x) (x)\n#    define XXH_unlikely(x) (x)\n#endif\n\n#ifndef XXH_HAS_INCLUDE\n#  ifdef __has_include\n/*\n * Not defined as XXH_HAS_INCLUDE(x) (function-like) because\n * this causes segfaults in Apple Clang 4.2 (on Mac OS X 10.7 Lion)\n */\n#    define XXH_HAS_INCLUDE __has_include\n#  else\n#    define XXH_HAS_INCLUDE(x) 0\n#  endif\n#endif\n\n#if defined(__GNUC__) || defined(__clang__)\n#  if defined(__ARM_FEATURE_SVE)\n#    include <arm_sve.h>\n#  endif\n#  if defined(__ARM_NEON__) || defined(__ARM_NEON) \\\n   || (defined(_M_ARM) && _M_ARM >= 7) \\\n   || defined(_M_ARM64) || defined(_M_ARM64EC) \\\n   || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE(<arm_neon.h>)) /* WASM SIMD128 via SIMDe */\n#    define inline __inline__  /* circumvent a clang bug */\n#    include <arm_neon.h>\n#    undef inline\n#  elif defined(__AVX2__)\n#    include <immintrin.h>\n#  elif defined(__SSE2__)\n#    include <emmintrin.h>\n#  elif defined(__loongarch_asx)\n#    include <lasxintrin.h>\n#    include <lsxintrin.h>\n#  elif defined(__loongarch_sx)\n#    include <lsxintrin.h>\n#  elif defined(__riscv_vector)\n#    include <riscv_vector.h>\n#  endif\n#endif\n\n#if defined(_MSC_VER)\n#  include <intrin.h>\n#endif\n\n/*\n * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while\n * remaining a true 64-bit/128-bit hash function.\n *\n * This is done by prioritizing a subset of 64-bit operations that can be\n * emulated without too many steps on the average 32-bit machine.\n *\n * For example, these two lines seem similar, and run equally fast on 64-bit:\n *\n *   xxh_u64 x;\n *   x ^= (x >> 47); // good\n *   x ^= (x >> 13); // bad\n *\n * However, to a 32-bit machine, there is a major difference.\n *\n * x ^= (x >> 47) looks like this:\n *\n *   x.lo ^= (x.hi >> (47 - 32));\n *\n * while x ^= (x >> 13) looks like this:\n *\n *   // note: funnel shifts are not usually cheap.\n *   x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13));\n *   x.hi ^= (x.hi >> 13);\n *\n * The first one is significantly faster than the second, simply because the\n * shift is larger than 32. This means:\n *  - All the bits we need are in the upper 32 bits, so we can ignore the lower\n *    32 bits in the shift.\n *  - The shift result will always fit in the lower 32 bits, and therefore,\n *    we can ignore the upper 32 bits in the xor.\n *\n * Thanks to this optimization, XXH3 only requires these features to be efficient:\n *\n *  - Usable unaligned access\n *  - A 32-bit or 64-bit ALU\n *      - If 32-bit, a decent ADC instruction\n *  - A 32 or 64-bit multiply with a 64-bit result\n *  - For the 128-bit variant, a decent byteswap helps short inputs.\n *\n * The first two are already required by XXH32, and almost all 32-bit and 64-bit\n * platforms which can run XXH32 can run XXH3 efficiently.\n *\n * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one\n * notable exception.\n *\n * First of all, Thumb-1 lacks support for the UMULL instruction which\n * performs the important long multiply. This means numerous __aeabi_lmul\n * calls.\n *\n * Second of all, the 8 functional registers are just not enough.\n * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need\n * Lo registers, and this shuffling results in thousands more MOVs than A32.\n *\n * A32 and T32 don't have this limitation. They can access all 14 registers,\n * do a 32->64 multiply with UMULL, and the flexible operand allowing free\n * shifts is helpful, too.\n *\n * Therefore, we do a quick sanity check.\n *\n * If compiling Thumb-1 for a target which supports ARM instructions, we will\n * emit a warning, as it is not a \"sane\" platform to compile for.\n *\n * Usually, if this happens, it is because of an accident and you probably need\n * to specify -march, as you likely meant to compile for a newer architecture.\n *\n * Credit: large sections of the vectorial and asm source code paths\n *         have been contributed by @easyaspi314\n */\n#if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM)\n#   warning \"XXH3 is highly inefficient without ARM or Thumb-2.\"\n#endif\n\n/* ==========================================\n * Vectorization detection\n * ========================================== */\n\n#ifdef XXH_DOXYGEN\n/*!\n * @ingroup tuning\n * @brief Overrides the vectorization implementation chosen for XXH3.\n *\n * Can be defined to 0 to disable SIMD,\n * or any other authorized value of @ref XXH_VECTOR.\n *\n * If this is not defined, it uses predefined macros to determine the best\n * implementation.\n */\n#  define XXH_VECTOR XXH_SCALAR\n/*!\n * @ingroup tuning\n * @brief Selects the minimum alignment for XXH3's accumulators.\n *\n * When using SIMD, this should match the alignment required for said vector\n * type, so, for example, 32 for AVX2.\n *\n * Default: Auto detected.\n */\n#  define XXH_ACC_ALIGN 8\n#endif\n\n/* Actual definition */\n#ifndef XXH_DOXYGEN\n#endif\n\n#ifndef XXH_VECTOR    /* can be defined on command line */\n#  if ( \\\n        defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \\\n     || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) /* msvc */ \\\n     || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE(<arm_neon.h>)) /* wasm simd128 via SIMDe */ \\\n   ) && ( \\\n        defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \\\n    || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \\\n   )\n#    define XXH_VECTOR XXH_NEON\n#  elif defined(__ARM_FEATURE_SVE)\n#    define XXH_VECTOR XXH_SVE\n#  elif defined(__AVX512F__)\n#    define XXH_VECTOR XXH_AVX512\n#  elif defined(__AVX2__)\n#    define XXH_VECTOR XXH_AVX2\n#  elif defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2))\n#    define XXH_VECTOR XXH_SSE2\n#  elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \\\n     || (defined(__s390x__) && defined(__VEC__)) \\\n     && defined(__GNUC__) /* TODO: IBM XL */\n#    define XXH_VECTOR XXH_VSX\n#  elif defined(__loongarch_asx)\n#    define XXH_VECTOR XXH_LASX\n#  elif defined(__loongarch_sx)\n#    define XXH_VECTOR XXH_LSX\n#  elif defined(__riscv_vector)\n#    define XXH_VECTOR XXH_RVV\n#  else\n#    define XXH_VECTOR XXH_SCALAR\n#  endif\n#endif\n\n/* __ARM_FEATURE_SVE is only supported by GCC & Clang. */\n#if (XXH_VECTOR == XXH_SVE) && !defined(__ARM_FEATURE_SVE)\n#  ifdef _MSC_VER\n#    pragma warning(once : 4606)\n#  else\n#    warning \"__ARM_FEATURE_SVE isn't supported. Use SCALAR instead.\"\n#  endif\n#  undef XXH_VECTOR\n#  define XXH_VECTOR XXH_SCALAR\n#endif\n\n/*\n * Controls the alignment of the accumulator,\n * for compatibility with aligned vector loads, which are usually faster.\n */\n#ifndef XXH_ACC_ALIGN\n#  if defined(XXH_X86DISPATCH)\n#     define XXH_ACC_ALIGN 64  /* for compatibility with avx512 */\n#  elif XXH_VECTOR == XXH_SCALAR  /* scalar */\n#     define XXH_ACC_ALIGN 8\n#  elif XXH_VECTOR == XXH_SSE2  /* sse2 */\n#     define XXH_ACC_ALIGN 16\n#  elif XXH_VECTOR == XXH_AVX2  /* avx2 */\n#     define XXH_ACC_ALIGN 32\n#  elif XXH_VECTOR == XXH_NEON  /* neon */\n#     define XXH_ACC_ALIGN 16\n#  elif XXH_VECTOR == XXH_VSX   /* vsx */\n#     define XXH_ACC_ALIGN 16\n#  elif XXH_VECTOR == XXH_AVX512  /* avx512 */\n#     define XXH_ACC_ALIGN 64\n#  elif XXH_VECTOR == XXH_SVE   /* sve */\n#     define XXH_ACC_ALIGN 64\n#  elif XXH_VECTOR == XXH_LASX   /* lasx */\n#     define XXH_ACC_ALIGN 64\n#  elif XXH_VECTOR == XXH_LSX   /* lsx */\n#     define XXH_ACC_ALIGN 64\n#  elif XXH_VECTOR == XXH_RVV   /* rvv */\n#     define XXH_ACC_ALIGN 64   /* could be 8, but 64 may be faster */\n#  endif\n#endif\n\n#if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \\\n    || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512\n#  define XXH_SEC_ALIGN XXH_ACC_ALIGN\n#elif XXH_VECTOR == XXH_SVE\n#  define XXH_SEC_ALIGN XXH_ACC_ALIGN\n#elif XXH_VECTOR == XXH_RVV\n#  define XXH_SEC_ALIGN XXH_ACC_ALIGN\n#else\n#  define XXH_SEC_ALIGN 8\n#endif\n\n#if defined(__GNUC__) || defined(__clang__)\n#  define XXH_ALIASING __attribute__((__may_alias__))\n#else\n#  define XXH_ALIASING /* nothing */\n#endif\n\n/*\n * UGLY HACK:\n * GCC usually generates the best code with -O3 for xxHash.\n *\n * However, when targeting AVX2, it is overzealous in its unrolling resulting\n * in code roughly 3/4 the speed of Clang.\n *\n * There are other issues, such as GCC splitting _mm256_loadu_si256 into\n * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which\n * only applies to Sandy and Ivy Bridge... which don't even support AVX2.\n *\n * That is why when compiling the AVX2 version, it is recommended to use either\n *   -O2 -mavx2 -march=haswell\n * or\n *   -O2 -mavx2 -mno-avx256-split-unaligned-load\n * for decent performance, or to use Clang instead.\n *\n * Fortunately, we can control the first one with a pragma that forces GCC into\n * -O2, but the other one we can't control without \"failed to inline always\n * inline function due to target mismatch\" warnings.\n */\n#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \\\n  && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \\\n  && defined(__OPTIMIZE__) && XXH_SIZE_OPT <= 0 /* respect -O0 and -Os */\n#  pragma GCC push_options\n#  pragma GCC optimize(\"-O2\")\n#endif\n\n#if XXH_VECTOR == XXH_NEON\n\n/*\n * UGLY HACK: While AArch64 GCC on Linux does not seem to care, on macOS, GCC -O3\n * optimizes out the entire hashLong loop because of the aliasing violation.\n *\n * However, GCC is also inefficient at load-store optimization with vld1q/vst1q,\n * so the only option is to mark it as aliasing.\n */\ntypedef uint64x2_t xxh_aliasing_uint64x2_t XXH_ALIASING;\n\n/*!\n * @internal\n * @brief `vld1q_u64` but faster and alignment-safe.\n *\n * On AArch64, unaligned access is always safe, but on ARMv7-a, it is only\n * *conditionally* safe (`vld1` has an alignment bit like `movdq[ua]` in x86).\n *\n * GCC for AArch64 sees `vld1q_u8` as an intrinsic instead of a load, so it\n * prohibits load-store optimizations. Therefore, a direct dereference is used.\n *\n * Otherwise, `vld1q_u8` is used with `vreinterpretq_u8_u64` to do a safe\n * unaligned load.\n */\n#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__)\nXXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) /* silence -Wcast-align */\n{\n    return *(xxh_aliasing_uint64x2_t const *)ptr;\n}\n#else\nXXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr)\n{\n    return vreinterpretq_u64_u8(vld1q_u8((uint8_t const*)ptr));\n}\n#endif\n\n/*!\n * @internal\n * @brief `vmlal_u32` on low and high halves of a vector.\n *\n * This is a workaround for AArch64 GCC < 11 which implemented arm_neon.h with\n * inline assembly and were therefore incapable of merging the `vget_{low, high}_u32`\n * with `vmlal_u32`.\n */\n#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 11\nXXH_FORCE_INLINE uint64x2_t\nXXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs)\n{\n    /* Inline assembly is the only way */\n    __asm__(\"umlal   %0.2d, %1.2s, %2.2s\" : \"+w\" (acc) : \"w\" (lhs), \"w\" (rhs));\n    return acc;\n}\nXXH_FORCE_INLINE uint64x2_t\nXXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs)\n{\n    /* This intrinsic works as expected */\n    return vmlal_high_u32(acc, lhs, rhs);\n}\n#else\n/* Portable intrinsic versions */\nXXH_FORCE_INLINE uint64x2_t\nXXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs)\n{\n    return vmlal_u32(acc, vget_low_u32(lhs), vget_low_u32(rhs));\n}\n/*! @copydoc XXH_vmlal_low_u32\n * Assume the compiler converts this to vmlal_high_u32 on aarch64 */\nXXH_FORCE_INLINE uint64x2_t\nXXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs)\n{\n    return vmlal_u32(acc, vget_high_u32(lhs), vget_high_u32(rhs));\n}\n#endif\n\n/*!\n * @ingroup tuning\n * @brief Controls the NEON to scalar ratio for XXH3\n *\n * This can be set to 2, 4, 6, or 8.\n *\n * ARM Cortex CPUs are _very_ sensitive to how their pipelines are used.\n *\n * For example, the Cortex-A73 can dispatch 3 micro-ops per cycle, but only 2 of those\n * can be NEON. If you are only using NEON instructions, you are only using 2/3 of the CPU\n * bandwidth.\n *\n * This is even more noticeable on the more advanced cores like the Cortex-A76 which\n * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once.\n *\n * Therefore, to make the most out of the pipeline, it is beneficial to run 6 NEON lanes\n * and 2 scalar lanes, which is chosen by default.\n *\n * This does not apply to Apple processors or 32-bit processors, which run better with\n * full NEON. These will default to 8. Additionally, size-optimized builds run 8 lanes.\n *\n * This change benefits CPUs with large micro-op buffers without negatively affecting\n * most other CPUs:\n *\n *  | Chipset               | Dispatch type       | NEON only | 6:2 hybrid | Diff. |\n *  |:----------------------|:--------------------|----------:|-----------:|------:|\n *  | Snapdragon 730 (A76)  | 2 NEON/8 micro-ops  |  8.8 GB/s |  10.1 GB/s |  ~16% |\n *  | Snapdragon 835 (A73)  | 2 NEON/3 micro-ops  |  5.1 GB/s |   5.3 GB/s |   ~5% |\n *  | Marvell PXA1928 (A53) | In-order dual-issue |  1.9 GB/s |   1.9 GB/s |    0% |\n *  | Apple M1              | 4 NEON/8 micro-ops  | 37.3 GB/s |  36.1 GB/s |  ~-3% |\n *\n * It also seems to fix some bad codegen on GCC, making it almost as fast as clang.\n *\n * When using WASM SIMD128, if this is 2 or 6, SIMDe will scalarize 2 of the lanes meaning\n * it effectively becomes worse 4.\n *\n * @see XXH3_accumulate_512_neon()\n */\n# ifndef XXH3_NEON_LANES\n#  if (defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) \\\n   && !defined(__APPLE__) && XXH_SIZE_OPT <= 0\n#   define XXH3_NEON_LANES 6\n#  else\n#   define XXH3_NEON_LANES XXH_ACC_NB\n#  endif\n# endif\n#endif  /* XXH_VECTOR == XXH_NEON */\n\n/*\n * VSX and Z Vector helpers.\n *\n * This is very messy, and any pull requests to clean this up are welcome.\n *\n * There are a lot of problems with supporting VSX and s390x, due to\n * inconsistent intrinsics, spotty coverage, and multiple endiannesses.\n */\n#if XXH_VECTOR == XXH_VSX\n/* Annoyingly, these headers _may_ define three macros: `bool`, `vector`,\n * and `pixel`. This is a problem for obvious reasons.\n *\n * These keywords are unnecessary; the spec literally says they are\n * equivalent to `__bool`, `__vector`, and `__pixel` and may be undef'd\n * after including the header.\n *\n * We use pragma push_macro/pop_macro to keep the namespace clean. */\n#  pragma push_macro(\"bool\")\n#  pragma push_macro(\"vector\")\n#  pragma push_macro(\"pixel\")\n/* silence potential macro redefined warnings */\n#  undef bool\n#  undef vector\n#  undef pixel\n\n#  if defined(__s390x__)\n#    include <s390intrin.h>\n#  else\n#    include <altivec.h>\n#  endif\n\n/* Restore the original macro values, if applicable. */\n#  pragma pop_macro(\"pixel\")\n#  pragma pop_macro(\"vector\")\n#  pragma pop_macro(\"bool\")\n\ntypedef __vector unsigned long long xxh_u64x2;\ntypedef __vector unsigned char xxh_u8x16;\ntypedef __vector unsigned xxh_u32x4;\n\n/*\n * UGLY HACK: Similar to aarch64 macOS GCC, s390x GCC has the same aliasing issue.\n */\ntypedef xxh_u64x2 xxh_aliasing_u64x2 XXH_ALIASING;\n\n# ifndef XXH_VSX_BE\n#  if defined(__BIG_ENDIAN__) \\\n  || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)\n#    define XXH_VSX_BE 1\n#  elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__\n#    warning \"-maltivec=be is not recommended. Please use native endianness.\"\n#    define XXH_VSX_BE 1\n#  else\n#    define XXH_VSX_BE 0\n#  endif\n# endif /* !defined(XXH_VSX_BE) */\n\n# if XXH_VSX_BE\n#  if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__))\n#    define XXH_vec_revb vec_revb\n#  else\n/*!\n * A polyfill for POWER9's vec_revb().\n */\nXXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val)\n{\n    xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,\n                                  0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 };\n    return vec_perm(val, val, vByteSwap);\n}\n#  endif\n# endif /* XXH_VSX_BE */\n\n/*!\n * Performs an unaligned vector load and byte swaps it on big endian.\n */\nXXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr)\n{\n    xxh_u64x2 ret;\n    XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2));\n# if XXH_VSX_BE\n    ret = XXH_vec_revb(ret);\n# endif\n    return ret;\n}\n\n/*\n * vec_mulo and vec_mule are very problematic intrinsics on PowerPC\n *\n * These intrinsics weren't added until GCC 8, despite existing for a while,\n * and they are endian dependent. Also, their meaning swap depending on version.\n * */\n# if defined(__s390x__)\n /* s390x is always big endian, no issue on this platform */\n#  define XXH_vec_mulo vec_mulo\n#  define XXH_vec_mule vec_mule\n# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) && !defined(__ibmxl__)\n/* Clang has a better way to control this, we can just use the builtin which doesn't swap. */\n /* The IBM XL Compiler (which defined __clang__) only implements the vec_* operations */\n#  define XXH_vec_mulo __builtin_altivec_vmulouw\n#  define XXH_vec_mule __builtin_altivec_vmuleuw\n# else\n/* gcc needs inline assembly */\n/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */\nXXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b)\n{\n    xxh_u64x2 result;\n    __asm__(\"vmulouw %0, %1, %2\" : \"=v\" (result) : \"v\" (a), \"v\" (b));\n    return result;\n}\nXXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b)\n{\n    xxh_u64x2 result;\n    __asm__(\"vmuleuw %0, %1, %2\" : \"=v\" (result) : \"v\" (a), \"v\" (b));\n    return result;\n}\n# endif /* XXH_vec_mulo, XXH_vec_mule */\n#endif /* XXH_VECTOR == XXH_VSX */\n\n#if XXH_VECTOR == XXH_SVE\n#define ACCRND(acc, offset) \\\ndo { \\\n    svuint64_t input_vec = svld1_u64(mask, xinput + offset);         \\\n    svuint64_t secret_vec = svld1_u64(mask, xsecret + offset);       \\\n    svuint64_t mixed = sveor_u64_x(mask, secret_vec, input_vec);     \\\n    svuint64_t swapped = svtbl_u64(input_vec, kSwap);                \\\n    svuint64_t mixed_lo = svextw_u64_x(mask, mixed);                 \\\n    svuint64_t mixed_hi = svlsr_n_u64_x(mask, mixed, 32);            \\\n    svuint64_t mul = svmad_u64_x(mask, mixed_lo, mixed_hi, swapped); \\\n    acc = svadd_u64_x(mask, acc, mul);                               \\\n} while (0)\n#endif /* XXH_VECTOR == XXH_SVE */\n\n/* prefetch\n * can be disabled, by declaring XXH_NO_PREFETCH build macro */\n#if defined(XXH_NO_PREFETCH)\n#  define XXH_PREFETCH(ptr)  (void)(ptr)  /* disabled */\n#else\n#  if XXH_SIZE_OPT >= 1\n#    define XXH_PREFETCH(ptr) (void)(ptr)\n#  elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))  /* _mm_prefetch() not defined outside of x86/x64 */\n#    include <mmintrin.h>   /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */\n#    define XXH_PREFETCH(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_T0)\n#  elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )\n#    define XXH_PREFETCH(ptr)  __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */)\n#  else\n#    define XXH_PREFETCH(ptr) (void)(ptr)  /* disabled */\n#  endif\n#endif  /* XXH_NO_PREFETCH */\n\n\n/* ==========================================\n * XXH3 default settings\n * ========================================== */\n\n#define XXH_SECRET_DEFAULT_SIZE 192   /* minimum XXH3_SECRET_SIZE_MIN */\n\n#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN)\n#  error \"default keyset is not large enough\"\n#endif\n\n/*!\n * @internal\n * @def XXH3_kSecret\n * @brief Pseudorandom secret taken directly from FARSH. */\nXXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = {\n    0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,\n    0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,\n    0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,\n    0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,\n    0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,\n    0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,\n    0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,\n    0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,\n    0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,\n    0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,\n    0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,\n    0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,\n};\n\nstatic const xxh_u64 PRIME_MX1 = 0x165667919E3779F9ULL;  /*!< 0b0001011001010110011001111001000110011110001101110111100111111001 */\nstatic const xxh_u64 PRIME_MX2 = 0x9FB21C651E98DF25ULL;  /*!< 0b1001111110110010000111000110010100011110100110001101111100100101 */\n\n#ifdef XXH_OLD_NAMES\n#  define kSecret XXH3_kSecret\n#endif\n\n#ifdef XXH_DOXYGEN\n/*!\n * @brief Calculates a 32-bit to 64-bit long multiply.\n *\n * Implemented as a macro.\n *\n * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't\n * need to (but it shouldn't need to anyways, it is about 7 instructions to do\n * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we\n * use that instead of the normal method.\n *\n * If you are compiling for platforms like Thumb-1 and don't have a better option,\n * you may also want to write your own long multiply routine here.\n *\n * @param x, y Numbers to be multiplied\n * @return 64-bit product of the low 32 bits of @p x and @p y.\n */\nXXH_FORCE_INLINE xxh_u64\nXXH_mult32to64(xxh_u64 x, xxh_u64 y)\n{\n   return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF);\n}\n#elif defined(_MSC_VER) && defined(_M_IX86)\n#    define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y))\n#else\n/*\n * Downcast + upcast is usually better than masking on older compilers like\n * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers.\n *\n * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both operands\n * and perform a full 64x64 multiply -- entirely redundant on 32-bit.\n */\n#    define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y))\n#endif\n\n/*!\n * @brief Calculates a 64->128-bit long multiply.\n *\n * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar\n * version.\n *\n * @param lhs , rhs The 64-bit integers to be multiplied\n * @return The 128-bit result represented in an @ref XXH128_hash_t.\n */\nstatic XXH128_hash_t\nXXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs)\n{\n    /*\n     * GCC/Clang __uint128_t method.\n     *\n     * On most 64-bit targets, GCC and Clang define a __uint128_t type.\n     * This is usually the best way as it usually uses a native long 64-bit\n     * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64.\n     *\n     * Usually.\n     *\n     * Despite being a 32-bit platform, Clang (and emscripten) define this type\n     * despite not having the arithmetic for it. This results in a laggy\n     * compiler builtin call which calculates a full 128-bit multiply.\n     * In that case it is best to use the portable one.\n     * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677\n     */\n#if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__) \\\n    && defined(__SIZEOF_INT128__) \\\n    || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)\n\n    __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs;\n    XXH128_hash_t r128;\n    r128.low64  = (xxh_u64)(product);\n    r128.high64 = (xxh_u64)(product >> 64);\n    return r128;\n\n    /*\n     * MSVC for x64's _umul128 method.\n     *\n     * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct);\n     *\n     * This compiles to single operand MUL on x64.\n     */\n#elif (defined(_M_X64) || defined(_M_IA64)) && !defined(_M_ARM64EC)\n\n#ifndef _MSC_VER\n#   pragma intrinsic(_umul128)\n#endif\n    xxh_u64 product_high;\n    xxh_u64 const product_low = _umul128(lhs, rhs, &product_high);\n    XXH128_hash_t r128;\n    r128.low64  = product_low;\n    r128.high64 = product_high;\n    return r128;\n\n    /*\n     * MSVC for ARM64's __umulh method.\n     *\n     * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method.\n     */\n#elif defined(_M_ARM64) || defined(_M_ARM64EC)\n\n#ifndef _MSC_VER\n#   pragma intrinsic(__umulh)\n#endif\n    XXH128_hash_t r128;\n    r128.low64  = lhs * rhs;\n    r128.high64 = __umulh(lhs, rhs);\n    return r128;\n\n#else\n    /*\n     * Portable scalar method. Optimized for 32-bit and 64-bit ALUs.\n     *\n     * This is a fast and simple grade school multiply, which is shown below\n     * with base 10 arithmetic instead of base 0x100000000.\n     *\n     *           9 3 // D2 lhs = 93\n     *         x 7 5 // D2 rhs = 75\n     *     ----------\n     *           1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15\n     *         4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45\n     *         2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21\n     *     + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63\n     *     ---------\n     *         2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27\n     *     + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67\n     *     ---------\n     *       6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975\n     *\n     * The reasons for adding the products like this are:\n     *  1. It avoids manual carry tracking. Just like how\n     *     (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX.\n     *     This avoids a lot of complexity.\n     *\n     *  2. It hints for, and on Clang, compiles to, the powerful UMAAL\n     *     instruction available in ARM's Digital Signal Processing extension\n     *     in 32-bit ARMv6 and later, which is shown below:\n     *\n     *         void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm)\n     *         {\n     *             xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm;\n     *             *RdLo = (xxh_u32)(product & 0xFFFFFFFF);\n     *             *RdHi = (xxh_u32)(product >> 32);\n     *         }\n     *\n     *     This instruction was designed for efficient long multiplication, and\n     *     allows this to be calculated in only 4 instructions at speeds\n     *     comparable to some 64-bit ALUs.\n     *\n     *  3. It isn't terrible on other platforms. Usually this will be a couple\n     *     of 32-bit ADD/ADCs.\n     */\n\n    /* First calculate all of the cross products. */\n    xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF);\n    xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32,        rhs & 0xFFFFFFFF);\n    xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32);\n    xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32,        rhs >> 32);\n\n    /* Now add the products together. These will never overflow. */\n    xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi;\n    xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32)        + hi_hi;\n    xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF);\n\n    XXH128_hash_t r128;\n    r128.low64  = lower;\n    r128.high64 = upper;\n    return r128;\n#endif\n}\n\n/*!\n * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it.\n *\n * The reason for the separate function is to prevent passing too many structs\n * around by value. This will hopefully inline the multiply, but we don't force it.\n *\n * @param lhs , rhs The 64-bit integers to multiply\n * @return The low 64 bits of the product XOR'd by the high 64 bits.\n * @see XXH_mult64to128()\n */\nstatic xxh_u64\nXXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs)\n{\n    XXH128_hash_t product = XXH_mult64to128(lhs, rhs);\n    return product.low64 ^ product.high64;\n}\n\n/*! Seems to produce slightly better code on GCC for some reason. */\nXXH_FORCE_INLINE XXH_CONSTF xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift)\n{\n    XXH_ASSERT(0 <= shift && shift < 64);\n    return v64 ^ (v64 >> shift);\n}\n\n/*\n * This is a fast avalanche stage,\n * suitable when input bits are already partially mixed\n */\nstatic XXH64_hash_t XXH3_avalanche(xxh_u64 h64)\n{\n    h64 = XXH_xorshift64(h64, 37);\n    h64 *= PRIME_MX1;\n    h64 = XXH_xorshift64(h64, 32);\n    return h64;\n}\n\n/*\n * This is a stronger avalanche,\n * inspired by Pelle Evensen's rrmxmx\n * preferable when input has not been previously mixed\n */\nstatic XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len)\n{\n    /* this mix is inspired by Pelle Evensen's rrmxmx */\n    h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24);\n    h64 *= PRIME_MX2;\n    h64 ^= (h64 >> 35) + len ;\n    h64 *= PRIME_MX2;\n    return XXH_xorshift64(h64, 28);\n}\n\n\n/* ==========================================\n * Short keys\n * ==========================================\n * One of the shortcomings of XXH32 and XXH64 was that their performance was\n * sub-optimal on short lengths. It used an iterative algorithm which strongly\n * favored lengths that were a multiple of 4 or 8.\n *\n * Instead of iterating over individual inputs, we use a set of single shot\n * functions which piece together a range of lengths and operate in constant time.\n *\n * Additionally, the number of multiplies has been significantly reduced. This\n * reduces latency, especially when emulating 64-bit multiplies on 32-bit.\n *\n * Depending on the platform, this may or may not be faster than XXH32, but it\n * is almost guaranteed to be faster than XXH64.\n */\n\n/*\n * At very short lengths, there isn't enough input to fully hide secrets, or use\n * the entire secret.\n *\n * There is also only a limited amount of mixing we can do before significantly\n * impacting performance.\n *\n * Therefore, we use different sections of the secret and always mix two secret\n * samples with an XOR. This should have no effect on performance on the\n * seedless or withSeed variants because everything _should_ be constant folded\n * by modern compilers.\n *\n * The XOR mixing hides individual parts of the secret and increases entropy.\n *\n * This adds an extra layer of strength for custom secrets.\n */\nXXH_FORCE_INLINE XXH_PUREF XXH64_hash_t\nXXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)\n{\n    XXH_ASSERT(input != NULL);\n    XXH_ASSERT(1 <= len && len <= 3);\n    XXH_ASSERT(secret != NULL);\n    /*\n     * len = 1: combined = { input[0], 0x01, input[0], input[0] }\n     * len = 2: combined = { input[1], 0x02, input[0], input[1] }\n     * len = 3: combined = { input[2], 0x03, input[0], input[1] }\n     */\n    {   xxh_u8  const c1 = input[0];\n        xxh_u8  const c2 = input[len >> 1];\n        xxh_u8  const c3 = input[len - 1];\n        xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2  << 24)\n                               | ((xxh_u32)c3 <<  0) | ((xxh_u32)len << 8);\n        xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed;\n        xxh_u64 const keyed = (xxh_u64)combined ^ bitflip;\n        return XXH64_avalanche(keyed);\n    }\n}\n\nXXH_FORCE_INLINE XXH_PUREF XXH64_hash_t\nXXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)\n{\n    XXH_ASSERT(input != NULL);\n    XXH_ASSERT(secret != NULL);\n    XXH_ASSERT(4 <= len && len <= 8);\n    seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;\n    {   xxh_u32 const input1 = XXH_readLE32(input);\n        xxh_u32 const input2 = XXH_readLE32(input + len - 4);\n        xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed;\n        xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32);\n        xxh_u64 const keyed = input64 ^ bitflip;\n        return XXH3_rrmxmx(keyed, len);\n    }\n}\n\nXXH_FORCE_INLINE XXH_PUREF XXH64_hash_t\nXXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)\n{\n    XXH_ASSERT(input != NULL);\n    XXH_ASSERT(secret != NULL);\n    XXH_ASSERT(9 <= len && len <= 16);\n    {   xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed;\n        xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed;\n        xxh_u64 const input_lo = XXH_readLE64(input)           ^ bitflip1;\n        xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2;\n        xxh_u64 const acc = len\n                          + XXH_swap64(input_lo) + input_hi\n                          + XXH3_mul128_fold64(input_lo, input_hi);\n        return XXH3_avalanche(acc);\n    }\n}\n\nXXH_FORCE_INLINE XXH_PUREF XXH64_hash_t\nXXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)\n{\n    XXH_ASSERT(len <= 16);\n    {   if (XXH_likely(len >  8)) return XXH3_len_9to16_64b(input, len, secret, seed);\n        if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed);\n        if (len) return XXH3_len_1to3_64b(input, len, secret, seed);\n        return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64)));\n    }\n}\n\n/*\n * DISCLAIMER: There are known *seed-dependent* multicollisions here due to\n * multiplication by zero, affecting hashes of lengths 17 to 240.\n *\n * However, they are very unlikely.\n *\n * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all\n * unseeded non-cryptographic hashes, it does not attempt to defend itself\n * against specially crafted inputs, only random inputs.\n *\n * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes\n * cancelling out the secret is taken an arbitrary number of times (addressed\n * in XXH3_accumulate_512), this collision is very unlikely with random inputs\n * and/or proper seeding:\n *\n * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a\n * function that is only called up to 16 times per hash with up to 240 bytes of\n * input.\n *\n * This is not too bad for a non-cryptographic hash function, especially with\n * only 64 bit outputs.\n *\n * The 128-bit variant (which trades some speed for strength) is NOT affected\n * by this, although it is always a good idea to use a proper seed if you care\n * about strength.\n */\nXXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input,\n                                     const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64)\n{\n#if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \\\n  && defined(__i386__) && defined(__SSE2__)  /* x86 + SSE2 */ \\\n  && !defined(XXH_ENABLE_AUTOVECTORIZE)      /* Define to disable like XXH32 hack */\n    /*\n     * UGLY HACK:\n     * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in\n     * slower code.\n     *\n     * By forcing seed64 into a register, we disrupt the cost model and\n     * cause it to scalarize. See `XXH32_round()`\n     *\n     * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600,\n     * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on\n     * GCC 9.2, despite both emitting scalar code.\n     *\n     * GCC generates much better scalar code than Clang for the rest of XXH3,\n     * which is why finding a more optimal codepath is an interest.\n     */\n    XXH_COMPILER_GUARD(seed64);\n#endif\n    {   xxh_u64 const input_lo = XXH_readLE64(input);\n        xxh_u64 const input_hi = XXH_readLE64(input+8);\n        return XXH3_mul128_fold64(\n            input_lo ^ (XXH_readLE64(secret)   + seed64),\n            input_hi ^ (XXH_readLE64(secret+8) - seed64)\n        );\n    }\n}\n\n/* For mid range keys, XXH3 uses a Mum-hash variant. */\nXXH_FORCE_INLINE XXH_PUREF XXH64_hash_t\nXXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len,\n                     const xxh_u8* XXH_RESTRICT secret, size_t secretSize,\n                     XXH64_hash_t seed)\n{\n    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;\n    XXH_ASSERT(16 < len && len <= 128);\n\n    {   xxh_u64 acc = len * XXH_PRIME64_1;\n#if XXH_SIZE_OPT >= 1\n        /* Smaller and cleaner, but slightly slower. */\n        unsigned int i = (unsigned int)(len - 1) / 32;\n        do {\n            acc += XXH3_mix16B(input+16 * i, secret+32*i, seed);\n            acc += XXH3_mix16B(input+len-16*(i+1), secret+32*i+16, seed);\n        } while (i-- != 0);\n#else\n        if (len > 32) {\n            if (len > 64) {\n                if (len > 96) {\n                    acc += XXH3_mix16B(input+48, secret+96, seed);\n                    acc += XXH3_mix16B(input+len-64, secret+112, seed);\n                }\n                acc += XXH3_mix16B(input+32, secret+64, seed);\n                acc += XXH3_mix16B(input+len-48, secret+80, seed);\n            }\n            acc += XXH3_mix16B(input+16, secret+32, seed);\n            acc += XXH3_mix16B(input+len-32, secret+48, seed);\n        }\n        acc += XXH3_mix16B(input+0, secret+0, seed);\n        acc += XXH3_mix16B(input+len-16, secret+16, seed);\n#endif\n        return XXH3_avalanche(acc);\n    }\n}\n\nXXH_NO_INLINE XXH_PUREF XXH64_hash_t\nXXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len,\n                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,\n                      XXH64_hash_t seed)\n{\n    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;\n    XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);\n\n    #define XXH3_MIDSIZE_STARTOFFSET 3\n    #define XXH3_MIDSIZE_LASTOFFSET  17\n\n    {   xxh_u64 acc = len * XXH_PRIME64_1;\n        xxh_u64 acc_end;\n        unsigned int const nbRounds = (unsigned int)len / 16;\n        unsigned int i;\n        XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);\n        for (i=0; i<8; i++) {\n            acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed);\n        }\n        /* last bytes */\n        acc_end = XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed);\n        XXH_ASSERT(nbRounds >= 8);\n        acc = XXH3_avalanche(acc);\n#if defined(__clang__)                                /* Clang */ \\\n    && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \\\n    && !defined(XXH_ENABLE_AUTOVECTORIZE)             /* Define to disable */\n        /*\n         * UGLY HACK:\n         * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86.\n         * In everywhere else, it uses scalar code.\n         *\n         * For 64->128-bit multiplies, even if the NEON was 100% optimal, it\n         * would still be slower than UMAAL (see XXH_mult64to128).\n         *\n         * Unfortunately, Clang doesn't handle the long multiplies properly and\n         * converts them to the nonexistent \"vmulq_u64\" intrinsic, which is then\n         * scalarized into an ugly mess of VMOV.32 instructions.\n         *\n         * This mess is difficult to avoid without turning autovectorization\n         * off completely, but they are usually relatively minor and/or not\n         * worth it to fix.\n         *\n         * This loop is the easiest to fix, as unlike XXH32, this pragma\n         * _actually works_ because it is a loop vectorization instead of an\n         * SLP vectorization.\n         */\n        #pragma clang loop vectorize(disable)\n#endif\n        for (i=8 ; i < nbRounds; i++) {\n            /*\n             * Prevents clang for unrolling the acc loop and interleaving with this one.\n             */\n            XXH_COMPILER_GUARD(acc);\n            acc_end += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed);\n        }\n        return XXH3_avalanche(acc + acc_end);\n    }\n}\n\n\n/* =======     Long Keys     ======= */\n\n#define XXH_STRIPE_LEN 64\n#define XXH_SECRET_CONSUME_RATE 8   /* nb of secret bytes consumed at each accumulation */\n#define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64))\n\n#ifdef XXH_OLD_NAMES\n#  define STRIPE_LEN XXH_STRIPE_LEN\n#  define ACC_NB XXH_ACC_NB\n#endif\n\n#ifndef XXH_PREFETCH_DIST\n#  ifdef __clang__\n#    define XXH_PREFETCH_DIST 320\n#  else\n#    if (XXH_VECTOR == XXH_AVX512)\n#      define XXH_PREFETCH_DIST 512\n#    else\n#      define XXH_PREFETCH_DIST 384\n#    endif\n#  endif  /* __clang__ */\n#endif  /* XXH_PREFETCH_DIST */\n\n/*\n * These macros are to generate an XXH3_accumulate() function.\n * The two arguments select the name suffix and target attribute.\n *\n * The name of this symbol is XXH3_accumulate_<name>() and it calls\n * XXH3_accumulate_512_<name>().\n *\n * It may be useful to hand implement this function if the compiler fails to\n * optimize the inline function.\n */\n#define XXH3_ACCUMULATE_TEMPLATE(name)                      \\\nvoid                                                        \\\nXXH3_accumulate_##name(xxh_u64* XXH_RESTRICT acc,           \\\n                       const xxh_u8* XXH_RESTRICT input,    \\\n                       const xxh_u8* XXH_RESTRICT secret,   \\\n                       size_t nbStripes)                    \\\n{                                                           \\\n    size_t n;                                               \\\n    for (n = 0; n < nbStripes; n++ ) {                      \\\n        const xxh_u8* const in = input + n*XXH_STRIPE_LEN;  \\\n        XXH_PREFETCH(in + XXH_PREFETCH_DIST);               \\\n        XXH3_accumulate_512_##name(                         \\\n                 acc,                                       \\\n                 in,                                        \\\n                 secret + n*XXH_SECRET_CONSUME_RATE);       \\\n    }                                                       \\\n}\n\n\nXXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64)\n{\n    if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64);\n    XXH_memcpy(dst, &v64, sizeof(v64));\n}\n\n/* Several intrinsic functions below are supposed to accept __int64 as argument,\n * as documented in https://software.intel.com/sites/landingpage/IntrinsicsGuide/ .\n * However, several environments do not define __int64 type,\n * requiring a workaround.\n */\n#if !defined (__VMS) \\\n  && (defined (__cplusplus) \\\n  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )\n    typedef int64_t xxh_i64;\n#else\n    /* the following type must have a width of 64-bit */\n    typedef long long xxh_i64;\n#endif\n\n\n/*\n * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized.\n *\n * It is a hardened version of UMAC, based off of FARSH's implementation.\n *\n * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD\n * implementations, and it is ridiculously fast.\n *\n * We harden it by mixing the original input to the accumulators as well as the product.\n *\n * This means that in the (relatively likely) case of a multiply by zero, the\n * original input is preserved.\n *\n * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve\n * cross-pollination, as otherwise the upper and lower halves would be\n * essentially independent.\n *\n * This doesn't matter on 64-bit hashes since they all get merged together in\n * the end, so we skip the extra step.\n *\n * Both XXH3_64bits and XXH3_128bits use this subroutine.\n */\n\n#if (XXH_VECTOR == XXH_AVX512) \\\n     || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0)\n\n#ifndef XXH_TARGET_AVX512\n# define XXH_TARGET_AVX512  /* disable attribute target */\n#endif\n\nXXH_FORCE_INLINE XXH_TARGET_AVX512 void\nXXH3_accumulate_512_avx512(void* XXH_RESTRICT acc,\n                     const void* XXH_RESTRICT input,\n                     const void* XXH_RESTRICT secret)\n{\n    __m512i* const xacc = (__m512i *) acc;\n    XXH_ASSERT((((size_t)acc) & 63) == 0);\n    XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i));\n\n    {\n        /* data_vec    = input[0]; */\n        __m512i const data_vec    = _mm512_loadu_si512   (input);\n        /* key_vec     = secret[0]; */\n        __m512i const key_vec     = _mm512_loadu_si512   (secret);\n        /* data_key    = data_vec ^ key_vec; */\n        __m512i const data_key    = _mm512_xor_si512     (data_vec, key_vec);\n        /* data_key_lo = data_key >> 32; */\n        __m512i const data_key_lo = _mm512_srli_epi64 (data_key, 32);\n        /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */\n        __m512i const product     = _mm512_mul_epu32     (data_key, data_key_lo);\n        /* xacc[0] += swap(data_vec); */\n        __m512i const data_swap = _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2));\n        __m512i const sum       = _mm512_add_epi64(*xacc, data_swap);\n        /* xacc[0] += product; */\n        *xacc = _mm512_add_epi64(product, sum);\n    }\n}\nXXH_FORCE_INLINE XXH_TARGET_AVX512 XXH3_ACCUMULATE_TEMPLATE(avx512)\n\n/*\n * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing.\n *\n * Multiplication isn't perfect, as explained by Google in HighwayHash:\n *\n *  // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to\n *  // varying degrees. In descending order of goodness, bytes\n *  // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32.\n *  // As expected, the upper and lower bytes are much worse.\n *\n * Source: https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291\n *\n * Since our algorithm uses a pseudorandom secret to add some variance into the\n * mix, we don't need to (or want to) mix as often or as much as HighwayHash does.\n *\n * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid\n * extraction.\n *\n * Both XXH3_64bits and XXH3_128bits use this subroutine.\n */\n\nXXH_FORCE_INLINE XXH_TARGET_AVX512 void\nXXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 63) == 0);\n    XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i));\n    {   __m512i* const xacc = (__m512i*) acc;\n        const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1);\n\n        /* xacc[0] ^= (xacc[0] >> 47) */\n        __m512i const acc_vec     = *xacc;\n        __m512i const shifted     = _mm512_srli_epi64    (acc_vec, 47);\n        /* xacc[0] ^= secret; */\n        __m512i const key_vec     = _mm512_loadu_si512   (secret);\n        __m512i const data_key    = _mm512_ternarylogic_epi32(key_vec, acc_vec, shifted, 0x96 /* key_vec ^ acc_vec ^ shifted */);\n\n        /* xacc[0] *= XXH_PRIME32_1; */\n        __m512i const data_key_hi = _mm512_srli_epi64 (data_key, 32);\n        __m512i const prod_lo     = _mm512_mul_epu32     (data_key, prime32);\n        __m512i const prod_hi     = _mm512_mul_epu32     (data_key_hi, prime32);\n        *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32));\n    }\n}\n\nXXH_FORCE_INLINE XXH_TARGET_AVX512 void\nXXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64)\n{\n    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0);\n    XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64);\n    XXH_ASSERT(((size_t)customSecret & 63) == 0);\n    (void)(&XXH_writeLE64);\n    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i);\n        __m512i const seed_pos = _mm512_set1_epi64((xxh_i64)seed64);\n        __m512i const seed     = _mm512_mask_sub_epi64(seed_pos, 0xAA, _mm512_set1_epi8(0), seed_pos);\n\n        const __m512i* const src  = (const __m512i*) ((const void*) XXH3_kSecret);\n              __m512i* const dest = (      __m512i*) customSecret;\n        int i;\n        XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */\n        XXH_ASSERT(((size_t)dest & 63) == 0);\n        for (i=0; i < nbRounds; ++i) {\n            dest[i] = _mm512_add_epi64(_mm512_load_si512(src + i), seed);\n    }   }\n}\n\n#endif\n\n#if (XXH_VECTOR == XXH_AVX2) \\\n    || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0)\n\n#ifndef XXH_TARGET_AVX2\n# define XXH_TARGET_AVX2  /* disable attribute target */\n#endif\n\nXXH_FORCE_INLINE XXH_TARGET_AVX2 void\nXXH3_accumulate_512_avx2( void* XXH_RESTRICT acc,\n                    const void* XXH_RESTRICT input,\n                    const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 31) == 0);\n    {   __m256i* const xacc    =       (__m256i *) acc;\n        /* Unaligned. This is mainly for pointer arithmetic, and because\n         * _mm256_loadu_si256 requires  a const __m256i * pointer for some reason. */\n        const         __m256i* const xinput  = (const __m256i *) input;\n        /* Unaligned. This is mainly for pointer arithmetic, and because\n         * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */\n        const         __m256i* const xsecret = (const __m256i *) secret;\n\n        size_t i;\n        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) {\n            /* data_vec    = xinput[i]; */\n            __m256i const data_vec    = _mm256_loadu_si256    (xinput+i);\n            /* key_vec     = xsecret[i]; */\n            __m256i const key_vec     = _mm256_loadu_si256   (xsecret+i);\n            /* data_key    = data_vec ^ key_vec; */\n            __m256i const data_key    = _mm256_xor_si256     (data_vec, key_vec);\n            /* data_key_lo = data_key >> 32; */\n            __m256i const data_key_lo = _mm256_srli_epi64 (data_key, 32);\n            /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */\n            __m256i const product     = _mm256_mul_epu32     (data_key, data_key_lo);\n            /* xacc[i] += swap(data_vec); */\n            __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2));\n            __m256i const sum       = _mm256_add_epi64(xacc[i], data_swap);\n            /* xacc[i] += product; */\n            xacc[i] = _mm256_add_epi64(product, sum);\n    }   }\n}\nXXH_FORCE_INLINE XXH_TARGET_AVX2 XXH3_ACCUMULATE_TEMPLATE(avx2)\n\nXXH_FORCE_INLINE XXH_TARGET_AVX2 void\nXXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 31) == 0);\n    {   __m256i* const xacc = (__m256i*) acc;\n        /* Unaligned. This is mainly for pointer arithmetic, and because\n         * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */\n        const         __m256i* const xsecret = (const __m256i *) secret;\n        const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1);\n\n        size_t i;\n        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) {\n            /* xacc[i] ^= (xacc[i] >> 47) */\n            __m256i const acc_vec     = xacc[i];\n            __m256i const shifted     = _mm256_srli_epi64    (acc_vec, 47);\n            __m256i const data_vec    = _mm256_xor_si256     (acc_vec, shifted);\n            /* xacc[i] ^= xsecret; */\n            __m256i const key_vec     = _mm256_loadu_si256   (xsecret+i);\n            __m256i const data_key    = _mm256_xor_si256     (data_vec, key_vec);\n\n            /* xacc[i] *= XXH_PRIME32_1; */\n            __m256i const data_key_hi = _mm256_srli_epi64 (data_key, 32);\n            __m256i const prod_lo     = _mm256_mul_epu32     (data_key, prime32);\n            __m256i const prod_hi     = _mm256_mul_epu32     (data_key_hi, prime32);\n            xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32));\n        }\n    }\n}\n\nXXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTRICT customSecret, xxh_u64 seed64)\n{\n    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0);\n    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6);\n    XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64);\n    (void)(&XXH_writeLE64);\n    XXH_PREFETCH(customSecret);\n    {   __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64);\n\n        const __m256i* const src  = (const __m256i*) ((const void*) XXH3_kSecret);\n              __m256i*       dest = (      __m256i*) customSecret;\n\n#       if defined(__GNUC__) || defined(__clang__)\n        /*\n         * On GCC & Clang, marking 'dest' as modified will cause the compiler:\n         *   - do not extract the secret from sse registers in the internal loop\n         *   - use less common registers, and avoid pushing these reg into stack\n         */\n        XXH_COMPILER_GUARD(dest);\n#       endif\n        XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */\n        XXH_ASSERT(((size_t)dest & 31) == 0);\n\n        /* GCC -O2 need unroll loop manually */\n        dest[0] = _mm256_add_epi64(_mm256_load_si256(src+0), seed);\n        dest[1] = _mm256_add_epi64(_mm256_load_si256(src+1), seed);\n        dest[2] = _mm256_add_epi64(_mm256_load_si256(src+2), seed);\n        dest[3] = _mm256_add_epi64(_mm256_load_si256(src+3), seed);\n        dest[4] = _mm256_add_epi64(_mm256_load_si256(src+4), seed);\n        dest[5] = _mm256_add_epi64(_mm256_load_si256(src+5), seed);\n    }\n}\n\n#endif\n\n/* x86dispatch always generates SSE2 */\n#if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH)\n\n#ifndef XXH_TARGET_SSE2\n# define XXH_TARGET_SSE2  /* disable attribute target */\n#endif\n\nXXH_FORCE_INLINE XXH_TARGET_SSE2 void\nXXH3_accumulate_512_sse2( void* XXH_RESTRICT acc,\n                    const void* XXH_RESTRICT input,\n                    const void* XXH_RESTRICT secret)\n{\n    /* SSE2 is just a half-scale version of the AVX2 version. */\n    XXH_ASSERT((((size_t)acc) & 15) == 0);\n    {   __m128i* const xacc    =       (__m128i *) acc;\n        /* Unaligned. This is mainly for pointer arithmetic, and because\n         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */\n        const         __m128i* const xinput  = (const __m128i *) input;\n        /* Unaligned. This is mainly for pointer arithmetic, and because\n         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */\n        const         __m128i* const xsecret = (const __m128i *) secret;\n\n        size_t i;\n        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) {\n            /* data_vec    = xinput[i]; */\n            __m128i const data_vec    = _mm_loadu_si128   (xinput+i);\n            /* key_vec     = xsecret[i]; */\n            __m128i const key_vec     = _mm_loadu_si128   (xsecret+i);\n            /* data_key    = data_vec ^ key_vec; */\n            __m128i const data_key    = _mm_xor_si128     (data_vec, key_vec);\n            /* data_key_lo = data_key >> 32; */\n            __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));\n            /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */\n            __m128i const product     = _mm_mul_epu32     (data_key, data_key_lo);\n            /* xacc[i] += swap(data_vec); */\n            __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2));\n            __m128i const sum       = _mm_add_epi64(xacc[i], data_swap);\n            /* xacc[i] += product; */\n            xacc[i] = _mm_add_epi64(product, sum);\n    }   }\n}\nXXH_FORCE_INLINE XXH_TARGET_SSE2 XXH3_ACCUMULATE_TEMPLATE(sse2)\n\nXXH_FORCE_INLINE XXH_TARGET_SSE2 void\nXXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 15) == 0);\n    {   __m128i* const xacc = (__m128i*) acc;\n        /* Unaligned. This is mainly for pointer arithmetic, and because\n         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */\n        const         __m128i* const xsecret = (const __m128i *) secret;\n        const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1);\n\n        size_t i;\n        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) {\n            /* xacc[i] ^= (xacc[i] >> 47) */\n            __m128i const acc_vec     = xacc[i];\n            __m128i const shifted     = _mm_srli_epi64    (acc_vec, 47);\n            __m128i const data_vec    = _mm_xor_si128     (acc_vec, shifted);\n            /* xacc[i] ^= xsecret[i]; */\n            __m128i const key_vec     = _mm_loadu_si128   (xsecret+i);\n            __m128i const data_key    = _mm_xor_si128     (data_vec, key_vec);\n\n            /* xacc[i] *= XXH_PRIME32_1; */\n            __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));\n            __m128i const prod_lo     = _mm_mul_epu32     (data_key, prime32);\n            __m128i const prod_hi     = _mm_mul_epu32     (data_key_hi, prime32);\n            xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32));\n        }\n    }\n}\n\nXXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64)\n{\n    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);\n    (void)(&XXH_writeLE64);\n    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i);\n\n#       if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER <= 1900\n        /* MSVC 32bit mode does not support _mm_set_epi64x before 2015\n         * and some specific variants of 2015 may also lack it */\n        /* Cast to unsigned 64-bit first to avoid signed arithmetic issues */\n        xxh_u64 const seed64_unsigned = (xxh_u64)seed64;\n        xxh_u64 const neg_seed64 = (xxh_u64)(0ULL - seed64_unsigned);\n        __m128i const seed = _mm_set_epi32(\n            (int)(neg_seed64 >> 32),      /* high 32 bits of negated seed */\n            (int)(neg_seed64),            /* low 32 bits of negated seed */\n            (int)(seed64_unsigned >> 32), /* high 32 bits of original seed */\n            (int)(seed64_unsigned)        /* low 32 bits of original seed */\n        );\n#       else\n        __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64);\n#       endif\n        int i;\n\n        const void* const src16 = XXH3_kSecret;\n        __m128i* dst16 = (__m128i*) customSecret;\n#       if defined(__GNUC__) || defined(__clang__)\n        /*\n         * On GCC & Clang, marking 'dest' as modified will cause the compiler:\n         *   - do not extract the secret from sse registers in the internal loop\n         *   - use less common registers, and avoid pushing these reg into stack\n         */\n        XXH_COMPILER_GUARD(dst16);\n#       endif\n        XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */\n        XXH_ASSERT(((size_t)dst16 & 15) == 0);\n\n        for (i=0; i < nbRounds; ++i) {\n            dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed);\n    }   }\n}\n\n#endif\n\n#if (XXH_VECTOR == XXH_NEON)\n\n/* forward declarations for the scalar routines */\nXXH_FORCE_INLINE void\nXXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input,\n                 void const* XXH_RESTRICT secret, size_t lane);\n\nXXH_FORCE_INLINE void\nXXH3_scalarScrambleRound(void* XXH_RESTRICT acc,\n                         void const* XXH_RESTRICT secret, size_t lane);\n\n/*!\n * @internal\n * @brief The bulk processing loop for NEON and WASM SIMD128.\n *\n * The NEON code path is actually partially scalar when running on AArch64. This\n * is to optimize the pipelining and can have up to 15% speedup depending on the\n * CPU, and it also mitigates some GCC codegen issues.\n *\n * @see XXH3_NEON_LANES for configuring this and details about this optimization.\n *\n * NEON's 32-bit to 64-bit long multiply takes a half vector of 32-bit\n * integers instead of the other platforms which mask full 64-bit vectors,\n * so the setup is more complicated than just shifting right.\n *\n * Additionally, there is an optimization for 4 lanes at once noted below.\n *\n * Since, as stated, the most optimal amount of lanes for Cortexes is 6,\n * there needs to be *three* versions of the accumulate operation used\n * for the remaining 2 lanes.\n *\n * WASM's SIMD128 uses SIMDe's arm_neon.h polyfill because the intrinsics overlap\n * nearly perfectly.\n */\n\nXXH_FORCE_INLINE void\nXXH3_accumulate_512_neon( void* XXH_RESTRICT acc,\n                    const void* XXH_RESTRICT input,\n                    const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 15) == 0);\n    XXH_STATIC_ASSERT(XXH3_NEON_LANES > 0 && XXH3_NEON_LANES <= XXH_ACC_NB && XXH3_NEON_LANES % 2 == 0);\n    {   /* GCC for darwin arm64 does not like aliasing here */\n        xxh_aliasing_uint64x2_t* const xacc = (xxh_aliasing_uint64x2_t*) acc;\n        /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */\n        uint8_t const* xinput = (const uint8_t *) input;\n        uint8_t const* xsecret  = (const uint8_t *) secret;\n\n        size_t i;\n#ifdef __wasm_simd128__\n        /*\n         * On WASM SIMD128, Clang emits direct address loads when XXH3_kSecret\n         * is constant propagated, which results in it converting it to this\n         * inside the loop:\n         *\n         *    a = v128.load(XXH3_kSecret +  0 + $secret_offset, offset = 0)\n         *    b = v128.load(XXH3_kSecret + 16 + $secret_offset, offset = 0)\n         *    ...\n         *\n         * This requires a full 32-bit address immediate (and therefore a 6 byte\n         * instruction) as well as an add for each offset.\n         *\n         * Putting an asm guard prevents it from folding (at the cost of losing\n         * the alignment hint), and uses the free offset in `v128.load` instead\n         * of adding secret_offset each time which overall reduces code size by\n         * about a kilobyte and improves performance.\n         */\n        XXH_COMPILER_GUARD(xsecret);\n#endif\n        /* Scalar lanes use the normal scalarRound routine */\n        for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) {\n            XXH3_scalarRound(acc, input, secret, i);\n        }\n        i = 0;\n        /* 4 NEON lanes at a time. */\n        for (; i+1 < XXH3_NEON_LANES / 2; i+=2) {\n            /* data_vec = xinput[i]; */\n            uint64x2_t data_vec_1 = XXH_vld1q_u64(xinput  + (i * 16));\n            uint64x2_t data_vec_2 = XXH_vld1q_u64(xinput  + ((i+1) * 16));\n            /* key_vec  = xsecret[i];  */\n            uint64x2_t key_vec_1  = XXH_vld1q_u64(xsecret + (i * 16));\n            uint64x2_t key_vec_2  = XXH_vld1q_u64(xsecret + ((i+1) * 16));\n            /* data_swap = swap(data_vec) */\n            uint64x2_t data_swap_1 = vextq_u64(data_vec_1, data_vec_1, 1);\n            uint64x2_t data_swap_2 = vextq_u64(data_vec_2, data_vec_2, 1);\n            /* data_key = data_vec ^ key_vec; */\n            uint64x2_t data_key_1 = veorq_u64(data_vec_1, key_vec_1);\n            uint64x2_t data_key_2 = veorq_u64(data_vec_2, key_vec_2);\n\n            /*\n             * If we reinterpret the 64x2 vectors as 32x4 vectors, we can use a\n             * de-interleave operation for 4 lanes in 1 step with `vuzpq_u32` to\n             * get one vector with the low 32 bits of each lane, and one vector\n             * with the high 32 bits of each lane.\n             *\n             * The intrinsic returns a double vector because the original ARMv7-a\n             * instruction modified both arguments in place. AArch64 and SIMD128 emit\n             * two instructions from this intrinsic.\n             *\n             *  [ dk11L | dk11H | dk12L | dk12H ] -> [ dk11L | dk12L | dk21L | dk22L ]\n             *  [ dk21L | dk21H | dk22L | dk22H ] -> [ dk11H | dk12H | dk21H | dk22H ]\n             */\n            uint32x4x2_t unzipped = vuzpq_u32(\n                vreinterpretq_u32_u64(data_key_1),\n                vreinterpretq_u32_u64(data_key_2)\n            );\n            /* data_key_lo = data_key & 0xFFFFFFFF */\n            uint32x4_t data_key_lo = unzipped.val[0];\n            /* data_key_hi = data_key >> 32 */\n            uint32x4_t data_key_hi = unzipped.val[1];\n            /*\n             * Then, we can split the vectors horizontally and multiply which, as for most\n             * widening intrinsics, have a variant that works on both high half vectors\n             * for free on AArch64. A similar instruction is available on SIMD128.\n             *\n             * sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi\n             */\n            uint64x2_t sum_1 = XXH_vmlal_low_u32(data_swap_1, data_key_lo, data_key_hi);\n            uint64x2_t sum_2 = XXH_vmlal_high_u32(data_swap_2, data_key_lo, data_key_hi);\n            /*\n             * Clang reorders\n             *    a += b * c;     // umlal   swap.2d, dkl.2s, dkh.2s\n             *    c += a;         // add     acc.2d, acc.2d, swap.2d\n             * to\n             *    c += a;         // add     acc.2d, acc.2d, swap.2d\n             *    c += b * c;     // umlal   acc.2d, dkl.2s, dkh.2s\n             *\n             * While it would make sense in theory since the addition is faster,\n             * for reasons likely related to umlal being limited to certain NEON\n             * pipelines, this is worse. A compiler guard fixes this.\n             */\n            XXH_COMPILER_GUARD_CLANG_NEON(sum_1);\n            XXH_COMPILER_GUARD_CLANG_NEON(sum_2);\n            /* xacc[i] = acc_vec + sum; */\n            xacc[i]   = vaddq_u64(xacc[i], sum_1);\n            xacc[i+1] = vaddq_u64(xacc[i+1], sum_2);\n        }\n        /* Operate on the remaining NEON lanes 2 at a time. */\n        for (; i < XXH3_NEON_LANES / 2; i++) {\n            /* data_vec = xinput[i]; */\n            uint64x2_t data_vec = XXH_vld1q_u64(xinput  + (i * 16));\n            /* key_vec  = xsecret[i];  */\n            uint64x2_t key_vec  = XXH_vld1q_u64(xsecret + (i * 16));\n            /* acc_vec_2 = swap(data_vec) */\n            uint64x2_t data_swap = vextq_u64(data_vec, data_vec, 1);\n            /* data_key = data_vec ^ key_vec; */\n            uint64x2_t data_key = veorq_u64(data_vec, key_vec);\n            /* For two lanes, just use VMOVN and VSHRN. */\n            /* data_key_lo = data_key & 0xFFFFFFFF; */\n            uint32x2_t data_key_lo = vmovn_u64(data_key);\n            /* data_key_hi = data_key >> 32; */\n            uint32x2_t data_key_hi = vshrn_n_u64(data_key, 32);\n            /* sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi; */\n            uint64x2_t sum = vmlal_u32(data_swap, data_key_lo, data_key_hi);\n            /* Same Clang workaround as before */\n            XXH_COMPILER_GUARD_CLANG_NEON(sum);\n            /* xacc[i] = acc_vec + sum; */\n            xacc[i] = vaddq_u64 (xacc[i], sum);\n        }\n    }\n}\nXXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(neon)\n\nXXH_FORCE_INLINE void\nXXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 15) == 0);\n\n    {   xxh_aliasing_uint64x2_t* xacc       = (xxh_aliasing_uint64x2_t*) acc;\n        uint8_t const* xsecret = (uint8_t const*) secret;\n\n        size_t i;\n        /* WASM uses operator overloads and doesn't need these. */\n#ifndef __wasm_simd128__\n        /* { prime32_1, prime32_1 } */\n        uint32x2_t const kPrimeLo = vdup_n_u32(XXH_PRIME32_1);\n        /* { 0, prime32_1, 0, prime32_1 } */\n        uint32x4_t const kPrimeHi = vreinterpretq_u32_u64(vdupq_n_u64((xxh_u64)XXH_PRIME32_1 << 32));\n#endif\n\n        /* AArch64 uses both scalar and neon at the same time */\n        for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) {\n            XXH3_scalarScrambleRound(acc, secret, i);\n        }\n        for (i=0; i < XXH3_NEON_LANES / 2; i++) {\n            /* xacc[i] ^= (xacc[i] >> 47); */\n            uint64x2_t acc_vec  = xacc[i];\n            uint64x2_t shifted  = vshrq_n_u64(acc_vec, 47);\n            uint64x2_t data_vec = veorq_u64(acc_vec, shifted);\n\n            /* xacc[i] ^= xsecret[i]; */\n            uint64x2_t key_vec  = XXH_vld1q_u64(xsecret + (i * 16));\n            uint64x2_t data_key = veorq_u64(data_vec, key_vec);\n            /* xacc[i] *= XXH_PRIME32_1 */\n#ifdef __wasm_simd128__\n            /* SIMD128 has multiply by u64x2, use it instead of expanding and scalarizing */\n            xacc[i] = data_key * XXH_PRIME32_1;\n#else\n            /*\n             * Expanded version with portable NEON intrinsics\n             *\n             *    lo(x) * lo(y) + (hi(x) * lo(y) << 32)\n             *\n             * prod_hi = hi(data_key) * lo(prime) << 32\n             *\n             * Since we only need 32 bits of this multiply a trick can be used, reinterpreting the vector\n             * as a uint32x4_t and multiplying by { 0, prime, 0, prime } to cancel out the unwanted bits\n             * and avoid the shift.\n             */\n            uint32x4_t prod_hi = vmulq_u32 (vreinterpretq_u32_u64(data_key), kPrimeHi);\n            /* Extract low bits for vmlal_u32  */\n            uint32x2_t data_key_lo = vmovn_u64(data_key);\n            /* xacc[i] = prod_hi + lo(data_key) * XXH_PRIME32_1; */\n            xacc[i] = vmlal_u32(vreinterpretq_u64_u32(prod_hi), data_key_lo, kPrimeLo);\n#endif\n        }\n    }\n}\n#endif\n\n#if (XXH_VECTOR == XXH_VSX)\n\nXXH_FORCE_INLINE void\nXXH3_accumulate_512_vsx(  void* XXH_RESTRICT acc,\n                    const void* XXH_RESTRICT input,\n                    const void* XXH_RESTRICT secret)\n{\n    /* presumed aligned */\n    xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc;\n    xxh_u8 const* const xinput   = (xxh_u8 const*) input;   /* no alignment restriction */\n    xxh_u8 const* const xsecret  = (xxh_u8 const*) secret;    /* no alignment restriction */\n    xxh_u64x2 const v32 = { 32, 32 };\n    size_t i;\n    for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) {\n        /* data_vec = xinput[i]; */\n        xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + 16*i);\n        /* key_vec = xsecret[i]; */\n        xxh_u64x2 const key_vec  = XXH_vec_loadu(xsecret + 16*i);\n        xxh_u64x2 const data_key = data_vec ^ key_vec;\n        /* shuffled = (data_key << 32) | (data_key >> 32); */\n        xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32);\n        /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */\n        xxh_u64x2 const product  = XXH_vec_mulo((xxh_u32x4)data_key, shuffled);\n        /* acc_vec = xacc[i]; */\n        xxh_u64x2 acc_vec        = xacc[i];\n        acc_vec += product;\n\n        /* swap high and low halves */\n#ifdef __s390x__\n        acc_vec += vec_permi(data_vec, data_vec, 2);\n#else\n        acc_vec += vec_xxpermdi(data_vec, data_vec, 2);\n#endif\n        xacc[i] = acc_vec;\n    }\n}\nXXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(vsx)\n\nXXH_FORCE_INLINE void\nXXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 15) == 0);\n\n    {   xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc;\n        const xxh_u8* const xsecret = (const xxh_u8*) secret;\n        /* constants */\n        xxh_u64x2 const v32  = { 32, 32 };\n        xxh_u64x2 const v47 = { 47, 47 };\n        xxh_u32x4 const prime = { XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1 };\n        size_t i;\n        for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) {\n            /* xacc[i] ^= (xacc[i] >> 47); */\n            xxh_u64x2 const acc_vec  = xacc[i];\n            xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47);\n\n            /* xacc[i] ^= xsecret[i]; */\n            xxh_u64x2 const key_vec  = XXH_vec_loadu(xsecret + 16*i);\n            xxh_u64x2 const data_key = data_vec ^ key_vec;\n\n            /* xacc[i] *= XXH_PRIME32_1 */\n            /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF);  */\n            xxh_u64x2 const prod_even  = XXH_vec_mule((xxh_u32x4)data_key, prime);\n            /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32);  */\n            xxh_u64x2 const prod_odd  = XXH_vec_mulo((xxh_u32x4)data_key, prime);\n            xacc[i] = prod_odd + (prod_even << v32);\n    }   }\n}\n\n#endif\n\n#if (XXH_VECTOR == XXH_SVE)\n\nXXH_FORCE_INLINE void\nXXH3_accumulate_512_sve( void* XXH_RESTRICT acc,\n                   const void* XXH_RESTRICT input,\n                   const void* XXH_RESTRICT secret)\n{\n    uint64_t *xacc = (uint64_t *)acc;\n    const uint64_t *xinput = (const uint64_t *)(const void *)input;\n    const uint64_t *xsecret = (const uint64_t *)(const void *)secret;\n    svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1);\n    uint64_t element_count = svcntd();\n    if (element_count >= 8) {\n        svbool_t mask = svptrue_pat_b64(SV_VL8);\n        svuint64_t vacc = svld1_u64(mask, xacc);\n        ACCRND(vacc, 0);\n        svst1_u64(mask, xacc, vacc);\n    } else if (element_count == 2) {   /* sve128 */\n        svbool_t mask = svptrue_pat_b64(SV_VL2);\n        svuint64_t acc0 = svld1_u64(mask, xacc + 0);\n        svuint64_t acc1 = svld1_u64(mask, xacc + 2);\n        svuint64_t acc2 = svld1_u64(mask, xacc + 4);\n        svuint64_t acc3 = svld1_u64(mask, xacc + 6);\n        ACCRND(acc0, 0);\n        ACCRND(acc1, 2);\n        ACCRND(acc2, 4);\n        ACCRND(acc3, 6);\n        svst1_u64(mask, xacc + 0, acc0);\n        svst1_u64(mask, xacc + 2, acc1);\n        svst1_u64(mask, xacc + 4, acc2);\n        svst1_u64(mask, xacc + 6, acc3);\n    } else {\n        svbool_t mask = svptrue_pat_b64(SV_VL4);\n        svuint64_t acc0 = svld1_u64(mask, xacc + 0);\n        svuint64_t acc1 = svld1_u64(mask, xacc + 4);\n        ACCRND(acc0, 0);\n        ACCRND(acc1, 4);\n        svst1_u64(mask, xacc + 0, acc0);\n        svst1_u64(mask, xacc + 4, acc1);\n    }\n}\n\nXXH_FORCE_INLINE void\nXXH3_accumulate_sve(xxh_u64* XXH_RESTRICT acc,\n               const xxh_u8* XXH_RESTRICT input,\n               const xxh_u8* XXH_RESTRICT secret,\n               size_t nbStripes)\n{\n    if (nbStripes != 0) {\n        uint64_t *xacc = (uint64_t *)acc;\n        const uint64_t *xinput = (const uint64_t *)(const void *)input;\n        const uint64_t *xsecret = (const uint64_t *)(const void *)secret;\n        svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1);\n        uint64_t element_count = svcntd();\n        if (element_count >= 8) {\n            svbool_t mask = svptrue_pat_b64(SV_VL8);\n            svuint64_t vacc = svld1_u64(mask, xacc + 0);\n            do {\n                /* svprfd(svbool_t, void *, enum svfprop); */\n                svprfd(mask, xinput + 128, SV_PLDL1STRM);\n                ACCRND(vacc, 0);\n                xinput += 8;\n                xsecret += 1;\n                nbStripes--;\n           } while (nbStripes != 0);\n\n           svst1_u64(mask, xacc + 0, vacc);\n        } else if (element_count == 2) { /* sve128 */\n            svbool_t mask = svptrue_pat_b64(SV_VL2);\n            svuint64_t acc0 = svld1_u64(mask, xacc + 0);\n            svuint64_t acc1 = svld1_u64(mask, xacc + 2);\n            svuint64_t acc2 = svld1_u64(mask, xacc + 4);\n            svuint64_t acc3 = svld1_u64(mask, xacc + 6);\n            do {\n                svprfd(mask, xinput + 128, SV_PLDL1STRM);\n                ACCRND(acc0, 0);\n                ACCRND(acc1, 2);\n                ACCRND(acc2, 4);\n                ACCRND(acc3, 6);\n                xinput += 8;\n                xsecret += 1;\n                nbStripes--;\n           } while (nbStripes != 0);\n\n           svst1_u64(mask, xacc + 0, acc0);\n           svst1_u64(mask, xacc + 2, acc1);\n           svst1_u64(mask, xacc + 4, acc2);\n           svst1_u64(mask, xacc + 6, acc3);\n        } else {\n            svbool_t mask = svptrue_pat_b64(SV_VL4);\n            svuint64_t acc0 = svld1_u64(mask, xacc + 0);\n            svuint64_t acc1 = svld1_u64(mask, xacc + 4);\n            do {\n                svprfd(mask, xinput + 128, SV_PLDL1STRM);\n                ACCRND(acc0, 0);\n                ACCRND(acc1, 4);\n                xinput += 8;\n                xsecret += 1;\n                nbStripes--;\n           } while (nbStripes != 0);\n\n           svst1_u64(mask, xacc + 0, acc0);\n           svst1_u64(mask, xacc + 4, acc1);\n       }\n    }\n}\n\n#endif\n\n#if (XXH_VECTOR == XXH_LSX)\n#define _LSX_SHUFFLE(z, y, x, w) (((z) << 6) | ((y) << 4) | ((x) << 2) | (w))\n\nXXH_FORCE_INLINE void\nXXH3_accumulate_512_lsx( void* XXH_RESTRICT acc,\n                    const void* XXH_RESTRICT input,\n                    const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 15) == 0);\n    {\n        __m128i* const xacc    =       (__m128i *) acc;\n        const __m128i* const xinput  = (const __m128i *) input;\n        const __m128i* const xsecret = (const __m128i *) secret;\n        size_t i;\n\n        for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) {\n            /* data_vec = xinput[i]; */\n            __m128i const data_vec = __lsx_vld(xinput + i, 0);\n            /* key_vec = xsecret[i]; */\n            __m128i const key_vec = __lsx_vld(xsecret + i, 0);\n            /* data_key = data_vec ^ key_vec; */\n            __m128i const data_key = __lsx_vxor_v(data_vec, key_vec);\n            /* data_key_lo = data_key >> 32; */\n            __m128i const data_key_lo = __lsx_vsrli_d(data_key, 32);\n            // __m128i const data_key_lo = __lsx_vsrli_d(data_key, 32);\n            /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */\n            __m128i const product = __lsx_vmulwev_d_wu(data_key, data_key_lo);\n            /* xacc[i] += swap(data_vec); */\n            __m128i const data_swap = __lsx_vshuf4i_w(data_vec, _LSX_SHUFFLE(1, 0, 3, 2));\n            __m128i const sum = __lsx_vadd_d(xacc[i], data_swap);\n            /* xacc[i] += product; */\n            xacc[i] = __lsx_vadd_d(product, sum);\n        }\n    }\n}\nXXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(lsx)\n\nXXH_FORCE_INLINE void\nXXH3_scrambleAcc_lsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 15) == 0);\n    {\n        __m128i* const xacc = (__m128i*) acc;\n        const __m128i* const xsecret = (const __m128i *) secret;\n        const __m128i prime32 = __lsx_vreplgr2vr_d(XXH_PRIME32_1);\n        size_t i;\n\n        for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) {\n            /* xacc[i] ^= (xacc[i] >> 47) */\n            __m128i const acc_vec = xacc[i];\n            __m128i const shifted = __lsx_vsrli_d(acc_vec, 47);\n            __m128i const data_vec = __lsx_vxor_v(acc_vec, shifted);\n            /* xacc[i] ^= xsecret[i]; */\n            __m128i const key_vec = __lsx_vld(xsecret + i, 0);\n            __m128i const data_key = __lsx_vxor_v(data_vec, key_vec);\n\n            /* xacc[i] *= XXH_PRIME32_1; */\n            xacc[i] = __lsx_vmul_d(data_key, prime32);\n        }\n    }\n}\n\n#endif\n\n#if (XXH_VECTOR == XXH_LASX)\n#define _LASX_SHUFFLE(z, y, x, w) (((z) << 6) | ((y) << 4) | ((x) << 2) | (w))\n\nXXH_FORCE_INLINE void\nXXH3_accumulate_512_lasx( void* XXH_RESTRICT acc,\n                    const void* XXH_RESTRICT input,\n                    const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 31) == 0);\n    {\n        size_t i;\n        __m256i* const xacc    =       (__m256i *) acc;\n        const __m256i* const xinput  = (const __m256i *) input;\n        const __m256i* const xsecret = (const __m256i *) secret;\n\n        for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m256i); i++) {\n            /* data_vec = xinput[i]; */\n            __m256i const data_vec = __lasx_xvld(xinput + i, 0);\n            /* key_vec = xsecret[i]; */\n            __m256i const key_vec = __lasx_xvld(xsecret + i, 0);\n            /* data_key = data_vec ^ key_vec; */\n            __m256i const data_key = __lasx_xvxor_v(data_vec, key_vec);\n            /* data_key_lo = data_key >> 32; */\n            __m256i const data_key_lo = __lasx_xvsrli_d(data_key, 32);\n            // __m256i const data_key_lo = __lasx_xvsrli_d(data_key, 32);\n            /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */\n            __m256i const product = __lasx_xvmulwev_d_wu(data_key, data_key_lo);\n            /* xacc[i] += swap(data_vec); */\n            __m256i const data_swap = __lasx_xvshuf4i_w(data_vec, _LASX_SHUFFLE(1, 0, 3, 2));\n            __m256i const sum = __lasx_xvadd_d(xacc[i], data_swap);\n            /* xacc[i] += product; */\n            xacc[i] = __lasx_xvadd_d(product, sum);\n        }\n    }\n}\nXXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(lasx)\n\nXXH_FORCE_INLINE void\nXXH3_scrambleAcc_lasx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 31) == 0);\n    {\n        __m256i* const xacc = (__m256i*) acc;\n        const __m256i* const xsecret = (const __m256i *) secret;\n        const __m256i prime32 = __lasx_xvreplgr2vr_d(XXH_PRIME32_1);\n        size_t i;\n\n        for (i = 0; i < XXH_STRIPE_LEN / sizeof(__m256i); i++) {\n            /* xacc[i] ^= (xacc[i] >> 47) */\n            __m256i const acc_vec = xacc[i];\n            __m256i const shifted = __lasx_xvsrli_d(acc_vec, 47);\n            __m256i const data_vec = __lasx_xvxor_v(acc_vec, shifted);\n            /* xacc[i] ^= xsecret[i]; */\n            __m256i const key_vec = __lasx_xvld(xsecret + i, 0);\n            __m256i const data_key = __lasx_xvxor_v(data_vec, key_vec);\n\n            /* xacc[i] *= XXH_PRIME32_1; */\n            xacc[i] = __lasx_xvmul_d(data_key, prime32);\n        }\n    }\n}\n\n#endif\n\n#if (XXH_VECTOR == XXH_RVV)\n    #define XXH_CONCAT2(X, Y) X ## Y\n    #define XXH_CONCAT(X, Y) XXH_CONCAT2(X, Y)\n#if ((defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 13) || \\\n        (defined(__clang__) && __clang_major__ < 16))\n    #define XXH_RVOP(op) op\n    #define XXH_RVCAST(op) XXH_CONCAT(vreinterpret_v_, op)\n#else\n    #define XXH_RVOP(op) XXH_CONCAT(__riscv_, op)\n    #define XXH_RVCAST(op) XXH_CONCAT(__riscv_vreinterpret_v_, op)\n#endif\nXXH_FORCE_INLINE void\nXXH3_accumulate_512_rvv(  void* XXH_RESTRICT acc,\n                    const void* XXH_RESTRICT input,\n                    const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 63) == 0);\n    {\n        // Try to set vector lenght to 512 bits.\n        // If this length is unavailable, then maximum available will be used\n        size_t vl = XXH_RVOP(vsetvl_e64m2)(8);\n\n        uint64_t*       xacc    = (uint64_t*) acc;\n        const uint64_t* xinput  = (const uint64_t*) input;\n        const uint64_t* xsecret = (const uint64_t*) secret;\n        static const uint64_t swap_mask[16] = {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14};\n        vuint64m2_t xswap_mask = XXH_RVOP(vle64_v_u64m2)(swap_mask, vl);\n\n        size_t i;\n        for (i = 0; i < XXH_STRIPE_LEN/8; i += vl) {\n            /* data_vec = xinput[i]; */\n            vuint64m2_t data_vec = XXH_RVCAST(u8m2_u64m2)(XXH_RVOP(vle8_v_u8m2)((const uint8_t*)(xinput + i), vl * 8));\n            /* key_vec = xsecret[i]; */\n            vuint64m2_t key_vec = XXH_RVCAST(u8m2_u64m2)(XXH_RVOP(vle8_v_u8m2)((const uint8_t*)(xsecret + i), vl * 8));\n            /* acc_vec = xacc[i]; */\n            vuint64m2_t acc_vec = XXH_RVOP(vle64_v_u64m2)(xacc + i, vl);\n            /* data_key = data_vec ^ key_vec; */\n            vuint64m2_t data_key = XXH_RVOP(vxor_vv_u64m2)(data_vec, key_vec, vl);\n            /* data_key_hi = data_key >> 32; */\n            vuint64m2_t data_key_hi = XXH_RVOP(vsrl_vx_u64m2)(data_key, 32, vl);\n            /* data_key_lo = data_key & 0xffffffff; */\n            vuint64m2_t data_key_lo = XXH_RVOP(vand_vx_u64m2)(data_key, 0xffffffff, vl);\n            /* swap high and low halves */\n            vuint64m2_t data_swap = XXH_RVOP(vrgather_vv_u64m2)(data_vec, xswap_mask, vl);\n            /* acc_vec += data_key_lo * data_key_hi; */\n            acc_vec = XXH_RVOP(vmacc_vv_u64m2)(acc_vec, data_key_lo, data_key_hi, vl);\n            /* acc_vec += data_swap; */\n            acc_vec = XXH_RVOP(vadd_vv_u64m2)(acc_vec, data_swap, vl);\n            /* xacc[i] = acc_vec; */\n            XXH_RVOP(vse64_v_u64m2)(xacc + i, acc_vec, vl);\n        }\n    }\n}\n\nXXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(rvv)\n\nXXH_FORCE_INLINE void\nXXH3_scrambleAcc_rvv(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    XXH_ASSERT((((size_t)acc) & 15) == 0);\n    {\n        size_t count = XXH_STRIPE_LEN/8;\n        uint64_t* xacc = (uint64_t*)acc;\n        const uint8_t* xsecret = (const uint8_t *)secret;\n        size_t vl;\n        for (; count > 0; count -= vl, xacc += vl, xsecret += vl*8) {\n            vl = XXH_RVOP(vsetvl_e64m2)(count);\n            {\n                /* key_vec = xsecret[i]; */\n                vuint64m2_t key_vec = XXH_RVCAST(u8m2_u64m2)(XXH_RVOP(vle8_v_u8m2)(xsecret, vl*8));\n                /* acc_vec = xacc[i]; */\n                vuint64m2_t acc_vec = XXH_RVOP(vle64_v_u64m2)(xacc, vl);\n                /* acc_vec ^= acc_vec >> 47; */\n                vuint64m2_t vsrl = XXH_RVOP(vsrl_vx_u64m2)(acc_vec, 47, vl);\n                acc_vec = XXH_RVOP(vxor_vv_u64m2)(acc_vec, vsrl, vl);\n                /* acc_vec ^= key_vec; */\n                acc_vec = XXH_RVOP(vxor_vv_u64m2)(acc_vec, key_vec, vl);\n                /* acc_vec *= XXH_PRIME32_1; */\n                acc_vec = XXH_RVOP(vmul_vx_u64m2)(acc_vec, XXH_PRIME32_1, vl);\n                /* xacc[i] *= acc_vec; */\n                XXH_RVOP(vse64_v_u64m2)(xacc, acc_vec, vl);\n            }\n        }\n    }\n}\n\nXXH_FORCE_INLINE void\nXXH3_initCustomSecret_rvv(void* XXH_RESTRICT customSecret, xxh_u64 seed64)\n{\n    XXH_STATIC_ASSERT(XXH_SEC_ALIGN >= 8);\n    XXH_ASSERT(((size_t)customSecret & 7) == 0);\n    (void)(&XXH_writeLE64);\n    {\n        size_t count = XXH_SECRET_DEFAULT_SIZE/8;\n        size_t vl;\n        size_t VLMAX = XXH_RVOP(vsetvlmax_e64m2)();\n        int64_t* cSecret = (int64_t*)customSecret;\n        const int64_t* kSecret = (const int64_t*)(const void*)XXH3_kSecret;\n\n#if __riscv_v_intrinsic >= 1000000\n        // ratified v1.0 intrinics version\n        vbool32_t mneg = XXH_RVCAST(u8m1_b32)(\n                         XXH_RVOP(vmv_v_x_u8m1)(0xaa, XXH_RVOP(vsetvlmax_e8m1)()));\n#else\n        // support pre-ratification intrinics, which lack mask to vector casts\n        size_t vlmax = XXH_RVOP(vsetvlmax_e8m1)();\n        vbool32_t mneg = XXH_RVOP(vmseq_vx_u8mf4_b32)(\n                         XXH_RVOP(vand_vx_u8mf4)(\n                         XXH_RVOP(vid_v_u8mf4)(vlmax), 1, vlmax), 1, vlmax);\n#endif\n        vint64m2_t seed = XXH_RVOP(vmv_v_x_i64m2)((int64_t)seed64, VLMAX);\n        seed = XXH_RVOP(vneg_v_i64m2_mu)(mneg, seed, seed, VLMAX);\n\n        for (; count > 0; count -= vl, cSecret += vl, kSecret += vl) {\n            /* make sure vl=VLMAX until last iteration */\n            vl = XXH_RVOP(vsetvl_e64m2)(count < VLMAX ? count : VLMAX);\n            {\n                vint64m2_t src = XXH_RVOP(vle64_v_i64m2)(kSecret, vl);\n                vint64m2_t res = XXH_RVOP(vadd_vv_i64m2)(src, seed, vl);\n                XXH_RVOP(vse64_v_i64m2)(cSecret, res, vl);\n            }\n        }\n    }\n}\n#endif\n\n\n/* scalar variants - universal */\n\n#if defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__))\n/*\n * In XXH3_scalarRound(), GCC and Clang have a similar codegen issue, where they\n * emit an excess mask and a full 64-bit multiply-add (MADD X-form).\n *\n * While this might not seem like much, as AArch64 is a 64-bit architecture, only\n * big Cortex designs have a full 64-bit multiplier.\n *\n * On the little cores, the smaller 32-bit multiplier is used, and full 64-bit\n * multiplies expand to 2-3 multiplies in microcode. This has a major penalty\n * of up to 4 latency cycles and 2 stall cycles in the multiply pipeline.\n *\n * Thankfully, AArch64 still provides the 32-bit long multiply-add (UMADDL) which does\n * not have this penalty and does the mask automatically.\n */\nXXH_FORCE_INLINE xxh_u64\nXXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc)\n{\n    xxh_u64 ret;\n    /* note: %x = 64-bit register, %w = 32-bit register */\n    __asm__(\"umaddl %x0, %w1, %w2, %x3\" : \"=r\" (ret) : \"r\" (lhs), \"r\" (rhs), \"r\" (acc));\n    return ret;\n}\n#else\nXXH_FORCE_INLINE xxh_u64\nXXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc)\n{\n    return XXH_mult32to64((xxh_u32)lhs, (xxh_u32)rhs) + acc;\n}\n#endif\n\n/*!\n * @internal\n * @brief Scalar round for @ref XXH3_accumulate_512_scalar().\n *\n * This is extracted to its own function because the NEON path uses a combination\n * of NEON and scalar.\n */\nXXH_FORCE_INLINE void\nXXH3_scalarRound(void* XXH_RESTRICT acc,\n                 void const* XXH_RESTRICT input,\n                 void const* XXH_RESTRICT secret,\n                 size_t lane)\n{\n    xxh_u64* xacc = (xxh_u64*) acc;\n    xxh_u8 const* xinput  = (xxh_u8 const*) input;\n    xxh_u8 const* xsecret = (xxh_u8 const*) secret;\n    XXH_ASSERT(lane < XXH_ACC_NB);\n    XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0);\n    {\n        xxh_u64 const data_val = XXH_readLE64(xinput + lane * 8);\n        xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + lane * 8);\n        xacc[lane ^ 1] += data_val; /* swap adjacent lanes */\n        xacc[lane] = XXH_mult32to64_add64(data_key /* & 0xFFFFFFFF */, data_key >> 32, xacc[lane]);\n    }\n}\n\n/*!\n * @internal\n * @brief Processes a 64 byte block of data using the scalar path.\n */\nXXH_FORCE_INLINE void\nXXH3_accumulate_512_scalar(void* XXH_RESTRICT acc,\n                     const void* XXH_RESTRICT input,\n                     const void* XXH_RESTRICT secret)\n{\n    size_t i;\n    /* ARM GCC refuses to unroll this loop, resulting in a 24% slowdown on ARMv6. */\n#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 8 \\\n  && (defined(__arm__) || defined(__thumb2__)) \\\n  && defined(__ARM_FEATURE_UNALIGNED) /* no unaligned access just wastes bytes */ \\\n  && XXH_SIZE_OPT <= 0\n#  pragma GCC unroll 8\n#endif\n    for (i=0; i < XXH_ACC_NB; i++) {\n        XXH3_scalarRound(acc, input, secret, i);\n    }\n}\nXXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(scalar)\n\n/*!\n * @internal\n * @brief Scalar scramble step for @ref XXH3_scrambleAcc_scalar().\n *\n * This is extracted to its own function because the NEON path uses a combination\n * of NEON and scalar.\n */\nXXH_FORCE_INLINE void\nXXH3_scalarScrambleRound(void* XXH_RESTRICT acc,\n                         void const* XXH_RESTRICT secret,\n                         size_t lane)\n{\n    xxh_u64* const xacc = (xxh_u64*) acc;   /* presumed aligned */\n    const xxh_u8* const xsecret = (const xxh_u8*) secret;   /* no alignment restriction */\n    XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0);\n    XXH_ASSERT(lane < XXH_ACC_NB);\n    {\n        xxh_u64 const key64 = XXH_readLE64(xsecret + lane * 8);\n        xxh_u64 acc64 = xacc[lane];\n        acc64 = XXH_xorshift64(acc64, 47);\n        acc64 ^= key64;\n        acc64 *= XXH_PRIME32_1;\n        xacc[lane] = acc64;\n    }\n}\n\n/*!\n * @internal\n * @brief Scrambles the accumulators after a large chunk has been read\n */\nXXH_FORCE_INLINE void\nXXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)\n{\n    size_t i;\n    for (i=0; i < XXH_ACC_NB; i++) {\n        XXH3_scalarScrambleRound(acc, secret, i);\n    }\n}\n\nXXH_FORCE_INLINE void\nXXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64)\n{\n    /*\n     * We need a separate pointer for the hack below,\n     * which requires a non-const pointer.\n     * Any decent compiler will optimize this out otherwise.\n     */\n    const xxh_u8* kSecretPtr = XXH3_kSecret;\n    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);\n\n#if defined(__GNUC__) && defined(__aarch64__)\n    /*\n     * UGLY HACK:\n     * GCC and Clang generate a bunch of MOV/MOVK pairs for aarch64, and they are\n     * placed sequentially, in order, at the top of the unrolled loop.\n     *\n     * While MOVK is great for generating constants (2 cycles for a 64-bit\n     * constant compared to 4 cycles for LDR), it fights for bandwidth with\n     * the arithmetic instructions.\n     *\n     *   I   L   S\n     * MOVK\n     * MOVK\n     * MOVK\n     * MOVK\n     * ADD\n     * SUB      STR\n     *          STR\n     * By forcing loads from memory (as the asm line causes the compiler to assume\n     * that XXH3_kSecretPtr has been changed), the pipelines are used more\n     * efficiently:\n     *   I   L   S\n     *      LDR\n     *  ADD LDR\n     *  SUB     STR\n     *          STR\n     *\n     * See XXH3_NEON_LANES for details on the pipeline.\n     *\n     * XXH3_64bits_withSeed, len == 256, Snapdragon 835\n     *   without hack: 2654.4 MB/s\n     *   with hack:    3202.9 MB/s\n     */\n    XXH_COMPILER_GUARD(kSecretPtr);\n#endif\n    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16;\n        int i;\n        for (i=0; i < nbRounds; i++) {\n            /*\n             * The asm hack causes the compiler to assume that kSecretPtr aliases with\n             * customSecret, and on aarch64, this prevented LDP from merging two\n             * loads together for free. Putting the loads together before the stores\n             * properly generates LDP.\n             */\n            xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i)     + seed64;\n            xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64;\n            XXH_writeLE64((xxh_u8*)customSecret + 16*i,     lo);\n            XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi);\n    }   }\n}\n\n\ntypedef void (*XXH3_f_accumulate)(xxh_u64* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, size_t);\ntypedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*);\ntypedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64);\n\n\n#if (XXH_VECTOR == XXH_AVX512)\n\n#define XXH3_accumulate_512 XXH3_accumulate_512_avx512\n#define XXH3_accumulate     XXH3_accumulate_avx512\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_avx512\n#define XXH3_initCustomSecret XXH3_initCustomSecret_avx512\n\n#elif (XXH_VECTOR == XXH_AVX2)\n\n#define XXH3_accumulate_512 XXH3_accumulate_512_avx2\n#define XXH3_accumulate     XXH3_accumulate_avx2\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_avx2\n#define XXH3_initCustomSecret XXH3_initCustomSecret_avx2\n\n#elif (XXH_VECTOR == XXH_SSE2)\n\n#define XXH3_accumulate_512 XXH3_accumulate_512_sse2\n#define XXH3_accumulate     XXH3_accumulate_sse2\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_sse2\n#define XXH3_initCustomSecret XXH3_initCustomSecret_sse2\n\n#elif (XXH_VECTOR == XXH_NEON)\n\n#define XXH3_accumulate_512 XXH3_accumulate_512_neon\n#define XXH3_accumulate     XXH3_accumulate_neon\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_neon\n#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar\n\n#elif (XXH_VECTOR == XXH_VSX)\n\n#define XXH3_accumulate_512 XXH3_accumulate_512_vsx\n#define XXH3_accumulate     XXH3_accumulate_vsx\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_vsx\n#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar\n\n#elif (XXH_VECTOR == XXH_SVE)\n#define XXH3_accumulate_512 XXH3_accumulate_512_sve\n#define XXH3_accumulate     XXH3_accumulate_sve\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_scalar\n#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar\n\n#elif (XXH_VECTOR == XXH_LASX)\n#define XXH3_accumulate_512 XXH3_accumulate_512_lasx\n#define XXH3_accumulate     XXH3_accumulate_lasx\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_lasx\n#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar\n\n#elif (XXH_VECTOR == XXH_LSX)\n#define XXH3_accumulate_512 XXH3_accumulate_512_lsx\n#define XXH3_accumulate     XXH3_accumulate_lsx\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_lsx\n#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar\n\n#elif (XXH_VECTOR == XXH_RVV)\n#define XXH3_accumulate_512 XXH3_accumulate_512_rvv\n#define XXH3_accumulate     XXH3_accumulate_rvv\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_rvv\n#define XXH3_initCustomSecret XXH3_initCustomSecret_rvv\n\n#else /* scalar */\n\n#define XXH3_accumulate_512 XXH3_accumulate_512_scalar\n#define XXH3_accumulate     XXH3_accumulate_scalar\n#define XXH3_scrambleAcc    XXH3_scrambleAcc_scalar\n#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar\n\n#endif\n\n#if XXH_SIZE_OPT >= 1 /* don't do SIMD for initialization */\n#  undef XXH3_initCustomSecret\n#  define XXH3_initCustomSecret XXH3_initCustomSecret_scalar\n#endif\n\nXXH_FORCE_INLINE void\nXXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc,\n                      const xxh_u8* XXH_RESTRICT input, size_t len,\n                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,\n                            XXH3_f_accumulate f_acc,\n                            XXH3_f_scrambleAcc f_scramble)\n{\n    size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE;\n    size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock;\n    size_t const nb_blocks = (len - 1) / block_len;\n\n    size_t n;\n\n    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);\n\n    for (n = 0; n < nb_blocks; n++) {\n        f_acc(acc, input + n*block_len, secret, nbStripesPerBlock);\n        f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN);\n    }\n\n    /* last partial block */\n    XXH_ASSERT(len > XXH_STRIPE_LEN);\n    {   size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN;\n        XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE));\n        f_acc(acc, input + nb_blocks*block_len, secret, nbStripes);\n\n        /* last stripe */\n        {   const xxh_u8* const p = input + len - XXH_STRIPE_LEN;\n#define XXH_SECRET_LASTACC_START 7  /* not aligned on 8, last secret is different from acc & scrambler */\n            XXH3_accumulate_512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START);\n    }   }\n}\n\nXXH_FORCE_INLINE xxh_u64\nXXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret)\n{\n    return XXH3_mul128_fold64(\n               acc[0] ^ XXH_readLE64(secret),\n               acc[1] ^ XXH_readLE64(secret+8) );\n}\n\nstatic XXH_PUREF XXH64_hash_t\nXXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start)\n{\n    xxh_u64 result64 = start;\n    size_t i = 0;\n\n    for (i = 0; i < 4; i++) {\n        result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i);\n#if defined(__clang__)                                /* Clang */ \\\n    && (defined(__arm__) || defined(__thumb__))       /* ARMv7 */ \\\n    && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */  \\\n    && !defined(XXH_ENABLE_AUTOVECTORIZE)             /* Define to disable */\n        /*\n         * UGLY HACK:\n         * Prevent autovectorization on Clang ARMv7-a. Exact same problem as\n         * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b.\n         * XXH3_64bits, len == 256, Snapdragon 835:\n         *   without hack: 2063.7 MB/s\n         *   with hack:    2560.7 MB/s\n         */\n        XXH_COMPILER_GUARD(result64);\n#endif\n    }\n\n    return XXH3_avalanche(result64);\n}\n\n/* do not align on 8, so that the secret is different from the accumulator */\n#define XXH_SECRET_MERGEACCS_START 11\n\nstatic XXH_PUREF XXH64_hash_t\nXXH3_finalizeLong_64b(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 len)\n{\n    return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, len * XXH_PRIME64_1);\n}\n\n#define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \\\n                        XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 }\n\nXXH_FORCE_INLINE XXH64_hash_t\nXXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len,\n                           const void* XXH_RESTRICT secret, size_t secretSize,\n                           XXH3_f_accumulate f_acc,\n                           XXH3_f_scrambleAcc f_scramble)\n{\n    XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;\n\n    XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc, f_scramble);\n\n    /* converge into final hash */\n    XXH_STATIC_ASSERT(sizeof(acc) == 64);\n    XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);\n    return XXH3_finalizeLong_64b(acc, (const xxh_u8*)secret, (xxh_u64)len);\n}\n\n/*\n * It's important for performance to transmit secret's size (when it's static)\n * so that the compiler can properly optimize the vectorized loop.\n * This makes a big performance difference for \"medium\" keys (<1 KB) when using AVX instruction set.\n * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE\n * breaks -Og, this is XXH_NO_INLINE.\n */\nXXH3_WITH_SECRET_INLINE XXH64_hash_t\nXXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len,\n                             XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)\n{\n    (void)seed64;\n    return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate, XXH3_scrambleAcc);\n}\n\n/*\n * It's preferable for performance that XXH3_hashLong is not inlined,\n * as it results in a smaller function for small data, easier to the instruction cache.\n * Note that inside this no_inline function, we do inline the internal loop,\n * and provide a statically defined secret size to allow optimization of vector loop.\n */\nXXH_NO_INLINE XXH_PUREF XXH64_hash_t\nXXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len,\n                          XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)\n{\n    (void)seed64; (void)secret; (void)secretLen;\n    return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate, XXH3_scrambleAcc);\n}\n\n/*\n * XXH3_hashLong_64b_withSeed():\n * Generate a custom key based on alteration of default XXH3_kSecret with the seed,\n * and then use this key for long mode hashing.\n *\n * This operation is decently fast but nonetheless costs a little bit of time.\n * Try to avoid it whenever possible (typically when seed==0).\n *\n * It's important for performance that XXH3_hashLong is not inlined. Not sure\n * why (uop cache maybe?), but the difference is large and easily measurable.\n */\nXXH_FORCE_INLINE XXH64_hash_t\nXXH3_hashLong_64b_withSeed_internal(const void* input, size_t len,\n                                    XXH64_hash_t seed,\n                                    XXH3_f_accumulate f_acc,\n                                    XXH3_f_scrambleAcc f_scramble,\n                                    XXH3_f_initCustomSecret f_initSec)\n{\n#if XXH_SIZE_OPT <= 0\n    if (seed == 0)\n        return XXH3_hashLong_64b_internal(input, len,\n                                          XXH3_kSecret, sizeof(XXH3_kSecret),\n                                          f_acc, f_scramble);\n#endif\n    {   XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];\n        f_initSec(secret, seed);\n        return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret),\n                                          f_acc, f_scramble);\n    }\n}\n\n/*\n * It's important for performance that XXH3_hashLong is not inlined.\n */\nXXH_NO_INLINE XXH64_hash_t\nXXH3_hashLong_64b_withSeed(const void* XXH_RESTRICT input, size_t len,\n                           XXH64_hash_t seed, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)\n{\n    (void)secret; (void)secretLen;\n    return XXH3_hashLong_64b_withSeed_internal(input, len, seed,\n                XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret);\n}\n\n\ntypedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t,\n                                          XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t);\n\nXXH_FORCE_INLINE XXH64_hash_t\nXXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len,\n                     XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen,\n                     XXH3_hashLong64_f f_hashLong)\n{\n    XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);\n    /*\n     * If an action is to be taken if `secretLen` condition is not respected,\n     * it should be done here.\n     * For now, it's a contract pre-condition.\n     * Adding a check and a branch here would cost performance at every hash.\n     * Also, note that function signature doesn't offer room to return an error.\n     */\n    if (len <= 16)\n        return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64);\n    if (len <= 128)\n        return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);\n    if (len <= XXH3_MIDSIZE_MAX)\n        return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);\n    return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen);\n}\n\n\n/* ===   Public entry point   === */\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length)\n{\n    return XXH3_64bits_internal(input, length, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH64_hash_t\nXXH3_64bits_withSecret(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize)\n{\n    return XXH3_64bits_internal(input, length, 0, secret, secretSize, XXH3_hashLong_64b_withSecret);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH64_hash_t\nXXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed)\n{\n    return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed);\n}\n\nXXH_PUBLIC_API XXH64_hash_t\nXXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed)\n{\n    if (length <= XXH3_MIDSIZE_MAX)\n        return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL);\n    return XXH3_hashLong_64b_withSecret(input, length, seed, (const xxh_u8*)secret, secretSize);\n}\n\n\n/* ===   XXH3 streaming   === */\n#ifndef XXH_NO_STREAM\n/*\n * Malloc's a pointer that is always aligned to @align.\n *\n * This must be freed with `XXH_alignedFree()`.\n *\n * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte\n * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2\n * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON.\n *\n * This underalignment previously caused a rather obvious crash which went\n * completely unnoticed due to XXH3_createState() not actually being tested.\n * Credit to RedSpah for noticing this bug.\n *\n * The alignment is done manually: Functions like posix_memalign or _mm_malloc\n * are avoided: To maintain portability, we would have to write a fallback\n * like this anyways, and besides, testing for the existence of library\n * functions without relying on external build tools is impossible.\n *\n * The method is simple: Overallocate, manually align, and store the offset\n * to the original behind the returned pointer.\n *\n * Align must be a power of 2 and 8 <= align <= 128.\n */\nstatic XXH_MALLOCF void* XXH_alignedMalloc(size_t s, size_t align)\n{\n    XXH_ASSERT(align <= 128 && align >= 8); /* range check */\n    XXH_ASSERT((align & (align-1)) == 0);   /* power of 2 */\n    XXH_ASSERT(s != 0 && s < (s + align));  /* empty/overflow */\n    {   /* Overallocate to make room for manual realignment and an offset byte */\n        xxh_u8* base = (xxh_u8*)XXH_malloc(s + align);\n        if (base != NULL) {\n            /*\n             * Get the offset needed to align this pointer.\n             *\n             * Even if the returned pointer is aligned, there will always be\n             * at least one byte to store the offset to the original pointer.\n             */\n            size_t offset = align - ((size_t)base & (align - 1)); /* base % align */\n            /* Add the offset for the now-aligned pointer */\n            xxh_u8* ptr = base + offset;\n\n            XXH_ASSERT((size_t)ptr % align == 0);\n\n            /* Store the offset immediately before the returned pointer. */\n            ptr[-1] = (xxh_u8)offset;\n            return ptr;\n        }\n        return NULL;\n    }\n}\n/*\n * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass\n * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout.\n */\nstatic void XXH_alignedFree(void* p)\n{\n    if (p != NULL) {\n        xxh_u8* ptr = (xxh_u8*)p;\n        /* Get the offset byte we added in XXH_malloc. */\n        xxh_u8 offset = ptr[-1];\n        /* Free the original malloc'd pointer */\n        xxh_u8* base = ptr - offset;\n        XXH_free(base);\n    }\n}\n/*! @ingroup XXH3_family */\n/*!\n * @brief Allocate an @ref XXH3_state_t.\n *\n * @return An allocated pointer of @ref XXH3_state_t on success.\n * @return `NULL` on failure.\n *\n * @note Must be freed with XXH3_freeState().\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH3_state_t* XXH3_createState(void)\n{\n    XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64);\n    if (state==NULL) return NULL;\n    XXH3_INITSTATE(state);\n    return state;\n}\n\n/*! @ingroup XXH3_family */\n/*!\n * @brief Frees an @ref XXH3_state_t.\n *\n * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState().\n *\n * @return @ref XXH_OK.\n *\n * @note Must be allocated with XXH3_createState().\n *\n * @see @ref streaming_example \"Streaming Example\"\n */\nXXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr)\n{\n    XXH_alignedFree(statePtr);\n    return XXH_OK;\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API void\nXXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state)\n{\n    XXH_memcpy(dst_state, src_state, sizeof(*dst_state));\n}\n\nstatic void\nXXH3_reset_internal(XXH3_state_t* statePtr,\n                    XXH64_hash_t seed,\n                    const void* secret, size_t secretSize)\n{\n    size_t const initStart = offsetof(XXH3_state_t, bufferedSize);\n    size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart;\n    XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart);\n    XXH_ASSERT(statePtr != NULL);\n    /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */\n    XXH_memset((char*)statePtr + initStart, 0, initLength);\n    statePtr->acc[0] = XXH_PRIME32_3;\n    statePtr->acc[1] = XXH_PRIME64_1;\n    statePtr->acc[2] = XXH_PRIME64_2;\n    statePtr->acc[3] = XXH_PRIME64_3;\n    statePtr->acc[4] = XXH_PRIME64_4;\n    statePtr->acc[5] = XXH_PRIME32_2;\n    statePtr->acc[6] = XXH_PRIME64_5;\n    statePtr->acc[7] = XXH_PRIME32_1;\n    statePtr->seed = seed;\n    statePtr->useSeed = (seed != 0);\n    statePtr->extSecret = (const unsigned char*)secret;\n    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);\n    statePtr->secretLimit = secretSize - XXH_STRIPE_LEN;\n    statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE;\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr)\n{\n    if (statePtr == NULL) return XXH_ERROR;\n    XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE);\n    return XXH_OK;\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize)\n{\n    if (statePtr == NULL) return XXH_ERROR;\n    XXH3_reset_internal(statePtr, 0, secret, secretSize);\n    if (secret == NULL) return XXH_ERROR;\n    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;\n    return XXH_OK;\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed)\n{\n    if (statePtr == NULL) return XXH_ERROR;\n    if (seed==0) return XXH3_64bits_reset(statePtr);\n    if ((seed != statePtr->seed) || (statePtr->extSecret != NULL))\n        XXH3_initCustomSecret(statePtr->customSecret, seed);\n    XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE);\n    return XXH_OK;\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64)\n{\n    if (statePtr == NULL) return XXH_ERROR;\n    if (secret == NULL) return XXH_ERROR;\n    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;\n    XXH3_reset_internal(statePtr, seed64, secret, secretSize);\n    statePtr->useSeed = 1; /* always, even if seed64==0 */\n    return XXH_OK;\n}\n\n/*!\n * @internal\n * @brief Processes a large input for XXH3_update() and XXH3_digest_long().\n *\n * Unlike XXH3_hashLong_internal_loop(), this can process data that overlaps a block.\n *\n * @param acc                Pointer to the 8 accumulator lanes\n * @param nbStripesSoFarPtr  In/out pointer to the number of leftover stripes in the block*\n * @param nbStripesPerBlock  Number of stripes in a block\n * @param input              Input pointer\n * @param nbStripes          Number of stripes to process\n * @param secret             Secret pointer\n * @param secretLimit        Offset of the last block in @p secret\n * @param f_acc              Pointer to an XXH3_accumulate implementation\n * @param f_scramble         Pointer to an XXH3_scrambleAcc implementation\n * @return                   Pointer past the end of @p input after processing\n */\nXXH_FORCE_INLINE const xxh_u8 *\nXXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc,\n                    size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock,\n                    const xxh_u8* XXH_RESTRICT input, size_t nbStripes,\n                    const xxh_u8* XXH_RESTRICT secret, size_t secretLimit,\n                    XXH3_f_accumulate f_acc,\n                    XXH3_f_scrambleAcc f_scramble)\n{\n    const xxh_u8* initialSecret = secret + *nbStripesSoFarPtr * XXH_SECRET_CONSUME_RATE;\n    /* Process full blocks */\n    if (nbStripes >= (nbStripesPerBlock - *nbStripesSoFarPtr)) {\n        /* Process the initial partial block... */\n        size_t nbStripesThisIter = nbStripesPerBlock - *nbStripesSoFarPtr;\n\n        do {\n            /* Accumulate and scramble */\n            f_acc(acc, input, initialSecret, nbStripesThisIter);\n            f_scramble(acc, secret + secretLimit);\n            input += nbStripesThisIter * XXH_STRIPE_LEN;\n            nbStripes -= nbStripesThisIter;\n            /* Then continue the loop with the full block size */\n            nbStripesThisIter = nbStripesPerBlock;\n            initialSecret = secret;\n        } while (nbStripes >= nbStripesPerBlock);\n        *nbStripesSoFarPtr = 0;\n    }\n    /* Process a partial block */\n    if (nbStripes > 0) {\n        f_acc(acc, input, initialSecret, nbStripes);\n        input += nbStripes * XXH_STRIPE_LEN;\n        *nbStripesSoFarPtr += nbStripes;\n    }\n    /* Return end pointer */\n    return input;\n}\n\n#ifndef XXH3_STREAM_USE_STACK\n# if XXH_SIZE_OPT <= 0 && !defined(__clang__) /* clang doesn't need additional stack space */\n#   define XXH3_STREAM_USE_STACK 1\n# endif\n#endif\n/* This function accepts f_acc and f_scramble as function pointers,\n * making it possible to implement multiple variants with different acc & scramble stages.\n * This is notably useful to implement multiple vector variants with different intrinsics.\n */\nXXH_FORCE_INLINE XXH_errorcode\nXXH3_update(XXH3_state_t* XXH_RESTRICT const state,\n            const xxh_u8* XXH_RESTRICT input, size_t len,\n            XXH3_f_accumulate f_acc,\n            XXH3_f_scrambleAcc f_scramble)\n{\n    if (input==NULL) {\n        XXH_ASSERT(len == 0);\n        return XXH_OK;\n    }\n\n    XXH_ASSERT(state != NULL);\n    state->totalLen += len;\n\n    /* small input : just fill in tmp buffer */\n    XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE);\n    if (len <= XXH3_INTERNALBUFFER_SIZE - state->bufferedSize) {\n        XXH_memcpy(state->buffer + state->bufferedSize, input, len);\n        state->bufferedSize += (XXH32_hash_t)len;\n        return XXH_OK;\n    }\n\n    {   const xxh_u8* const bEnd = input + len;\n        const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;\n#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1\n        /* For some reason, gcc and MSVC seem to suffer greatly\n         * when operating accumulators directly into state.\n         * Operating into stack space seems to enable proper optimization.\n         * clang, on the other hand, doesn't seem to need this trick */\n        XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8];\n        XXH_memcpy(acc, state->acc, sizeof(acc));\n#else\n        xxh_u64* XXH_RESTRICT const acc = state->acc;\n#endif\n\n        /* total input is now > XXH3_INTERNALBUFFER_SIZE */\n        #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN)\n        XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0);   /* clean multiple */\n\n        /*\n         * Internal buffer is partially filled (always, except at beginning)\n         * Complete it, then consume it.\n         */\n        if (state->bufferedSize) {\n            size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize;\n            XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize);\n            input += loadSize;\n            XXH3_consumeStripes(acc,\n                               &state->nbStripesSoFar, state->nbStripesPerBlock,\n                                state->buffer, XXH3_INTERNALBUFFER_STRIPES,\n                                secret, state->secretLimit,\n                                f_acc, f_scramble);\n            state->bufferedSize = 0;\n        }\n        XXH_ASSERT(input < bEnd);\n        if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) {\n            size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN;\n            input = XXH3_consumeStripes(acc,\n                                       &state->nbStripesSoFar, state->nbStripesPerBlock,\n                                       input, nbStripes,\n                                       secret, state->secretLimit,\n                                       f_acc, f_scramble);\n            XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN);\n\n        }\n        /* Some remaining input (always) : buffer it */\n        XXH_ASSERT(input < bEnd);\n        XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE);\n        XXH_ASSERT(state->bufferedSize == 0);\n        XXH_memcpy(state->buffer, input, (size_t)(bEnd-input));\n        state->bufferedSize = (XXH32_hash_t)(bEnd-input);\n#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1\n        /* save stack accumulators into state */\n        XXH_memcpy(state->acc, acc, sizeof(acc));\n#endif\n    }\n\n    return XXH_OK;\n}\n\n/*\n * Both XXH3_64bits_update and XXH3_128bits_update use this routine.\n */\nXXH_NO_INLINE XXH_errorcode\nXXH3_update_regular(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len)\n{\n    return XXH3_update(state, (const xxh_u8*)input, len,\n                       XXH3_accumulate, XXH3_scrambleAcc);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_64bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len)\n{\n    return XXH3_update_regular(state, input, len);\n}\n\n\nXXH_FORCE_INLINE void\nXXH3_digest_long (XXH64_hash_t* acc,\n                  const XXH3_state_t* state,\n                  const unsigned char* secret)\n{\n    xxh_u8 lastStripe[XXH_STRIPE_LEN];\n    const xxh_u8* lastStripePtr;\n\n    /*\n     * Digest on a local copy. This way, the state remains unaltered, and it can\n     * continue ingesting more input afterwards.\n     */\n    XXH_memcpy(acc, state->acc, sizeof(state->acc));\n    if (state->bufferedSize >= XXH_STRIPE_LEN) {\n        /* Consume remaining stripes then point to remaining data in buffer */\n        size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN;\n        size_t nbStripesSoFar = state->nbStripesSoFar;\n        XXH3_consumeStripes(acc,\n                           &nbStripesSoFar, state->nbStripesPerBlock,\n                            state->buffer, nbStripes,\n                            secret, state->secretLimit,\n                            XXH3_accumulate, XXH3_scrambleAcc);\n        lastStripePtr = state->buffer + state->bufferedSize - XXH_STRIPE_LEN;\n    } else {  /* bufferedSize < XXH_STRIPE_LEN */\n        /* Copy to temp buffer */\n        size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize;\n        XXH_ASSERT(state->bufferedSize > 0);  /* there is always some input buffered */\n        XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize);\n        XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize);\n        lastStripePtr = lastStripe;\n    }\n    /* Last stripe */\n    XXH3_accumulate_512(acc,\n                        lastStripePtr,\n                        secret + state->secretLimit - XXH_SECRET_LASTACC_START);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* state)\n{\n    const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;\n    if (state->totalLen > XXH3_MIDSIZE_MAX) {\n        XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];\n        XXH3_digest_long(acc, state, secret);\n        return XXH3_finalizeLong_64b(acc, secret, (xxh_u64)state->totalLen);\n    }\n    /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */\n    if (state->useSeed)\n        return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);\n    return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen),\n                                  secret, state->secretLimit + XXH_STRIPE_LEN);\n}\n#endif /* !XXH_NO_STREAM */\n\n\n/* ==========================================\n * XXH3 128 bits (a.k.a XXH128)\n * ==========================================\n * XXH3's 128-bit variant has better mixing and strength than the 64-bit variant,\n * even without counting the significantly larger output size.\n *\n * For example, extra steps are taken to avoid the seed-dependent collisions\n * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B).\n *\n * This strength naturally comes at the cost of some speed, especially on short\n * lengths. Note that longer hashes are about as fast as the 64-bit version\n * due to it using only a slight modification of the 64-bit loop.\n *\n * XXH128 is also more oriented towards 64-bit machines. It is still extremely\n * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).\n */\n\nXXH_FORCE_INLINE XXH_PUREF XXH128_hash_t\nXXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)\n{\n    /* A doubled version of 1to3_64b with different constants. */\n    XXH_ASSERT(input != NULL);\n    XXH_ASSERT(1 <= len && len <= 3);\n    XXH_ASSERT(secret != NULL);\n    /*\n     * len = 1: combinedl = { input[0], 0x01, input[0], input[0] }\n     * len = 2: combinedl = { input[1], 0x02, input[0], input[1] }\n     * len = 3: combinedl = { input[2], 0x03, input[0], input[1] }\n     */\n    {   xxh_u8 const c1 = input[0];\n        xxh_u8 const c2 = input[len >> 1];\n        xxh_u8 const c3 = input[len - 1];\n        xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24)\n                                | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8);\n        xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13);\n        xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed;\n        xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed;\n        xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl;\n        xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph;\n        XXH128_hash_t h128;\n        h128.low64  = XXH64_avalanche(keyed_lo);\n        h128.high64 = XXH64_avalanche(keyed_hi);\n        return h128;\n    }\n}\n\nXXH_FORCE_INLINE XXH_PUREF XXH128_hash_t\nXXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)\n{\n    XXH_ASSERT(input != NULL);\n    XXH_ASSERT(secret != NULL);\n    XXH_ASSERT(4 <= len && len <= 8);\n    seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;\n    {   xxh_u32 const input_lo = XXH_readLE32(input);\n        xxh_u32 const input_hi = XXH_readLE32(input + len - 4);\n        xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32);\n        xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed;\n        xxh_u64 const keyed = input_64 ^ bitflip;\n\n        /* Shift len to the left to ensure it is even, this avoids even multiplies. */\n        XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2));\n\n        m128.high64 += (m128.low64 << 1);\n        m128.low64  ^= (m128.high64 >> 3);\n\n        m128.low64   = XXH_xorshift64(m128.low64, 35);\n        m128.low64  *= PRIME_MX2;\n        m128.low64   = XXH_xorshift64(m128.low64, 28);\n        m128.high64  = XXH3_avalanche(m128.high64);\n        return m128;\n    }\n}\n\nXXH_FORCE_INLINE XXH_PUREF XXH128_hash_t\nXXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)\n{\n    XXH_ASSERT(input != NULL);\n    XXH_ASSERT(secret != NULL);\n    XXH_ASSERT(9 <= len && len <= 16);\n    {   xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed;\n        xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed;\n        xxh_u64 const input_lo = XXH_readLE64(input);\n        xxh_u64       input_hi = XXH_readLE64(input + len - 8);\n        XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1);\n        /*\n         * Put len in the middle of m128 to ensure that the length gets mixed to\n         * both the low and high bits in the 128x64 multiply below.\n         */\n        m128.low64 += (xxh_u64)(len - 1) << 54;\n        input_hi   ^= bitfliph;\n        /*\n         * Add the high 32 bits of input_hi to the high 32 bits of m128, then\n         * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to\n         * the high 64 bits of m128.\n         *\n         * The best approach to this operation is different on 32-bit and 64-bit.\n         */\n        if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */\n            /*\n             * 32-bit optimized version, which is more readable.\n             *\n             * On 32-bit, it removes an ADC and delays a dependency between the two\n             * halves of m128.high64, but it generates an extra mask on 64-bit.\n             */\n            m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2);\n        } else {\n            /*\n             * 64-bit optimized (albeit more confusing) version.\n             *\n             * Uses some properties of addition and multiplication to remove the mask:\n             *\n             * Let:\n             *    a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF)\n             *    b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000)\n             *    c = XXH_PRIME32_2\n             *\n             *    a + (b * c)\n             * Inverse Property: x + y - x == y\n             *    a + (b * (1 + c - 1))\n             * Distributive Property: x * (y + z) == (x * y) + (x * z)\n             *    a + (b * 1) + (b * (c - 1))\n             * Identity Property: x * 1 == x\n             *    a + b + (b * (c - 1))\n             *\n             * Substitute a, b, and c:\n             *    input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1))\n             *\n             * Since input_hi.hi + input_hi.lo == input_hi, we get this:\n             *    input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1))\n             */\n            m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1);\n        }\n        /* m128 ^= XXH_swap64(m128 >> 64); */\n        m128.low64  ^= XXH_swap64(m128.high64);\n\n        {   /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */\n            XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2);\n            h128.high64 += m128.high64 * XXH_PRIME64_2;\n\n            h128.low64   = XXH3_avalanche(h128.low64);\n            h128.high64  = XXH3_avalanche(h128.high64);\n            return h128;\n    }   }\n}\n\n/*\n * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN\n */\nXXH_FORCE_INLINE XXH_PUREF XXH128_hash_t\nXXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)\n{\n    XXH_ASSERT(len <= 16);\n    {   if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed);\n        if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed);\n        if (len) return XXH3_len_1to3_128b(input, len, secret, seed);\n        {   XXH128_hash_t h128;\n            xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72);\n            xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88);\n            h128.low64 = XXH64_avalanche(seed ^ bitflipl);\n            h128.high64 = XXH64_avalanche( seed ^ bitfliph);\n            return h128;\n    }   }\n}\n\n/*\n * A bit slower than XXH3_mix16B, but handles multiply by zero better.\n */\nXXH_FORCE_INLINE XXH128_hash_t\nXXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2,\n              const xxh_u8* secret, XXH64_hash_t seed)\n{\n    acc.low64  += XXH3_mix16B (input_1, secret+0, seed);\n    acc.low64  ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8);\n    acc.high64 += XXH3_mix16B (input_2, secret+16, seed);\n    acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8);\n    return acc;\n}\n\n\nXXH_FORCE_INLINE XXH_PUREF XXH128_hash_t\nXXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len,\n                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,\n                      XXH64_hash_t seed)\n{\n    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;\n    XXH_ASSERT(16 < len && len <= 128);\n\n    {   XXH128_hash_t acc;\n        acc.low64 = len * XXH_PRIME64_1;\n        acc.high64 = 0;\n\n#if XXH_SIZE_OPT >= 1\n        {\n            /* Smaller, but slightly slower. */\n            unsigned int i = (unsigned int)(len - 1) / 32;\n            do {\n                acc = XXH128_mix32B(acc, input+16*i, input+len-16*(i+1), secret+32*i, seed);\n            } while (i-- != 0);\n        }\n#else\n        if (len > 32) {\n            if (len > 64) {\n                if (len > 96) {\n                    acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed);\n                }\n                acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed);\n            }\n            acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed);\n        }\n        acc = XXH128_mix32B(acc, input, input+len-16, secret, seed);\n#endif\n        {   XXH128_hash_t h128;\n            h128.low64  = acc.low64 + acc.high64;\n            h128.high64 = (acc.low64    * XXH_PRIME64_1)\n                        + (acc.high64   * XXH_PRIME64_4)\n                        + ((len - seed) * XXH_PRIME64_2);\n            h128.low64  = XXH3_avalanche(h128.low64);\n            h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64);\n            return h128;\n        }\n    }\n}\n\nXXH_NO_INLINE XXH_PUREF XXH128_hash_t\nXXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len,\n                       const xxh_u8* XXH_RESTRICT secret, size_t secretSize,\n                       XXH64_hash_t seed)\n{\n    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;\n    XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);\n\n    {   XXH128_hash_t acc;\n        unsigned i;\n        acc.low64 = len * XXH_PRIME64_1;\n        acc.high64 = 0;\n        /*\n         *  We set as `i` as offset + 32. We do this so that unchanged\n         * `len` can be used as upper bound. This reaches a sweet spot\n         * where both x86 and aarch64 get simple agen and good codegen\n         * for the loop.\n         */\n        for (i = 32; i < 160; i += 32) {\n            acc = XXH128_mix32B(acc,\n                                input  + i - 32,\n                                input  + i - 16,\n                                secret + i - 32,\n                                seed);\n        }\n        acc.low64 = XXH3_avalanche(acc.low64);\n        acc.high64 = XXH3_avalanche(acc.high64);\n        /*\n         * NB: `i <= len` will duplicate the last 32-bytes if\n         * len % 32 was zero. This is an unfortunate necessity to keep\n         * the hash result stable.\n         */\n        for (i=160; i <= len; i += 32) {\n            acc = XXH128_mix32B(acc,\n                                input + i - 32,\n                                input + i - 16,\n                                secret + XXH3_MIDSIZE_STARTOFFSET + i - 160,\n                                seed);\n        }\n        /* last bytes */\n        acc = XXH128_mix32B(acc,\n                            input + len - 16,\n                            input + len - 32,\n                            secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16,\n                            (XXH64_hash_t)0 - seed);\n\n        {   XXH128_hash_t h128;\n            h128.low64  = acc.low64 + acc.high64;\n            h128.high64 = (acc.low64    * XXH_PRIME64_1)\n                        + (acc.high64   * XXH_PRIME64_4)\n                        + ((len - seed) * XXH_PRIME64_2);\n            h128.low64  = XXH3_avalanche(h128.low64);\n            h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64);\n            return h128;\n        }\n    }\n}\n\nstatic XXH_PUREF XXH128_hash_t\nXXH3_finalizeLong_128b(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, xxh_u64 len)\n{\n    XXH128_hash_t h128;\n    h128.low64 = XXH3_finalizeLong_64b(acc, secret, len);\n    h128.high64 = XXH3_mergeAccs(acc, secret + secretSize\n                                             - XXH_STRIPE_LEN - XXH_SECRET_MERGEACCS_START,\n                                             ~(len * XXH_PRIME64_2));\n    return h128;\n}\n\nXXH_FORCE_INLINE XXH128_hash_t\nXXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len,\n                            const xxh_u8* XXH_RESTRICT secret, size_t secretSize,\n                            XXH3_f_accumulate f_acc,\n                            XXH3_f_scrambleAcc f_scramble)\n{\n    XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;\n\n    XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc, f_scramble);\n\n    /* converge into final hash */\n    XXH_STATIC_ASSERT(sizeof(acc) == 64);\n    XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);\n    return XXH3_finalizeLong_128b(acc, secret, secretSize, (xxh_u64)len);\n}\n\n/*\n * It's important for performance that XXH3_hashLong() is not inlined.\n */\nXXH_NO_INLINE XXH_PUREF XXH128_hash_t\nXXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len,\n                           XXH64_hash_t seed64,\n                           const void* XXH_RESTRICT secret, size_t secretLen)\n{\n    (void)seed64; (void)secret; (void)secretLen;\n    return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret),\n                                       XXH3_accumulate, XXH3_scrambleAcc);\n}\n\n/*\n * It's important for performance to pass @p secretLen (when it's static)\n * to the compiler, so that it can properly optimize the vectorized loop.\n *\n * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE\n * breaks -Og, this is XXH_NO_INLINE.\n */\nXXH3_WITH_SECRET_INLINE XXH128_hash_t\nXXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len,\n                              XXH64_hash_t seed64,\n                              const void* XXH_RESTRICT secret, size_t secretLen)\n{\n    (void)seed64;\n    return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen,\n                                       XXH3_accumulate, XXH3_scrambleAcc);\n}\n\nXXH_FORCE_INLINE XXH128_hash_t\nXXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len,\n                                XXH64_hash_t seed64,\n                                XXH3_f_accumulate f_acc,\n                                XXH3_f_scrambleAcc f_scramble,\n                                XXH3_f_initCustomSecret f_initSec)\n{\n    if (seed64 == 0)\n        return XXH3_hashLong_128b_internal(input, len,\n                                           XXH3_kSecret, sizeof(XXH3_kSecret),\n                                           f_acc, f_scramble);\n    {   XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];\n        f_initSec(secret, seed64);\n        return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret),\n                                           f_acc, f_scramble);\n    }\n}\n\n/*\n * It's important for performance that XXH3_hashLong is not inlined.\n */\nXXH_NO_INLINE XXH128_hash_t\nXXH3_hashLong_128b_withSeed(const void* input, size_t len,\n                            XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen)\n{\n    (void)secret; (void)secretLen;\n    return XXH3_hashLong_128b_withSeed_internal(input, len, seed64,\n                XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret);\n}\n\ntypedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t,\n                                            XXH64_hash_t, const void* XXH_RESTRICT, size_t);\n\nXXH_FORCE_INLINE XXH128_hash_t\nXXH3_128bits_internal(const void* input, size_t len,\n                      XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen,\n                      XXH3_hashLong128_f f_hl128)\n{\n    XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);\n    /*\n     * If an action is to be taken if `secret` conditions are not respected,\n     * it should be done here.\n     * For now, it's a contract pre-condition.\n     * Adding a check and a branch here would cost performance at every hash.\n     */\n    if (len <= 16)\n        return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64);\n    if (len <= 128)\n        return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);\n    if (len <= XXH3_MIDSIZE_MAX)\n        return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);\n    return f_hl128(input, len, seed64, secret, secretLen);\n}\n\n\n/* ===   Public XXH128 API   === */\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* input, size_t len)\n{\n    return XXH3_128bits_internal(input, len, 0,\n                                 XXH3_kSecret, sizeof(XXH3_kSecret),\n                                 XXH3_hashLong_128b_default);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH128_hash_t\nXXH3_128bits_withSecret(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize)\n{\n    return XXH3_128bits_internal(input, len, 0,\n                                 (const xxh_u8*)secret, secretSize,\n                                 XXH3_hashLong_128b_withSecret);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH128_hash_t\nXXH3_128bits_withSeed(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed)\n{\n    return XXH3_128bits_internal(input, len, seed,\n                                 XXH3_kSecret, sizeof(XXH3_kSecret),\n                                 XXH3_hashLong_128b_withSeed);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH128_hash_t\nXXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed)\n{\n    if (len <= XXH3_MIDSIZE_MAX)\n        return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL);\n    return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH128_hash_t\nXXH128(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed)\n{\n    return XXH3_128bits_withSeed(input, len, seed);\n}\n\n\n/* ===   XXH3 128-bit streaming   === */\n#ifndef XXH_NO_STREAM\n/*\n * All initialization and update functions are identical to 64-bit streaming variant.\n * The only difference is the finalization routine.\n */\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr)\n{\n    return XXH3_64bits_reset(statePtr);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize)\n{\n    return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed)\n{\n    return XXH3_64bits_reset_withSeed(statePtr, seed);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed)\n{\n    return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_128bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len)\n{\n    return XXH3_update_regular(state, input, len);\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* state)\n{\n    const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;\n    if (state->totalLen > XXH3_MIDSIZE_MAX) {\n        XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];\n        XXH3_digest_long(acc, state, secret);\n        XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);\n        return XXH3_finalizeLong_128b(acc, secret, state->secretLimit + XXH_STRIPE_LEN,  (xxh_u64)state->totalLen);\n    }\n    /* len <= XXH3_MIDSIZE_MAX : short code */\n    if (state->useSeed)\n        return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);\n    return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen),\n                                   secret, state->secretLimit + XXH_STRIPE_LEN);\n}\n#endif /* !XXH_NO_STREAM */\n/* 128-bit utility functions */\n\n/* return : 1 is equal, 0 if different */\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2)\n{\n    /* note : XXH128_hash_t is compact, it has no padding byte */\n    return !(XXH_memcmp(&h1, &h2, sizeof(h1)));\n}\n\n/* This prototype is compatible with stdlib's qsort().\n * @return : >0 if *h128_1  > *h128_2\n *           <0 if *h128_1  < *h128_2\n *           =0 if *h128_1 == *h128_2  */\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2)\n{\n    XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1;\n    XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2;\n    int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64);\n    /* note : bets that, in most cases, hash values are different */\n    if (hcmp) return hcmp;\n    return (h1.low64 > h2.low64) - (h2.low64 > h1.low64);\n}\n\n\n/*======   Canonical representation   ======*/\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API void\nXXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash)\n{\n    XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t));\n    if (XXH_CPU_LITTLE_ENDIAN) {\n        hash.high64 = XXH_swap64(hash.high64);\n        hash.low64  = XXH_swap64(hash.low64);\n    }\n    XXH_memcpy(dst, &hash.high64, sizeof(hash.high64));\n    XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64));\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH128_hash_t\nXXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src)\n{\n    XXH128_hash_t h;\n    h.high64 = XXH_readBE64(src);\n    h.low64  = XXH_readBE64(src->digest + 8);\n    return h;\n}\n\n\n\n/* ==========================================\n * Secret generators\n * ==========================================\n */\n#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x))\n\nXXH_FORCE_INLINE void XXH3_combine16(void* dst, XXH128_hash_t h128)\n{\n    XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 );\n    XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 );\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API XXH_errorcode\nXXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize)\n{\n#if (XXH_DEBUGLEVEL >= 1)\n    XXH_ASSERT(secretBuffer != NULL);\n    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);\n#else\n    /* production mode, assert() are disabled */\n    if (secretBuffer == NULL) return XXH_ERROR;\n    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;\n#endif\n\n    if (customSeedSize == 0) {\n        customSeed = XXH3_kSecret;\n        customSeedSize = XXH_SECRET_DEFAULT_SIZE;\n    }\n#if (XXH_DEBUGLEVEL >= 1)\n    XXH_ASSERT(customSeed != NULL);\n#else\n    if (customSeed == NULL) return XXH_ERROR;\n#endif\n\n    /* Fill secretBuffer with a copy of customSeed - repeat as needed */\n    {   size_t pos = 0;\n        while (pos < secretSize) {\n            size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize);\n            XXH_memcpy((char*)secretBuffer + pos, customSeed, toCopy);\n            pos += toCopy;\n    }   }\n\n    {   size_t const nbSeg16 = secretSize / 16;\n        size_t n;\n        XXH128_canonical_t scrambler;\n        XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0));\n        for (n=0; n<nbSeg16; n++) {\n            XXH128_hash_t const h128 = XXH128(&scrambler, sizeof(scrambler), n);\n            XXH3_combine16((char*)secretBuffer + n*16, h128);\n        }\n        /* last segment */\n        XXH3_combine16((char*)secretBuffer + secretSize - 16, XXH128_hashFromCanonical(&scrambler));\n    }\n    return XXH_OK;\n}\n\n/*! @ingroup XXH3_family */\nXXH_PUBLIC_API void\nXXH3_generateSecret_fromSeed(XXH_NOESCAPE void* secretBuffer, XXH64_hash_t seed)\n{\n    XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];\n    XXH3_initCustomSecret(secret, seed);\n    XXH_ASSERT(secretBuffer != NULL);\n    XXH_memcpy(secretBuffer, secret, XXH_SECRET_DEFAULT_SIZE);\n}\n\n\n\n/* Pop our optimization override from above */\n#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \\\n  && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \\\n  && defined(__OPTIMIZE__) && XXH_SIZE_OPT <= 0 /* respect -O0 and -Os */\n#  pragma GCC pop_options\n#endif\n\n#endif  /* XXH_NO_LONG_LONG */\n\n#endif  /* XXH_NO_XXH3 */\n\n/*!\n * @}\n */\n#endif  /* XXH_IMPLEMENTATION */\n\n\n#if defined (__cplusplus) && !defined(XXH_NO_EXTERNC_GUARD)\n} /* extern \"C\" */\n#endif"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/base32/base32.c",
    "content": "// Base32 implementation\n//\n// Copyright 2010 Google Inc.\n// Author: Markus Gutschke\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"base32/base32.h\"\n\nstatic unsigned char base32_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',\n                                       'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7'};\n\nvoid tbc_base32_encode(const unsigned char* src, int srcLen, unsigned char* dst) {\n    int count = 0;\n    int buffer = src[0];\n    int next = 1;\n    int bitsLeft = 8;\n    while (bitsLeft > 0 || next < srcLen) {\n        if (bitsLeft < 5) {\n            if (next < srcLen) {\n                buffer <<= 8;\n                buffer |= src[next++] & 0xFF;\n                bitsLeft += 8;\n            } else {\n                int pad = 5 - bitsLeft;\n                buffer <<= pad;\n                bitsLeft += pad;\n            }\n        }\n        int index = 0x1F & (buffer >> (bitsLeft - 5));\n        bitsLeft -= 5;\n        dst[count++] = base32_table[index];\n    }\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/crc/crc32.c",
    "content": "/*\n** The crc32 is licensed under the Apache License, Version 2.0, and a copy of the license is included in this file.\n**\n** Author: Wang Yaofu voipman@qq.com\n** Description: The source file of class crc32.\n**  CRC32 implementation according to IEEE standards.\n**  Polynomials are represented in LSB-first form\n**  following parameters:\n**    Width                      : 32 bit\n**    Poly                       : 0xEDB88320\n**    Output for \"123456789\"     : 0xCBF43926\n*/\n\n#include \"crc/crc32.h\"\n\nstatic uint32_t tbc_crc32Table[] = {\n    0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, 0x0EDB8832L,\n    0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, 0x1DB71064L, 0x6AB020F2L,\n    0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL,\n    0x8A65C9ECL, 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,\n    0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, 0x32D86CE3L,\n    0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L, 0x56B3C423L,\n    0xCFBA9599L, 0xB8BDA50FL, 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL,\n    0xB6662D3DL, 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,\n    0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, 0x6B6B51F4L,\n    0x1C6C6162L, 0x856530D8L, 0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, 0x65B0D9C6L, 0x12B7E950L,\n    0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L,\n    0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,\n    0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L,\n    0x206F85B3L, 0xB966D409L, 0xCE61E49FL, 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L,\n    0xB7BD5C3BL, 0xC0BA6CADL, 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L,\n    0x73DC1683L, 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,\n    0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, 0xFED41B76L,\n    0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, 0xD6D6A3E8L, 0xA1D1937EL,\n    0x38D8C2C4L, 0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L,\n    0x41047A60L, 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,\n    0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, 0xC2D7FFA7L,\n    0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, 0x9C0906A9L, 0xEB0E363FL,\n    0x72076785L, 0x05005713L, 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L,\n    0x0BDBDF21L, 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,\n    0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, 0xA00AE278L,\n    0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, 0xAED16A4AL, 0xD9D65ADCL,\n    0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L,\n    0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,\n    0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL};\n\nuint32_t tbc_crc32(const unsigned char* src, size_t srcLen, uint32_t prev_val) {\n    uint32_t crc32_val = ~prev_val;\n\n    unsigned char* cursor = (unsigned char*)src;\n    while (srcLen--) {\n        crc32_val = (crc32_val >> 8) ^ tbc_crc32Table[(crc32_val & 0xFF) ^ *cursor++];\n    }\n\n    return ~crc32_val;\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/mbedtls/md5.c",
    "content": "/*\n *  RFC 1321 compliant MD5 implementation\n *\n *  Copyright The Mbed TLS Contributors\n *  SPDX-License-Identifier: Apache-2.0\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n *  not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n/*\n *  The MD5 algorithm was designed by Ron Rivest in 1991.\n *\n *  http://www.ietf.org/rfc/rfc1321.txt\n */\n\n#include \"mbedtls/common.h\"\n#include \"mbedtls/md5.h\"\n\nvoid mbedtls_md5_init(mbedtls_md5_context *ctx)\n{\n    memset(ctx, 0, sizeof(mbedtls_md5_context));\n}\n\n/*\n * MD5 context setup\n */\nvoid mbedtls_md5_starts(mbedtls_md5_context *ctx)\n{\n    ctx->total[0] = 0;\n    ctx->total[1] = 0;\n\n    ctx->state[0] = 0x67452301;\n    ctx->state[1] = 0xEFCDAB89;\n    ctx->state[2] = 0x98BADCFE;\n    ctx->state[3] = 0x10325476;\n}\n\nvoid mbedtls_internal_md5_process(mbedtls_md5_context *ctx,\n                                 const unsigned char data[64])\n{\n    struct {\n        uint32_t X[16], A, B, C, D;\n    } local;\n\n    local.X[0] = MBEDTLS_GET_UINT32_LE(data,  0);\n    local.X[1] = MBEDTLS_GET_UINT32_LE(data,  4);\n    local.X[2] = MBEDTLS_GET_UINT32_LE(data,  8);\n    local.X[3] = MBEDTLS_GET_UINT32_LE(data, 12);\n    local.X[4] = MBEDTLS_GET_UINT32_LE(data, 16);\n    local.X[5] = MBEDTLS_GET_UINT32_LE(data, 20);\n    local.X[6] = MBEDTLS_GET_UINT32_LE(data, 24);\n    local.X[7] = MBEDTLS_GET_UINT32_LE(data, 28);\n    local.X[8] = MBEDTLS_GET_UINT32_LE(data, 32);\n    local.X[9] = MBEDTLS_GET_UINT32_LE(data, 36);\n    local.X[10] = MBEDTLS_GET_UINT32_LE(data, 40);\n    local.X[11] = MBEDTLS_GET_UINT32_LE(data, 44);\n    local.X[12] = MBEDTLS_GET_UINT32_LE(data, 48);\n    local.X[13] = MBEDTLS_GET_UINT32_LE(data, 52);\n    local.X[14] = MBEDTLS_GET_UINT32_LE(data, 56);\n    local.X[15] = MBEDTLS_GET_UINT32_LE(data, 60);\n\n#define S(x, n)                                                          \\\n    (((x) << (n)) | (((x) & 0xFFFFFFFF) >> (32 - (n))))\n\n#define P(a, b, c, d, k, s, t)                                                \\\n    do                                                                  \\\n    {                                                                   \\\n        (a) += F((b), (c), (d)) + local.X[(k)] + (t);                     \\\n        (a) = S((a), (s)) + (b);                                         \\\n    } while (0)\n\n    local.A = ctx->state[0];\n    local.B = ctx->state[1];\n    local.C = ctx->state[2];\n    local.D = ctx->state[3];\n\n#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))\n\n    P(local.A, local.B, local.C, local.D,  0,  7, 0xD76AA478);\n    P(local.D, local.A, local.B, local.C,  1, 12, 0xE8C7B756);\n    P(local.C, local.D, local.A, local.B,  2, 17, 0x242070DB);\n    P(local.B, local.C, local.D, local.A,  3, 22, 0xC1BDCEEE);\n    P(local.A, local.B, local.C, local.D,  4,  7, 0xF57C0FAF);\n    P(local.D, local.A, local.B, local.C,  5, 12, 0x4787C62A);\n    P(local.C, local.D, local.A, local.B,  6, 17, 0xA8304613);\n    P(local.B, local.C, local.D, local.A,  7, 22, 0xFD469501);\n    P(local.A, local.B, local.C, local.D,  8,  7, 0x698098D8);\n    P(local.D, local.A, local.B, local.C,  9, 12, 0x8B44F7AF);\n    P(local.C, local.D, local.A, local.B, 10, 17, 0xFFFF5BB1);\n    P(local.B, local.C, local.D, local.A, 11, 22, 0x895CD7BE);\n    P(local.A, local.B, local.C, local.D, 12,  7, 0x6B901122);\n    P(local.D, local.A, local.B, local.C, 13, 12, 0xFD987193);\n    P(local.C, local.D, local.A, local.B, 14, 17, 0xA679438E);\n    P(local.B, local.C, local.D, local.A, 15, 22, 0x49B40821);\n\n#undef F\n\n#define F(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))\n\n    P(local.A, local.B, local.C, local.D,  1,  5, 0xF61E2562);\n    P(local.D, local.A, local.B, local.C,  6,  9, 0xC040B340);\n    P(local.C, local.D, local.A, local.B, 11, 14, 0x265E5A51);\n    P(local.B, local.C, local.D, local.A,  0, 20, 0xE9B6C7AA);\n    P(local.A, local.B, local.C, local.D,  5,  5, 0xD62F105D);\n    P(local.D, local.A, local.B, local.C, 10,  9, 0x02441453);\n    P(local.C, local.D, local.A, local.B, 15, 14, 0xD8A1E681);\n    P(local.B, local.C, local.D, local.A,  4, 20, 0xE7D3FBC8);\n    P(local.A, local.B, local.C, local.D,  9,  5, 0x21E1CDE6);\n    P(local.D, local.A, local.B, local.C, 14,  9, 0xC33707D6);\n    P(local.C, local.D, local.A, local.B,  3, 14, 0xF4D50D87);\n    P(local.B, local.C, local.D, local.A,  8, 20, 0x455A14ED);\n    P(local.A, local.B, local.C, local.D, 13,  5, 0xA9E3E905);\n    P(local.D, local.A, local.B, local.C,  2,  9, 0xFCEFA3F8);\n    P(local.C, local.D, local.A, local.B,  7, 14, 0x676F02D9);\n    P(local.B, local.C, local.D, local.A, 12, 20, 0x8D2A4C8A);\n\n#undef F\n\n#define F(x, y, z) ((x) ^ (y) ^ (z))\n\n    P(local.A, local.B, local.C, local.D,  5,  4, 0xFFFA3942);\n    P(local.D, local.A, local.B, local.C,  8, 11, 0x8771F681);\n    P(local.C, local.D, local.A, local.B, 11, 16, 0x6D9D6122);\n    P(local.B, local.C, local.D, local.A, 14, 23, 0xFDE5380C);\n    P(local.A, local.B, local.C, local.D,  1,  4, 0xA4BEEA44);\n    P(local.D, local.A, local.B, local.C,  4, 11, 0x4BDECFA9);\n    P(local.C, local.D, local.A, local.B,  7, 16, 0xF6BB4B60);\n    P(local.B, local.C, local.D, local.A, 10, 23, 0xBEBFBC70);\n    P(local.A, local.B, local.C, local.D, 13,  4, 0x289B7EC6);\n    P(local.D, local.A, local.B, local.C,  0, 11, 0xEAA127FA);\n    P(local.C, local.D, local.A, local.B,  3, 16, 0xD4EF3085);\n    P(local.B, local.C, local.D, local.A,  6, 23, 0x04881D05);\n    P(local.A, local.B, local.C, local.D,  9,  4, 0xD9D4D039);\n    P(local.D, local.A, local.B, local.C, 12, 11, 0xE6DB99E5);\n    P(local.C, local.D, local.A, local.B, 15, 16, 0x1FA27CF8);\n    P(local.B, local.C, local.D, local.A,  2, 23, 0xC4AC5665);\n\n#undef F\n\n#define F(x, y, z) ((y) ^ ((x) | ~(z)))\n\n    P(local.A, local.B, local.C, local.D,  0,  6, 0xF4292244);\n    P(local.D, local.A, local.B, local.C,  7, 10, 0x432AFF97);\n    P(local.C, local.D, local.A, local.B, 14, 15, 0xAB9423A7);\n    P(local.B, local.C, local.D, local.A,  5, 21, 0xFC93A039);\n    P(local.A, local.B, local.C, local.D, 12,  6, 0x655B59C3);\n    P(local.D, local.A, local.B, local.C,  3, 10, 0x8F0CCC92);\n    P(local.C, local.D, local.A, local.B, 10, 15, 0xFFEFF47D);\n    P(local.B, local.C, local.D, local.A,  1, 21, 0x85845DD1);\n    P(local.A, local.B, local.C, local.D,  8,  6, 0x6FA87E4F);\n    P(local.D, local.A, local.B, local.C, 15, 10, 0xFE2CE6E0);\n    P(local.C, local.D, local.A, local.B,  6, 15, 0xA3014314);\n    P(local.B, local.C, local.D, local.A, 13, 21, 0x4E0811A1);\n    P(local.A, local.B, local.C, local.D,  4,  6, 0xF7537E82);\n    P(local.D, local.A, local.B, local.C, 11, 10, 0xBD3AF235);\n    P(local.C, local.D, local.A, local.B,  2, 15, 0x2AD7D2BB);\n    P(local.B, local.C, local.D, local.A,  9, 21, 0xEB86D391);\n\n#undef F\n\n    ctx->state[0] += local.A;\n    ctx->state[1] += local.B;\n    ctx->state[2] += local.C;\n    ctx->state[3] += local.D;\n}\n\n/*\n * MD5 process buffer\n */\nvoid mbedtls_md5_update(mbedtls_md5_context *ctx,\n                       const unsigned char *input,\n                       size_t ilen)\n{\n    size_t fill;\n    uint32_t left;\n\n    if (ilen == 0) {\n        return;\n    }\n\n    left = ctx->total[0] & 0x3F;\n    fill = 64 - left;\n\n    ctx->total[0] += (uint32_t) ilen;\n    ctx->total[0] &= 0xFFFFFFFF;\n\n    if (ctx->total[0] < (uint32_t) ilen) {\n        ctx->total[1]++;\n    }\n\n    if (left && ilen >= fill) {\n        memcpy((void *) (ctx->buffer + left), input, fill);\n        mbedtls_internal_md5_process(ctx, ctx->buffer);\n        input += fill;\n        ilen  -= fill;\n        left = 0;\n    }\n\n    while (ilen >= 64) {\n        mbedtls_internal_md5_process(ctx, input);\n        input += 64;\n        ilen  -= 64;\n    }\n\n    if (ilen > 0) {\n        memcpy((void *) (ctx->buffer + left), input, ilen);\n    }\n}\n\n/*\n * MD5 final digest\n */\nvoid mbedtls_md5_finish(mbedtls_md5_context *ctx,\n                       unsigned char output[16])\n{\n    uint32_t used;\n    uint32_t high, low;\n\n    /*\n     * Add padding: 0x80 then 0x00 until 8 bytes remain for the length\n     */\n    used = ctx->total[0] & 0x3F;\n\n    ctx->buffer[used++] = 0x80;\n\n    if (used <= 56) {\n        /* Enough room for padding + length in current block */\n        memset(ctx->buffer + used, 0, 56 - used);\n    } else {\n        /* We'll need an extra block */\n        memset(ctx->buffer + used, 0, 64 - used);\n        mbedtls_internal_md5_process(ctx, ctx->buffer);\n        memset(ctx->buffer, 0, 56);\n    }\n\n    /*\n     * Add message length\n     */\n    high = (ctx->total[0] >> 29)\n           | (ctx->total[1] <<  3);\n    low  = (ctx->total[0] <<  3);\n\n    MBEDTLS_PUT_UINT32_LE(low,  ctx->buffer, 56);\n    MBEDTLS_PUT_UINT32_LE(high, ctx->buffer, 60);\n\n    mbedtls_internal_md5_process(ctx, ctx->buffer);\n\n    /*\n     * Output final state\n     */\n    MBEDTLS_PUT_UINT32_LE(ctx->state[0], output,  0);\n    MBEDTLS_PUT_UINT32_LE(ctx->state[1], output,  4);\n    MBEDTLS_PUT_UINT32_LE(ctx->state[2], output,  8);\n    MBEDTLS_PUT_UINT32_LE(ctx->state[3], output, 12);\n}\n\n/*\n * output = MD5( input buffer )\n */\nvoid mbedtls_md5(const unsigned char *input,\n                size_t ilen,\n                unsigned char output[16])\n{\n    mbedtls_md5_context ctx;\n    mbedtls_md5_init(&ctx);\n    mbedtls_md5_starts(&ctx);\n    mbedtls_md5_update(&ctx, input, ilen);\n    mbedtls_md5_finish(&ctx, output);\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/mbedtls/sha1.c",
    "content": "/*\n *  FIPS-180-1 compliant SHA-1 implementation\n *\n *  Copyright The Mbed TLS Contributors\n *  SPDX-License-Identifier: Apache-2.0\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n *  not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n/*\n *  The SHA-1 standard was published by NIST in 1993.\n *\n *  http://www.itl.nist.gov/fipspubs/fip180-1.htm\n */\n\n#include \"mbedtls/common.h\"\n#include \"mbedtls/sha1.h\"\n\nvoid mbedtls_sha1_init(mbedtls_sha1_context *ctx)\n{\n    memset(ctx, 0, sizeof(mbedtls_sha1_context));\n}\n\n/*\n * SHA-1 context setup\n */\nvoid mbedtls_sha1_starts(mbedtls_sha1_context *ctx)\n{\n    ctx->total[0] = 0;\n    ctx->total[1] = 0;\n\n    ctx->state[0] = 0x67452301;\n    ctx->state[1] = 0xEFCDAB89;\n    ctx->state[2] = 0x98BADCFE;\n    ctx->state[3] = 0x10325476;\n    ctx->state[4] = 0xC3D2E1F0;\n}\n\nvoid mbedtls_internal_sha1_process(mbedtls_sha1_context *ctx,\n                                  const unsigned char data[64])\n{\n    struct {\n        uint32_t temp, W[16], A, B, C, D, E;\n    } local;\n\n    local.W[0] = MBEDTLS_GET_UINT32_BE(data,  0);\n    local.W[1] = MBEDTLS_GET_UINT32_BE(data,  4);\n    local.W[2] = MBEDTLS_GET_UINT32_BE(data,  8);\n    local.W[3] = MBEDTLS_GET_UINT32_BE(data, 12);\n    local.W[4] = MBEDTLS_GET_UINT32_BE(data, 16);\n    local.W[5] = MBEDTLS_GET_UINT32_BE(data, 20);\n    local.W[6] = MBEDTLS_GET_UINT32_BE(data, 24);\n    local.W[7] = MBEDTLS_GET_UINT32_BE(data, 28);\n    local.W[8] = MBEDTLS_GET_UINT32_BE(data, 32);\n    local.W[9] = MBEDTLS_GET_UINT32_BE(data, 36);\n    local.W[10] = MBEDTLS_GET_UINT32_BE(data, 40);\n    local.W[11] = MBEDTLS_GET_UINT32_BE(data, 44);\n    local.W[12] = MBEDTLS_GET_UINT32_BE(data, 48);\n    local.W[13] = MBEDTLS_GET_UINT32_BE(data, 52);\n    local.W[14] = MBEDTLS_GET_UINT32_BE(data, 56);\n    local.W[15] = MBEDTLS_GET_UINT32_BE(data, 60);\n\n#define S(x, n) (((x) << (n)) | (((x) & 0xFFFFFFFF) >> (32 - (n))))\n\n#define R(t)                                                    \\\n    (                                                           \\\n        local.temp = local.W[((t) -  3) & 0x0F] ^             \\\n                     local.W[((t) -  8) & 0x0F] ^             \\\n                     local.W[((t) - 14) & 0x0F] ^             \\\n                     local.W[(t)        & 0x0F],              \\\n        (local.W[(t) & 0x0F] = S(local.temp, 1))               \\\n    )\n\n#define P(a, b, c, d, e, x)                                          \\\n    do                                                          \\\n    {                                                           \\\n        (e) += S((a), 5) + F((b), (c), (d)) + K + (x);             \\\n        (b) = S((b), 30);                                        \\\n    } while (0)\n\n    local.A = ctx->state[0];\n    local.B = ctx->state[1];\n    local.C = ctx->state[2];\n    local.D = ctx->state[3];\n    local.E = ctx->state[4];\n\n#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))\n#define K 0x5A827999\n\n    P(local.A, local.B, local.C, local.D, local.E, local.W[0]);\n    P(local.E, local.A, local.B, local.C, local.D, local.W[1]);\n    P(local.D, local.E, local.A, local.B, local.C, local.W[2]);\n    P(local.C, local.D, local.E, local.A, local.B, local.W[3]);\n    P(local.B, local.C, local.D, local.E, local.A, local.W[4]);\n    P(local.A, local.B, local.C, local.D, local.E, local.W[5]);\n    P(local.E, local.A, local.B, local.C, local.D, local.W[6]);\n    P(local.D, local.E, local.A, local.B, local.C, local.W[7]);\n    P(local.C, local.D, local.E, local.A, local.B, local.W[8]);\n    P(local.B, local.C, local.D, local.E, local.A, local.W[9]);\n    P(local.A, local.B, local.C, local.D, local.E, local.W[10]);\n    P(local.E, local.A, local.B, local.C, local.D, local.W[11]);\n    P(local.D, local.E, local.A, local.B, local.C, local.W[12]);\n    P(local.C, local.D, local.E, local.A, local.B, local.W[13]);\n    P(local.B, local.C, local.D, local.E, local.A, local.W[14]);\n    P(local.A, local.B, local.C, local.D, local.E, local.W[15]);\n    P(local.E, local.A, local.B, local.C, local.D, R(16));\n    P(local.D, local.E, local.A, local.B, local.C, R(17));\n    P(local.C, local.D, local.E, local.A, local.B, R(18));\n    P(local.B, local.C, local.D, local.E, local.A, R(19));\n\n#undef K\n#undef F\n\n#define F(x, y, z) ((x) ^ (y) ^ (z))\n#define K 0x6ED9EBA1\n\n    P(local.A, local.B, local.C, local.D, local.E, R(20));\n    P(local.E, local.A, local.B, local.C, local.D, R(21));\n    P(local.D, local.E, local.A, local.B, local.C, R(22));\n    P(local.C, local.D, local.E, local.A, local.B, R(23));\n    P(local.B, local.C, local.D, local.E, local.A, R(24));\n    P(local.A, local.B, local.C, local.D, local.E, R(25));\n    P(local.E, local.A, local.B, local.C, local.D, R(26));\n    P(local.D, local.E, local.A, local.B, local.C, R(27));\n    P(local.C, local.D, local.E, local.A, local.B, R(28));\n    P(local.B, local.C, local.D, local.E, local.A, R(29));\n    P(local.A, local.B, local.C, local.D, local.E, R(30));\n    P(local.E, local.A, local.B, local.C, local.D, R(31));\n    P(local.D, local.E, local.A, local.B, local.C, R(32));\n    P(local.C, local.D, local.E, local.A, local.B, R(33));\n    P(local.B, local.C, local.D, local.E, local.A, R(34));\n    P(local.A, local.B, local.C, local.D, local.E, R(35));\n    P(local.E, local.A, local.B, local.C, local.D, R(36));\n    P(local.D, local.E, local.A, local.B, local.C, R(37));\n    P(local.C, local.D, local.E, local.A, local.B, R(38));\n    P(local.B, local.C, local.D, local.E, local.A, R(39));\n\n#undef K\n#undef F\n\n#define F(x, y, z) (((x) & (y)) | ((z) & ((x) | (y))))\n#define K 0x8F1BBCDC\n\n    P(local.A, local.B, local.C, local.D, local.E, R(40));\n    P(local.E, local.A, local.B, local.C, local.D, R(41));\n    P(local.D, local.E, local.A, local.B, local.C, R(42));\n    P(local.C, local.D, local.E, local.A, local.B, R(43));\n    P(local.B, local.C, local.D, local.E, local.A, R(44));\n    P(local.A, local.B, local.C, local.D, local.E, R(45));\n    P(local.E, local.A, local.B, local.C, local.D, R(46));\n    P(local.D, local.E, local.A, local.B, local.C, R(47));\n    P(local.C, local.D, local.E, local.A, local.B, R(48));\n    P(local.B, local.C, local.D, local.E, local.A, R(49));\n    P(local.A, local.B, local.C, local.D, local.E, R(50));\n    P(local.E, local.A, local.B, local.C, local.D, R(51));\n    P(local.D, local.E, local.A, local.B, local.C, R(52));\n    P(local.C, local.D, local.E, local.A, local.B, R(53));\n    P(local.B, local.C, local.D, local.E, local.A, R(54));\n    P(local.A, local.B, local.C, local.D, local.E, R(55));\n    P(local.E, local.A, local.B, local.C, local.D, R(56));\n    P(local.D, local.E, local.A, local.B, local.C, R(57));\n    P(local.C, local.D, local.E, local.A, local.B, R(58));\n    P(local.B, local.C, local.D, local.E, local.A, R(59));\n\n#undef K\n#undef F\n\n#define F(x, y, z) ((x) ^ (y) ^ (z))\n#define K 0xCA62C1D6\n\n    P(local.A, local.B, local.C, local.D, local.E, R(60));\n    P(local.E, local.A, local.B, local.C, local.D, R(61));\n    P(local.D, local.E, local.A, local.B, local.C, R(62));\n    P(local.C, local.D, local.E, local.A, local.B, R(63));\n    P(local.B, local.C, local.D, local.E, local.A, R(64));\n    P(local.A, local.B, local.C, local.D, local.E, R(65));\n    P(local.E, local.A, local.B, local.C, local.D, R(66));\n    P(local.D, local.E, local.A, local.B, local.C, R(67));\n    P(local.C, local.D, local.E, local.A, local.B, R(68));\n    P(local.B, local.C, local.D, local.E, local.A, R(69));\n    P(local.A, local.B, local.C, local.D, local.E, R(70));\n    P(local.E, local.A, local.B, local.C, local.D, R(71));\n    P(local.D, local.E, local.A, local.B, local.C, R(72));\n    P(local.C, local.D, local.E, local.A, local.B, R(73));\n    P(local.B, local.C, local.D, local.E, local.A, R(74));\n    P(local.A, local.B, local.C, local.D, local.E, R(75));\n    P(local.E, local.A, local.B, local.C, local.D, R(76));\n    P(local.D, local.E, local.A, local.B, local.C, R(77));\n    P(local.C, local.D, local.E, local.A, local.B, R(78));\n    P(local.B, local.C, local.D, local.E, local.A, R(79));\n\n#undef K\n#undef F\n\n    ctx->state[0] += local.A;\n    ctx->state[1] += local.B;\n    ctx->state[2] += local.C;\n    ctx->state[3] += local.D;\n    ctx->state[4] += local.E;\n}\n\n/*\n * SHA-1 process buffer\n */\nvoid mbedtls_sha1_update(mbedtls_sha1_context *ctx,\n                        const unsigned char *input,\n                        size_t ilen)\n{\n    size_t fill;\n    uint32_t left;\n\n    if (ilen == 0) {\n        return;\n    }\n\n    left = ctx->total[0] & 0x3F;\n    fill = 64 - left;\n\n    ctx->total[0] += (uint32_t) ilen;\n    ctx->total[0] &= 0xFFFFFFFF;\n\n    if (ctx->total[0] < (uint32_t) ilen) {\n        ctx->total[1]++;\n    }\n\n    if (left && ilen >= fill) {\n        memcpy((void *) (ctx->buffer + left), input, fill);\n\n        mbedtls_internal_sha1_process(ctx, ctx->buffer);\n\n        input += fill;\n        ilen  -= fill;\n        left = 0;\n    }\n\n    while (ilen >= 64) {\n        mbedtls_internal_sha1_process(ctx, input);\n        input += 64;\n        ilen  -= 64;\n    }\n\n    if (ilen > 0) {\n        memcpy((void *) (ctx->buffer + left), input, ilen);\n    }\n}\n\n/*\n * SHA-1 final digest\n */\nvoid mbedtls_sha1_finish(mbedtls_sha1_context *ctx,\n                        unsigned char output[20])\n{\n    uint32_t used;\n    uint32_t high, low;\n\n    /*\n     * Add padding: 0x80 then 0x00 until 8 bytes remain for the length\n     */\n    used = ctx->total[0] & 0x3F;\n\n    ctx->buffer[used++] = 0x80;\n\n    if (used <= 56) {\n        /* Enough room for padding + length in current block */\n        memset(ctx->buffer + used, 0, 56 - used);\n    } else {\n        /* We'll need an extra block */\n        memset(ctx->buffer + used, 0, 64 - used);\n        mbedtls_internal_sha1_process(ctx, ctx->buffer);\n        memset(ctx->buffer, 0, 56);\n    }\n\n    /*\n     * Add message length\n     */\n    high = (ctx->total[0] >> 29)\n           | (ctx->total[1] <<  3);\n    low  = (ctx->total[0] <<  3);\n\n    MBEDTLS_PUT_UINT32_BE(high, ctx->buffer, 56);\n    MBEDTLS_PUT_UINT32_BE(low,  ctx->buffer, 60);\n\n    mbedtls_internal_sha1_process(ctx, ctx->buffer);\n\n    /*\n     * Output final state\n     */\n    MBEDTLS_PUT_UINT32_BE(ctx->state[0], output,  0);\n    MBEDTLS_PUT_UINT32_BE(ctx->state[1], output,  4);\n    MBEDTLS_PUT_UINT32_BE(ctx->state[2], output,  8);\n    MBEDTLS_PUT_UINT32_BE(ctx->state[3], output, 12);\n    MBEDTLS_PUT_UINT32_BE(ctx->state[4], output, 16);\n}\n\n/*\n * output = SHA-1( input buffer )\n */\nvoid mbedtls_sha1(const unsigned char *input,\n                 size_t ilen,\n                 unsigned char output[20])\n{\n    mbedtls_sha1_context ctx;\n    mbedtls_sha1_init(&ctx);\n    mbedtls_sha1_starts(&ctx);\n    mbedtls_sha1_update(&ctx, input, ilen);\n    mbedtls_sha1_finish(&ctx, output);\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/tbcrypto/bb64.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\n#include \"tbcrypto/bb64.h\"\n\n#define LAST_IND(x, part_type) (sizeof(x) / sizeof(part_type) - 1)\n#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN\n#    define LOW_IND(x, part_type) LAST_IND(x, part_type)\n#    define HIGH_IND(x, part_type) 0\n#else\n#    define HIGH_IND(x, part_type) LAST_IND(x, part_type)\n#    define LOW_IND(x, part_type) 0\n#endif\n\n#define LOBYTE(x) BYTEn(x, LOW_IND(x, unsigned char))\n#define HIBYTE(x) BYTEn(x, HIGH_IND(x, unsigned char))\n\n#define BYTEn(x, n) (*((unsigned char *)&(x) + n))\n#define BYTE1(x) BYTEn(x, 1)\n#define BYTE2(x) BYTEn(x, 2)\n\nunsigned char *B6417 = \"qogjOuCRNkfil5p4SQ3LAmxGKZTdesvB6z_YPahMI9t80rJyHW1DEwFbc7nUVX2-\";\n\nunsigned int BB64ResultLen(int srcLen) { return 4 * ((srcLen + 2) / 3u) + 2; }\n\nunsigned char *B6411(unsigned char *result, unsigned char a2, int a3)  // int result\n{\n    while (a3) {\n        --a3;\n        *(unsigned char *)(result + a3) = a2;\n    }\n    return result;\n}\n\nunsigned char *B6412(unsigned char *result, const unsigned char *a2, int a3) {\n    while (a3) {\n        --a3;\n        *(unsigned char *)(result + a3) = *(unsigned char *)(a2 + a3);\n    }\n    return result;\n}\n\nunsigned char *B6413(int *a1, int a2, int a3) {\n    int v3;                 // r6\n    int v4;                 // r8\n    int v7;                 // r7\n    unsigned char *result;  // r0\n    int i;                  // r3\n\n    v3 = a2 & 0x3F;\n    v4 = 64 - v3;\n    v7 = (a2 << (32 - 5) | (a2 >> 5));\n    B6412((unsigned char *)(a1 + 33), &B6417[v3], v4);\n    result = B6412((unsigned char *)a1 + v4 + 132, B6417, v3);\n    *a1 = v7 ^ (758653732 << (v7 & 0xF));\n    if (a3) {\n        result = B6411((unsigned char *)(a1 + 1), 64, 128);\n        for (i = 0; i != 64; ++i) *((unsigned char *)a1 + *((unsigned char *)a1 + i + 132) + 4) = i;\n    }\n    return result;\n}\n\nint *B6419(const int *a1, int *a2, unsigned int a3) {\n    unsigned int v3;  // r3\n    int v4;           // r4\n    int *result;      // r0\n\n    v3 = a3 >> 2;\n    v4 = *a1;\n    for (result = a2; result != &a2[a3 >> 2]; ++result) *result = v4 ^ (*result << (32 - 3) | (*result >> 3));\n    if (v3 < (a3 + 3) >> 2) a2[v3] ^= v4;\n    return result;\n}\n\nunsigned char *B6414(unsigned char *a1, const unsigned char *a2, int *a3) {\n    unsigned int v3;        // r5\n    unsigned int v4;        // r4\n    unsigned int v5;        // r3\n    unsigned char *v6;      // r1\n    unsigned char *result;  // r0\n    int v8;                 // [sp+4h] [bp-14h]\n\n    v3 = (unsigned int)*a2;\n    v4 = (unsigned int)a2[1];\n    LOBYTE(v8) = *(unsigned char *)((v3 & 0x3F) + a1 + 132);\n    BYTE1(v8) = *(unsigned char *)((v4 & 0x3F) + a1 + 132);\n    v5 = (unsigned int)a2[2];\n    v6 = (v5 & 0x3F) + a1;\n    result = a1 + ((4 * (v4 >> 6)) | (16 * (v3 >> 6)) | (v5 >> 6));\n    BYTE2(v8) = *(unsigned char *)(v6 + 132);\n    HIBYTE(v8) = *(unsigned char *)(result + 132);\n    *a3 = v8;\n    return result;\n}\n\nunsigned int GC02(unsigned char *preResultArray, unsigned int inputArrayLen, int mode) {\n    unsigned int v5;             // r9\n    unsigned int v6;             // r5\n    unsigned int v7;             // r8\n    unsigned int v8;             // r7\n    unsigned char *v9;           // r11\n    unsigned char *v10;          // r10\n    int i;                       // r8\n    unsigned int result;         // r0\n    unsigned char v13[4] = {0};  // [sp+Ch] [bp-F4h] BYREF\n    unsigned char v14[196];      // [sp+10h] [bp-F0h] BYREF\n\n    if (!inputArrayLen || !preResultArray) return -1;\n    v5 = inputArrayLen % 3;\n    v6 = inputArrayLen / 3;\n    v7 = 4 * (inputArrayLen / 3);\n    B6413((int *)v14, mode, 0);\n    B6419((int *)v14, (int *)preResultArray, inputArrayLen);\n    if (v5) {\n        B6412(v13, preResultArray + 3 * v6, v5);\n        B6414(v14, (unsigned char *)v13, (int *)(preResultArray + 3 * v6 + v6));\n        v8 = v7 + 4;\n    } else {\n        v8 = 4 * (inputArrayLen / 3);\n    }\n    v9 = preResultArray + 3 * v6;\n    v10 = preResultArray + v7 - 4;\n    for (i = 0;; ++i) {\n        v9 -= 3;\n        if (i == v6) break;\n        B6414(v14, v9, (int *)(v10 - 4 * i));\n    }\n    *(unsigned char *)(preResultArray + v8) = v5 + 65;\n    result = v8 + 2;\n    *(unsigned char *)(preResultArray + v8 + 1) = 0;\n    return result;\n}\n\nvoid tbc_BB64Encode(const unsigned char *inputArray, int srcLen, int mode, unsigned char *dst) {\n    signed int inputArrayLen;  // r8\n    void *v12;                 // r5\n\n    inputArrayLen = srcLen;  // GetArrayLength\n\n    unsigned int resultLen = BB64ResultLen(inputArrayLen);\n    v12 = (unsigned char *)malloc(resultLen);\n    memset(v12, 0, resultLen);\n    memcpy(v12, inputArray, inputArrayLen);\n    memcpy(dst, v12, resultLen * sizeof(unsigned char));  // memcpy\n    free(v12);\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/tbcrypto/cuid.c",
    "content": "#include <memory.h>\n#include <stdbool.h>\n\n#include \"base32/base32.h\"\n#include \"crc/crc32.h\"\n#include \"mbedtls/md5.h\"\n#include \"mbedtls/sha1.h\"\n\n#define XXH_INLINE_ALL\n#include \"xxHash/xxhash.h\"\n\n#include \"tbcrypto/const.h\"\n\n#include \"tbcrypto/cuid.h\"\n\n#define HASHER_NUM 4\n#define STEP_SIZE 5\n#define HASH_SIZE_IN_BIT 32\n\nstatic const char CUID2_PERFIX[] = {'c', 'o', 'm', '.', 'b', 'a', 'i', 'd', 'u'};\nstatic const char CUID3_PERFIX[] = {'c', 'o', 'm', '.', 'h', 'e', 'l', 'i', 'o', 's'};\n\nstatic void __tbc_update(uint64_t* sec, uint64_t hashVal, uint64_t start, bool flag) {\n    uint64_t end = start + HASH_SIZE_IN_BIT;\n    uint64_t secTemp = *sec;\n    uint64_t var9 = ((uint64_t)1 << end) - 1;\n    uint64_t var5 = (var9 & *sec) >> start;\n\n    if (flag) {\n        var5 ^= hashVal;\n    } else {\n        var5 &= hashVal;\n    }\n\n    for (uint64_t i = 0; i < HASH_SIZE_IN_BIT; i++) {\n        uint64_t opIdx = start + i;\n        if (var5 & (uint64_t)1 << i) {\n            secTemp |= (uint64_t)1 << opIdx;\n        } else {\n            secTemp &= ~((uint64_t)1 << opIdx);\n        }\n    }\n\n    *sec = secTemp;\n}\n\nstatic void __tbc_writeBuffer(unsigned char* buffer, const uint64_t sec) {\n    uint64_t tmpSec = sec;\n    for (uint64_t i = 0; i < STEP_SIZE; i++) {\n        buffer[i] = (unsigned char)((uint64_t)UINT8_MAX & tmpSec);\n        tmpSec >>= 8;\n    }\n}\n\nvoid tbc_heliosHash(const unsigned char* src, size_t srcSize, unsigned char* dst) {\n    // init\n    uint32_t crc32Val;\n    uint32_t xxhash32Val;\n    uint64_t sec = ((uint64_t)1 << 40) - 1;  // equals to `-1L>>>-40L` in java\n    unsigned char buffer[HASHER_NUM * STEP_SIZE];\n    memset(buffer, -1, STEP_SIZE);  // Now buffer is [-1 * 5, ...]\n\n    // 1st hash with CRC32\n    crc32Val = tbc_crc32(src, srcSize, 0);\n    crc32Val = tbc_crc32(buffer, STEP_SIZE, crc32Val);\n    __tbc_update(&sec, (uint64_t)crc32Val, 8, false);\n    __tbc_writeBuffer(buffer + STEP_SIZE, sec);  // Now buffer is [-1 * 5, crcrc, ...]\n\n    // 2nd hash with xxHash32\n    XXH32_state_t xxState4StepTwo, xxState4StepThree;\n    XXH32_reset(&xxState4StepTwo, 0);\n    XXH32_update(&xxState4StepTwo, src, srcSize);\n    XXH32_update(&xxState4StepTwo, buffer, STEP_SIZE * 2);\n    XXH32_copyState(&xxState4StepThree, &xxState4StepTwo);\n    xxhash32Val = XXH32_digest(&xxState4StepTwo);\n    __tbc_update(&sec, xxhash32Val, 0, true);\n    __tbc_writeBuffer(buffer + STEP_SIZE * 2, sec);  // Now buffer is [-1[5], crc[5], xxxxx, ...]\n\n    // 3rd hash with xxHash32\n    XXH32_update(&xxState4StepThree, buffer + STEP_SIZE * 2, STEP_SIZE);\n    xxhash32Val = XXH32_digest(&xxState4StepThree);\n    __tbc_update(&sec, xxhash32Val, 1, true);\n    __tbc_writeBuffer(buffer + STEP_SIZE * 3, sec);  // Now buffer is [-1[5], crc[5], xx[5], xx[5]]\n\n    // 4th hash with CRC32\n    crc32Val = tbc_crc32(buffer + STEP_SIZE, STEP_SIZE * 3, crc32Val);\n    __tbc_update(&sec, crc32Val, 7, true);\n\n    // write to dst\n    __tbc_writeBuffer(dst, sec);\n}\n\nvoid tbc_cuid_galaxy2(const unsigned char* androidID, unsigned char* dst) {\n    // step 1: build src buffer and compute md5\n    unsigned char md5Buffer[sizeof(CUID2_PERFIX) + TBC_ANDROID_ID_SIZE];\n\n    size_t buffOffset = 0;\n    memcpy(md5Buffer, CUID2_PERFIX, sizeof(CUID2_PERFIX));\n    buffOffset += sizeof(CUID2_PERFIX);\n    memcpy(md5Buffer + buffOffset, androidID, TBC_ANDROID_ID_SIZE);\n\n    unsigned char md5[TBC_MD5_HASH_SIZE];\n    mbedtls_md5(md5Buffer, sizeof(md5Buffer), md5);\n\n    // step 2: assign md5 hex to dst\n    // dst will be [md5 hex, ...]\n    size_t dstOffset = 0;\n    for (size_t imd5 = 0; imd5 < TBC_MD5_HASH_SIZE; imd5++) {\n        dst[dstOffset] = HEX_UPPERCASE_TABLE[md5[imd5] >> 4];\n        dstOffset++;\n        dst[dstOffset] = HEX_UPPERCASE_TABLE[md5[imd5] & 0x0F];\n        dstOffset++;\n    }\n\n    // step 3: add joining char\n    // dst will be [md5 hex, '|V', ...]\n    dst[dstOffset] = '|';\n    dstOffset++;\n    dst[dstOffset] = 'V';\n    dstOffset++;\n\n    // step 4: build dst buffer and compute helios hash\n    unsigned char heHash[TBC_HELIOS_HASH_SIZE];\n    tbc_heliosHash(dst, TBC_MD5_STR_SIZE, heHash);\n\n    // step 5: assign helios base32 to dst\n    // dst will be [md5 hex, '|V', heliosHash base32]\n    tbc_base32_encode(heHash, TBC_HELIOS_HASH_SIZE, (dst + dstOffset));\n}\n\nvoid tbc_c3_aid(const unsigned char* androidID, const unsigned char* uuid, unsigned char* dst) {\n    // step 1: set prefix\n    // dst will be ['A00-', ...]\n    dst[0] = 'A';\n    dst[1] = '0';\n    dst[2] = '0';\n    dst[3] = '-';\n    size_t dstOffset = 4;\n\n    // step 2: build src buffer and compute sha1\n    unsigned char sha1Buffer[sizeof(CUID3_PERFIX) + TBC_ANDROID_ID_SIZE + TBC_UUID_SIZE];\n\n    size_t sha1BuffOffset = 0;\n    memcpy(sha1Buffer, CUID3_PERFIX, sizeof(CUID3_PERFIX));\n    sha1BuffOffset += sizeof(CUID3_PERFIX);\n    memcpy(sha1Buffer + sha1BuffOffset, androidID, TBC_ANDROID_ID_SIZE);\n    sha1BuffOffset += TBC_ANDROID_ID_SIZE;\n    memcpy(sha1Buffer + sha1BuffOffset, uuid, TBC_UUID_SIZE);\n\n    unsigned char sha1[TBC_SHA1_HASH_SIZE];\n    mbedtls_sha1(sha1Buffer, sizeof(sha1Buffer), sha1);\n\n    // step 3: compute sha1 base32 and assign\n    // dst will be ['A00-', sha1 base32, ...]\n    tbc_base32_encode(sha1, TBC_SHA1_HASH_SIZE, (dst + dstOffset));\n    dstOffset += TBC_SHA1_BASE32_SIZE;\n\n    // step 4: add joining char\n    // dst will be ['A00-', sha1 base32, '-', ...]\n    dst[dstOffset] = '-';\n    dstOffset++;\n\n    // step 5: build dst buffer and compute helios hash\n    unsigned char heHash[TBC_HELIOS_HASH_SIZE];\n    tbc_heliosHash(dst, dstOffset, heHash);\n\n    // step 6: assign helios base32 to dst\n    // dst will be ['A00-', sha1 base32, '-', heliosHash base32]\n    tbc_base32_encode(heHash, TBC_HELIOS_HASH_SIZE, (dst + dstOffset));\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/tbcrypto/lib.c",
    "content": "#include \"tbcrypto/pywrap.h\"\n\n#include \"tbcrypto/bb64.h\"\n#include \"tbcrypto/const.h\"\n#include \"tbcrypto/cuid.h\"\n#include \"tbcrypto/rc442.h\"\n#include \"tbcrypto/sign.h\"\n\nPyObject* cuid_galaxy2(PyObject* Py_UNUSED(self), PyObject* args) {\n    unsigned char dst[TBC_CUID_GALAXY2_SIZE];\n    const unsigned char* androidID;\n    Py_ssize_t androidIDSize;\n\n    if (!PyArg_ParseTuple(args, \"s#\", &androidID, &androidIDSize)) {\n        PyErr_SetString(PyExc_TypeError, \"Failed to parse args\");\n        return NULL;\n    }\n\n    if (androidIDSize != 16) {\n        PyErr_Format(PyExc_ValueError, \"Invalid size of android_id. Expect 16, got %zu\", androidIDSize);\n        return NULL;\n    }\n\n    tbc_cuid_galaxy2(androidID, dst);\n\n    return PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, dst, TBC_CUID_GALAXY2_SIZE);\n}\n\nPyObject* c3_aid(PyObject* Py_UNUSED(self), PyObject* args) {\n    unsigned char dst[TBC_C3_AID_SIZE];\n    const unsigned char* androidID;\n    Py_ssize_t androidIDSize;\n    const unsigned char* uuid;\n    Py_ssize_t uuidSize;\n\n    if (!PyArg_ParseTuple(args, \"s#s#\", &androidID, &androidIDSize, &uuid, &uuidSize)) {\n        PyErr_SetString(PyExc_TypeError, \"Failed to parse args\");\n        return NULL;\n    }\n\n    if (androidIDSize != 16) {\n        PyErr_Format(PyExc_ValueError, \"Invalid size of android_id. Expect 16, got %zu\", androidIDSize);\n        return NULL;\n    }\n    if (uuidSize != 36) {\n        PyErr_Format(PyExc_ValueError, \"Invalid size of uuid. Expect 36, got %zu\", androidIDSize);\n        return NULL;\n    }\n\n    tbc_c3_aid(androidID, uuid, dst);\n\n    return PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, dst, TBC_C3_AID_SIZE);\n}\n\nPyObject* rc4_42(PyObject* Py_UNUSED(self), PyObject* args) {\n    unsigned char dst[TBC_RC4_SIZE];\n    const unsigned char* xyusMd5Str;\n    Py_ssize_t xyusMd5Size;\n    const unsigned char* cbcSecKey;\n    Py_ssize_t cbcSecKeySize;\n\n    if (!PyArg_ParseTuple(args, \"s#y#\", &xyusMd5Str, &xyusMd5Size, &cbcSecKey, &cbcSecKeySize)) {\n        PyErr_SetString(PyExc_TypeError, \"Failed to parse args\");\n        return NULL;\n    }\n\n    if (xyusMd5Size != 32) {\n        PyErr_Format(PyExc_ValueError, \"Invalid size of xyus_md5. Expect 32, got %zu\", xyusMd5Size);\n        return NULL;\n    }\n    if (cbcSecKeySize != 16) {\n        PyErr_Format(PyExc_ValueError, \"Invalid size of cbc_sec_key. Expect 16, got %zu\", cbcSecKeySize);\n        return NULL;\n    }\n\n    tbc_rc4_42(xyusMd5Str, cbcSecKey, dst);\n\n    return PyBytes_FromStringAndSize((char*)dst, TBC_RC4_SIZE);\n}\n\nPyObject* enuid(PyObject* Py_UNUSED(self), PyObject* args) {\n    unsigned char dst[TBC_ENUID_SIZE + 1];  // str ends with '\\0'\n    const unsigned char* cuid2;\n    Py_ssize_t cuid2Size;\n\n    if (!PyArg_ParseTuple(args, \"s#\", &cuid2, &cuid2Size)) {\n        PyErr_SetString(PyExc_TypeError, \"Failed to parse args\");\n        return NULL;\n    }\n\n    if (cuid2Size != TBC_CUID_GALAXY2_SIZE) {\n        PyErr_Format(PyExc_ValueError, \"Invalid size of cuid_galaxy2. Expect %zu, got %zu\", TBC_CUID_GALAXY2_SIZE,\n                     cuid2Size);\n        return NULL;\n    }\n\n    tbc_BB64Encode(cuid2, cuid2Size, 0, dst);\n\n    return PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, dst, TBC_ENUID_SIZE);\n}\n\nstatic PyMethodDef crypto_methods[] = {\n    {\"cuid_galaxy2\", (PyCFunction)cuid_galaxy2, METH_VARARGS, NULL},\n    {\"c3_aid\", (PyCFunction)c3_aid, METH_VARARGS, NULL},\n    {\"rc4_42\", (PyCFunction)rc4_42, METH_VARARGS, NULL},\n    {\"sign\", (PyCFunction)sign, METH_VARARGS, NULL},\n    {\"enuid\", (PyCFunction)enuid, METH_VARARGS, NULL},\n    {NULL, NULL, 0, NULL},\n};\n\nstatic PyModuleDef crypto_module = {PyModuleDef_HEAD_INIT, \"crypto\", NULL, -1, crypto_methods};\n\nPyMODINIT_FUNC PyInit_crypto(void) {\n    PyObject* mod = PyModule_Create(&crypto_module);\n    if (mod == NULL) {\n        return NULL;\n    }\n\n#ifdef Py_GIL_DISABLED\n    PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED);\n#endif\n\n    return mod;\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/tbcrypto/rc442.c",
    "content": "#include <stddef.h>\n\n#include \"tbcrypto/const.h\"\n\n#include \"tbcrypto/rc442.h\"\n\ntypedef struct rc4_42_context {\n    int x;\n    int y;\n    unsigned char m[256];\n} rc4_42_context;\n\nstatic void __tbc_rc442Setup(rc4_42_context* ctx, const unsigned char* key, unsigned int keyLen) {\n    int i, j, a;\n    unsigned int k;\n    unsigned char* m;\n\n    ctx->x = 0;\n    ctx->y = 0;\n    m = ctx->m;\n\n    for (i = 0; i < 256; i++) m[i] = (unsigned char)i;\n\n    j = k = 0;\n\n    for (i = 0; i < 256; i++, k++) {\n        if (k >= keyLen) k = 0;\n\n        a = m[i];\n        j = (j + a + key[k]) & 0xFF;\n        m[i] = m[j];\n        m[j] = (unsigned char)a;\n    }\n}\n\nstatic void __tbc_rc442Crypt(rc4_42_context* ctx, const unsigned char* src, size_t srcLen, unsigned char* dst) {\n    int x, y, a, b;\n    size_t i;\n    unsigned char* m;\n\n    x = ctx->x;\n    y = ctx->y;\n    m = ctx->m;\n\n    for (i = 0; i < srcLen; i++) {\n        x = (x + 1) & 0xFF;\n        a = m[x];\n        y = (y + a) & 0xFF;\n        b = m[y];\n\n        m[x] = (unsigned char)b;\n        m[y] = (unsigned char)a;\n\n        dst[i] = (unsigned char)(src[i] ^ m[(unsigned char)(a + b)]);\n        dst[i] = dst[i] ^ 42;  // different from general RC4\n    }\n\n    ctx->x = x;\n    ctx->y = y;\n}\n\nvoid tbc_rc4_42(const unsigned char* xyusMd5Str, const unsigned char* cbcSecKey, unsigned char* dst) {\n    rc4_42_context rc442Ctx;\n\n    __tbc_rc442Setup(&rc442Ctx, xyusMd5Str, TBC_MD5_STR_SIZE);\n    __tbc_rc442Crypt(&rc442Ctx, cbcSecKey, TBC_CBC_SECKEY_SIZE, dst);\n}\n"
  },
  {
    "path": "src/aiotieba/helper/crypto/src/tbcrypto/sign.c",
    "content": "#include <memory.h>\n#include <string.h>\n\n#include \"mbedtls/md5.h\"\n#include \"rapidjson/itoa.h\"\n\n#include \"tbcrypto/const.h\"\n\n#include \"tbcrypto/sign.h\"\n\nstatic const unsigned char SIGN_SUFFIX[] = {'t', 'i', 'e', 'b', 'a', 'c', 'l', 'i', 'e', 'n', 't', '!', '!', '!'};\n\nstatic void __tbc_pyStr2UTF8(const char** dst, size_t* dstSize, PyObject* pyoStr) {\n    if (PyUnicode_1BYTE_KIND == PyUnicode_KIND(pyoStr)) {\n        (*dst) = PyUnicode_DATA(pyoStr);\n        (*dstSize) = PyUnicode_GET_LENGTH(pyoStr);\n    } else {\n        (*dst) = PyUnicode_AsUTF8(pyoStr);\n        (*dstSize) = strlen(*dst);\n    }\n}\n\nPyObject* sign(PyObject* Py_UNUSED(self), PyObject* args) {\n    PyObject* items;\n\n    if (!PyArg_ParseTuple(args, \"O\", &items)) {\n        PyErr_SetString(PyExc_TypeError, \"Failed to parse args\");\n        return NULL;\n    }\n    if (!PyList_Check(items)) {\n        PyErr_SetString(PyExc_TypeError, \"Input should be List[Tuple[str, str | int]]]\");\n        return NULL;\n    }\n\n    Py_ssize_t listSize = PyList_GET_SIZE(items);\n\n    mbedtls_md5_context md5Ctx;\n    mbedtls_md5_init(&md5Ctx);\n    mbedtls_md5_starts(&md5Ctx);\n    char itoaBuffer[20];\n\n    for (Py_ssize_t iList = 0; iList < listSize; iList++) {\n        PyObject* item = PyList_GET_ITEM(items, iList);\n\n        if (!PyTuple_Check(item)) {\n            PyErr_SetString(PyExc_TypeError, \"List item should be Tuple[str, str | int]\");\n            return NULL;\n        }\n\n        PyObject* pyoKey = PyTuple_GetItem(item, 0);\n        if (!pyoKey) {\n            return NULL;  // IndexError\n        }\n\n        char* key;\n        size_t keySize;\n        __tbc_pyStr2UTF8((const char**)&key, &keySize, pyoKey);\n\n        // Warn: The last NULL is replaced by '=', DO NOT use `strlen` or similar method over `key` afterwards!\n        key[keySize] = '=';\n        keySize++;\n\n        mbedtls_md5_update(&md5Ctx, (unsigned char*)key, keySize);\n\n        PyObject* pyoVal = PyTuple_GetItem(item, 1);\n        if (!pyoVal) {\n            return NULL;  // IndexError\n        }\n\n        if (PyUnicode_Check(pyoVal)) {\n            const char* val;\n            size_t valSize;\n            __tbc_pyStr2UTF8(&val, &valSize, pyoVal);\n            mbedtls_md5_update(&md5Ctx, (unsigned char*)val, valSize);\n        } else if (PyLong_Check(pyoVal)) {\n            int64_t ival = PyLong_AsLongLong(pyoVal);\n            char* val = itoaBuffer;\n            char* valEnd = i64toa(ival, val);\n            size_t valSize = valEnd - val;\n            mbedtls_md5_update(&md5Ctx, (unsigned char*)val, valSize);\n        } else {\n            PyErr_SetString(PyExc_TypeError, \"item[1] should be str or int\");\n            return NULL;\n        }\n    }\n\n    mbedtls_md5_update(&md5Ctx, SIGN_SUFFIX, sizeof(SIGN_SUFFIX));\n\n    unsigned char md5[TBC_MD5_HASH_SIZE];\n    mbedtls_md5_finish(&md5Ctx, md5);\n\n    unsigned char dst[TBC_MD5_STR_SIZE];\n    size_t dstOffset = 0;\n    for (size_t imd5 = 0; imd5 < TBC_MD5_HASH_SIZE; imd5++) {\n        dst[dstOffset] = HEX_LOWERCASE_TABLE[md5[imd5] >> 4];\n        dstOffset++;\n        dst[dstOffset] = HEX_LOWERCASE_TABLE[md5[imd5] & 0x0F];\n        dstOffset++;\n    }\n\n    return PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, dst, TBC_MD5_STR_SIZE);\n}\n"
  },
  {
    "path": "src/aiotieba/helper/utils.py",
    "content": "from __future__ import annotations\n\nimport asyncio\nimport functools\nimport logging\nimport sys\nfrom datetime import datetime\nfrom typing import TYPE_CHECKING, Any\n\nfrom ..logging import get_logger\n\nif TYPE_CHECKING:\n    from collections.abc import Callable\n\nif sys.version_info >= (3, 11):\n    async_timeout = asyncio\nelse:\n    import async_timeout\n\ntry:\n    import orjson as jsonlib\n\n    def pack_json(obj: Any) -> str:\n        bjson: bytes = jsonlib.dumps(obj)\n        return bjson.decode(\"utf-8\")\n\nexcept ImportError:\n    import json as jsonlib\n\n    pack_json = functools.partial(jsonlib.dumps, separators=(\",\", \":\"))\n\nparse_json = jsonlib.loads\n\n\ndef is_portrait(portrait: Any) -> bool:\n    \"\"\"\n    简单判断输入是否符合portrait格式\n    \"\"\"\n\n    return isinstance(portrait, str) and portrait.startswith(\"tb.\")\n\n\ndef is_user_name(user_name: Any) -> bool:\n    \"\"\"\n    简单判断输入是否符合user_name格式\n    \"\"\"\n\n    return isinstance(user_name, str) and not user_name.startswith(\"tb.\")\n\n\ndef default_datetime() -> datetime:\n    return datetime(1970, 1, 1)\n\n\ndef timeout(delay: float, loop: asyncio.AbstractEventLoop) -> async_timeout.Timeout:\n    now = loop.time()\n    when = round(now) + delay\n    return async_timeout.timeout_at(when)\n\n\nif sys.version_info >= (3, 13):\n    from warnings import deprecated\nelse:\n    import warnings\n\n    def deprecated(reason):\n        def decorator(func):\n            @functools.wraps(func)\n            def wrapper(*args, **kwargs):\n                warnings.warn(f\"{func.__name__} is deprecated: {reason}\", DeprecationWarning, stacklevel=2)\n                return func(*args, **kwargs)\n\n            return wrapper\n\n        return decorator\n\n\ndef handle_exception(\n    null_factory: Callable[[], Any],\n    ok_log_level: int = logging.NOTSET,\n    err_log_level: int = logging.WARNING,\n):\n    \"\"\"\n    处理request抛出的异常 只能用于装饰类成员函数\n\n    Args:\n        null_factory (Callable[[], Any]): 空构造工厂 用于返回一个默认值\n        ok_log_level (int, optional): 正常日志等级. Defaults to logging.NOTSET.\n        err_log_level (int, optional): 异常日志等级. Defaults to logging.WARNING.\n    \"\"\"\n\n    def wrapper(func):\n        @functools.wraps(func)\n        async def awrapper(self, *args, **kwargs):\n            def _log(log_level: int, err: Exception | None = None) -> None:\n                logger = get_logger()\n                if logger.isEnabledFor(err_log_level):\n                    if err is None:\n                        err = \"Succeeded\"\n                    log_str = f\"{err}. args={args} kwargs={kwargs}\"\n                    record = logger.makeRecord(logger.name, log_level, None, 0, log_str, None, None, func.__name__)\n                    logger.handle(record)\n\n            try:\n                ret = await func(self, *args, **kwargs)\n\n                if ok_log_level:\n                    _log(ok_log_level)\n\n            except Exception as err:\n                _log(err_log_level, err)\n\n                ret = null_factory()\n                ret.err = err\n\n                return ret\n\n            else:\n                return ret\n\n        return awrapper\n\n    return wrapper\n"
  },
  {
    "path": "src/aiotieba/logging.py",
    "content": "import logging\nimport logging.handlers\nimport sys\nfrom pathlib import Path\n\nlogging.addLevelName(logging.FATAL, \"FATAL\")\nlogging.addLevelName(logging.WARNING, \"WARN\")\n\nlogging.raiseExceptions = False\n\n_FORMATTER = logging.Formatter(\"<{asctime}> [{levelname}] [{funcName}] {message}\", \"%Y-%m-%d %H:%M:%S\", style=\"{\")\n\n\nclass TiebaLogger(logging.Logger):\n    \"\"\"\n    日志记录器\n\n    Args:\n        name (str): 日志文件名(不含扩展名) 留空则自动设置为`sys.argv[0]`的文件名. Defaults to ''.\n        stream_log_level (int): 标准输出日志级别. Defaults to `logging.DEBUG`.\n    \"\"\"\n\n    def __init__(self, name: str = \"\", stream_log_level: int = logging.DEBUG) -> None:\n        if name == \"\":\n            name = Path(sys.argv[0]).stem\n        super().__init__(name)\n\n        stream_hd = logging.StreamHandler(sys.stdout)\n        stream_hd.setLevel(stream_log_level)\n        stream_hd.setFormatter(_FORMATTER)\n        self.addHandler(stream_hd)\n\n\nLOGGER = None\n\n\ndef get_logger() -> TiebaLogger:\n    \"\"\"\n    获取日志记录器\n\n    Returns:\n        TiebaLogger\n    \"\"\"\n\n    global LOGGER\n\n    if LOGGER is None:\n        LOGGER = TiebaLogger()\n\n    return LOGGER\n\n\ndef set_logger(new_logger: logging.Logger) -> None:\n    \"\"\"\n    更换aiotieba的日志记录器\n\n    Args:\n        new_logger (logging.Logger): 新日志记录器\n    \"\"\"\n\n    global LOGGER\n    LOGGER = new_logger\n\n\ndef set_formatter(formatter: logging.Formatter) -> None:\n    \"\"\"\n    更换aiotieba的日志格式\n\n    Args:\n        formatter (logging.Formatter): 新格式\n    \"\"\"\n\n    global _FORMATTER\n    _FORMATTER = formatter\n\n    if LOGGER is not None:\n        for hd in LOGGER.handlers:\n            hd.setFormatter(formatter)\n\n\n_FILELOG_ENABLED = False\n\n\ndef enable_filelog(log_level: int = logging.INFO, log_dir: Path = Path(\"log\"), backup_count: int = 5) -> None:\n    \"\"\"\n    启用文件日志\n\n    Args:\n        log_level (int): 文件日志级别. Defaults to `logging.INFO`.\n        log_dir (Path): 用于存放日志文件的文件夹. Defaults to Path('log').\n        backup_count (int): 时间轮转文件日志的保留文件数. Defaults to 5.\n    \"\"\"\n\n    global _FILELOG_ENABLED\n\n    if _FILELOG_ENABLED:\n        return\n\n    log_dir = Path(log_dir)\n    log_dir.mkdir(0o755, parents=True, exist_ok=True)\n\n    logger = get_logger()\n\n    file_hd = logging.handlers.TimedRotatingFileHandler(\n        log_dir / f\"{logger.name}.log\", when=\"MIDNIGHT\", backupCount=backup_count, encoding=\"utf-8\"\n    )\n    file_hd.setLevel(log_level)\n    file_hd.setFormatter(_FORMATTER)\n    logger.addHandler(file_hd)\n\n    _FILELOG_ENABLED = True\n"
  },
  {
    "path": "src/aiotieba/typing.py",
    "content": "from aiotieba.api._classdef import UserInfo\n\nfrom .api._classdef import contents\nfrom .api.get_comments import Comment, Comments\nfrom .api.get_posts import Post, Posts\nfrom .api.get_threads import Thread, Threads\n\nTypeUserInfo = UserInfo\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "import os\n\nimport pytest_asyncio\n\nimport aiotieba as tb\n\n\n@pytest_asyncio.fixture(loop_scope=\"session\")\nasync def client():\n    async with tb.Client(os.getenv(\"TB_BDUSS\"), os.getenv(\"TB_STOKEN\", \"\"), try_ws=True, proxy=True) as client:\n        yield client\n"
  },
  {
    "path": "tests/test_crypto.py",
    "content": "import pytest\n\nimport aiotieba as tb\nfrom aiotieba.helper.crypto import _sign, rc4_42\n\n\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_clib(client: tb.Client):\n    # await client._Client__init_z_id()\n    # assert client.account.z_id != ''\n\n    account = client.account\n    account.android_id = \"6723280942424242\"\n    account.uuid = \"67232809-3407-3442-4207-672346917aaa\"\n    assert account.cuid_galaxy2 == \"06C7F37D41256F25FABA97B885DB6EFB|VAPUDW7TA\"\n    assert account.c3_aid == \"A00-OGBA33NRAQASXI6FDZ4YAJFTK75EF4Y5-YVOG764X\"\n\n    data = [\n        (\"reverse\", 1999),\n        (\"hello_cosmic\", \"你好42\"),\n    ]\n    assert _sign(data) == \"248dab3e1fe46aea603cdc45c08f80eb\"\n\n    query_key = rc4_42(\"d0337b3b3d597c5f87a1c0c37139d87b\", b\"6723280942424242\")\n    assert query_key == b\"\\x9f\\xabU\\x14\\xa7\\x0e\\xb6k\\xc4wV\\xf2HN+.\"\n"
  },
  {
    "path": "tests/test_get_ats.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Ats(client: tb.Client):\n    ats = await client.get_ats()\n\n    ##### At #####\n    at = ats[0]\n\n    # UserInfo_at\n    user = at.user\n    assert user.user_id > 0\n    assert user.portrait != \"\"\n    assert user.nick_name_new != \"\"\n    assert user.nick_name == user.nick_name_new\n    assert user.show_name == user.nick_name_new\n    assert user.priv_like != 0\n    assert user.priv_reply != 0\n\n    # At\n    assert at.text != \"\"\n    assert at.fname != \"\"\n    assert at.tid > 0\n    assert at.pid > 0\n    assert at.author_id == user.user_id\n    assert at.create_time > 0\n"
  },
  {
    "path": "tests/test_get_blocks.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Blocks(client: tb.Client):\n    blocks = await client.get_blocks(21841105)\n\n    ##### Block #####\n    block = blocks[0]\n    assert block.user_id > 0\n    assert block.day > 0\n"
  },
  {
    "path": "tests/test_get_comments.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Comments(client: tb.Client):\n    comments = await client.get_comments(8211419000, 146544112004)\n    comment = comments[0]\n\n    ##### Forum_c #####\n    forum = comments.forum\n    assert forum.fid == 37574\n    assert forum.fname == \"starry\"\n    assert forum.category != \"\"\n    assert forum.subcategory != \"\"\n\n    ##### Thread_c #####\n    thread = comments.thread\n\n    # UserInfo_ct\n    user = thread.user\n    assert user.user_id > 0\n    assert user.portrait != \"\"\n    assert user.user_name != \"\"\n    assert user.nick_name_new != \"\"\n    assert user.nick_name == user.nick_name_new\n    assert user.show_name == user.nick_name_new\n    assert user.level > 0\n\n    # Thread_c\n    assert thread.title != \"\"\n    assert thread.fid > 0\n    assert thread.fname != \"\"\n    assert thread.tid == 8211419000\n    assert thread.author_id == user.user_id\n    assert thread.reply_num > 0\n\n    ##### Post_c #####\n    post = comments.post\n\n    # UserInfo_cp\n    user = post.user\n    assert user.user_id > 0\n    assert user.portrait != \"\"\n    assert user.user_name != \"\"\n    assert user.nick_name_new != \"\"\n    assert user.nick_name == user.nick_name_new\n    assert user.show_name == user.nick_name_new\n    assert user.level > 0\n    assert user.gender > 0\n    assert user.priv_like != 0\n    assert user.priv_reply != 0\n\n    # Post_c\n    assert post.text != \"\"\n    assert post.fid > 0\n    assert post.fname != \"\"\n    assert post.tid > 0\n    assert post.pid > 0\n    assert post.author_id == user.user_id\n    assert post.floor > 0\n    assert post.create_time > 0\n\n    # FragText\n    frag = post.contents.texts[0]\n    assert frag.text != \"\"\n\n    # FragAt\n    frag = post.contents.ats[0]\n    assert frag.text != \"\"\n    assert frag.user_id > 0\n\n    # FragVoice\n    frag = post.contents.voice\n    assert frag.md5 != \"\"\n    assert frag.duration > 0\n\n    # FragImage\n    frag = post.contents.imgs[0]\n    assert frag.src != \"\"\n    assert frag.big_src != \"\"\n    assert frag.origin_src != \"\"\n    assert len(frag.hash) == 40\n    assert frag.show_width > 0\n    assert frag.show_height > 0\n\n    # FragEmoji\n    frag = post.contents.emojis[0]\n    assert frag.desc != \"\"\n    assert frag.id == \"image_emoticon3\"\n\n    ##### Comment #####\n    comment = comments[0]\n\n    # UserInfo_c\n    user = comment.user\n    assert user.user_id > 0\n    assert user.portrait != \"\"\n    assert user.user_name != \"\"\n    assert user.nick_name_new != \"\"\n    assert user.nick_name == user.nick_name_new\n    assert user.show_name == user.nick_name_new\n    assert user.level > 0\n    assert user.gender > 0\n    assert user.priv_like != 0\n    assert user.priv_reply != 0\n\n    # Comment\n    assert comment.text != \"\"\n    assert comment.fid > 0\n    assert comment.fname != \"\"\n    assert comment.tid > 0\n    assert comment.ppid > 0\n    assert comment.pid > 0\n    assert comment.author_id == user.user_id\n    assert comment.floor > 0\n    assert comment.create_time > 0\n    assert comment.is_thread_author == (comment.author_id == thread.author_id)\n\n    # FragText\n    frag = comment.contents.texts[0]\n    assert frag.text != \"\"\n\n    # FragAt\n    frag = comment.contents.ats[0]\n    assert frag.text != \"\"\n    assert frag.user_id > 0\n\n    # FragVoice\n    frag = comment.contents.voice\n    assert frag.md5 != \"\"\n    assert frag.duration > 0\n\n    # FragEmoji\n    frag = comment.contents.emojis[0]\n    assert frag.desc != \"\"\n\n    # FragTiebaplus\n    frag = comment.contents.tiebapluses[0]\n    assert frag.text != \"\"\n    assert frag.url != \"\"\n    frag = comment.contents.tiebapluses[1]\n    assert frag.text != \"\"\n    assert frag.url != \"\"\n\n    comment = comments[1]\n    assert comment.reply_to_id != 0\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_FragLink(client: tb.Client):\n    comments = await client.get_comments(8211419000, 146546137439)\n\n    ##### Post_c #####\n    post = comments.post\n\n    # FragLink\n    frag = post.contents.links[0]\n    assert frag.title != \"\"\n    assert frag.url.host == \"tieba.baidu.com\"\n    assert frag.is_external is False\n    frag = post.contents.links[1]\n    assert frag.url.host == \"stackoverflow.com\"\n    assert frag.is_external is True\n\n    ##### Comment #####\n    comment = comments[0]\n\n    # FragLink\n    frag = comment.contents.links[0]\n    assert frag.title != \"\"\n    assert frag.url.host == \"tieba.baidu.com\"\n    assert frag.is_external is False\n    frag = comment.contents.links[1]\n    assert frag.url.host == \"stackoverflow.com\"\n    assert frag.is_external is True\n"
  },
  {
    "path": "tests/test_get_fans.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Fans(client: tb.Client):\n    fans = await client.get_fans(957339815)\n\n    ##### Fan #####\n    fan = fans[0]\n    assert fan.user_id > 0\n    assert fan.portrait != \"\"\n"
  },
  {
    "path": "tests/test_get_follow_forums.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_FollowForums(client: tb.Client):\n    forums = await client.get_follow_forums(4954297652)\n\n    ##### FollowForum #####\n    forum = forums[0]\n    assert forum.fid > 0\n    assert forum.fname != \"\"\n    assert forum.level > 0\n    assert forum.exp > 0\n"
  },
  {
    "path": "tests/test_get_follows.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Follows(client: tb.Client):\n    follows = await client.get_follows(957339815)\n\n    ##### Follow #####\n    follow = follows[0]\n    assert follow.user_id > 0\n    assert follow.portrait != \"\"\n"
  },
  {
    "path": "tests/test_get_forum_detail.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Forum_detail(client: tb.Client):\n    forum = await client.get_forum_detail(21841105)\n\n    ##### Forum_detail #####\n    assert forum.fid > 0\n    assert forum.fname != \"\"\n    assert forum.small_avatar != \"\"\n    assert forum.origin_avatar != \"\"\n    assert forum.slogan != \"\"\n    assert forum.member_num > 0\n    assert forum.post_num > 0\n    assert forum.has_bawu is True\n"
  },
  {
    "path": "tests/test_get_homepage.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Homepage(client: tb.Client):\n    homepage = await client.get_homepage(957339815)\n\n    ##### Thread_pf #####\n    thread = homepage[0]\n    assert thread.fid > 0\n    assert thread.fname != \"\"\n    assert thread.tid > 0\n    assert thread.pid > 0\n    assert thread.author_id == thread.user.user_id\n    assert thread.view_num > 0\n    assert thread.create_time > 0\n\n    ##### UserInfo_pf #####\n    user = homepage.user\n    assert user.user_id == thread.user.user_id\n    assert user.user_id > 0\n    assert user.portrait != \"\"\n    assert user.user_name != \"\"\n    assert user.nick_name_new != \"\"\n    assert user.tieba_uid > 0\n    assert user.glevel > 0\n    assert user.gender != tb.Gender.UNKNOWN\n    assert user.age > 0.0\n    assert user.post_num > 0\n    assert user.agree_num > 0\n    assert user.fan_num > 0\n    assert user.follow_num > 0\n    assert user.forum_num > 0\n    assert user.ip != \"\"\n"
  },
  {
    "path": "tests/test_get_posts.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Posts(client: tb.Client):\n    posts = await client.get_posts(8211419000)\n\n    ##### Forum_p #####\n    forum = posts.forum\n    assert forum.fid == 37574\n    assert forum.fname == \"starry\"\n    assert forum.category != \"\"\n    assert forum.subcategory != \"\"\n    assert forum.member_num > 0\n    assert forum.post_num > 0\n\n    ##### Thread_p #####\n    thread = posts.thread\n\n    # UserInfo_pt\n    user = thread.user\n    assert user.user_id > 0\n    assert user.portrait != \"\"\n    assert user.user_name != \"\"\n    assert user.nick_name_new != \"\"\n    assert user.nick_name == user.nick_name_new\n    assert user.show_name == user.nick_name_new\n    assert user.level > 0\n    assert user.glevel > 0\n    assert user.ip != \"\"\n    assert user.priv_like != 0\n    assert user.priv_reply != 0\n\n    # VoteInfo\n    vote_info = thread.vote_info\n    assert vote_info.title != \"\"\n    assert vote_info.total_vote > 0\n    assert vote_info.total_user > 0\n    option = vote_info.options[0]\n    assert option.vote_num > 0\n    assert option.text != \"\"\n\n    # Thread_p\n    assert thread.text != \"\"\n    assert thread.title != \"\"\n    assert thread.fid > 0\n    assert thread.fname != \"\"\n    assert thread.tid > 0\n    assert thread.pid == posts[0].pid\n    assert thread.author_id == posts[0].user.user_id\n    assert thread.view_num > 0\n    assert thread.reply_num > 0\n    assert thread.share_num > 0\n    assert thread.create_time > 0\n\n    ##### Post #####\n    assert len(posts) >= 2\n    post = posts[1]\n\n    # UserInfo_p\n    user = post.user\n    assert user.user_id > 0\n    assert user.portrait != \"\"\n    assert user.user_name != \"\"\n    assert user.nick_name_new != \"\"\n    assert user.show_name == user.nick_name_new\n    assert user.level > 0\n    assert user.glevel > 0\n    assert user.ip != \"\"\n    assert user.priv_like != 0\n    assert user.priv_reply != 0\n\n    # Post\n    assert post.text != \"\"\n    assert post.fid > 0\n    assert post.fname != \"\"\n    assert post.tid > 0\n    assert post.pid > 0\n    assert post.author_id == user.user_id\n    assert post.floor > 0\n    assert post.reply_num > 0\n    assert post.create_time > 0\n    assert post.is_thread_author == (post.author_id == thread.author_id)\n\n    # FragText\n    frag = post.contents.texts[0]\n    assert frag.text != \"\"\n\n    # FragAt\n    frag = post.contents.ats[0]\n    assert frag.text != \"\"\n    assert frag.user_id > 0\n\n    # FragVoice\n    frag = post.contents.voice\n    assert frag.md5 != \"\"\n\n    # FragImage\n    frag = post.contents.imgs[0]\n    assert frag.src != \"\"\n    assert frag.big_src != \"\"\n    assert frag.origin_src != \"\"\n    assert len(frag.hash) == 40\n    assert frag.show_width > 0\n    assert frag.show_height > 0\n\n    # FragEmoji\n    frag = post.contents.emojis[0]\n    assert frag.id == \"image_emoticon3\"\n    assert frag.desc != \"\"\n\n    # FragTiebaplus\n    frag = post.contents.tiebapluses[0]\n    assert frag.text != \"\"\n    assert frag.url != \"\"\n    frag = post.contents.tiebapluses[1]\n    assert frag.text != \"\"\n    assert frag.url != \"\"\n\n    # FragLink\n    post = posts[2]\n    frag = post.contents.links[0]\n    assert frag.title != \"\"\n    assert frag.url.host == \"tieba.baidu.com\"\n    assert frag.is_external is False\n    frag = post.contents.links[1]\n    assert frag.url.host == \"stackoverflow.com\"\n    assert frag.is_external is True\n\n    # Posts with video\n    posts = await client.get_posts(6205407601)\n\n    # FragVideo\n    frag = posts.thread.contents.video\n    assert frag.src != \"\"\n    assert frag.cover_src != \"\"\n    assert frag.duration > 0\n    assert frag.width > 0\n    assert frag.height > 0\n    assert frag.view_num > 0\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_ShareThread_pt(client: tb.Client):\n    posts = await client.get_posts(8213449397)\n\n    ##### ShareThread_pt #####\n    sthread = posts.thread.share_origin\n    assert sthread.text != \"\"\n    assert sthread.title != \"\"\n    assert sthread.author_id > 0\n    assert sthread.fid == 37574\n    assert sthread.fname == \"starry\"\n    assert sthread.tid > 0\n\n    # VoteInfo\n    vote_info = sthread.vote_info\n    assert vote_info.title != \"\"\n    assert vote_info.total_vote > 0\n    assert vote_info.total_user > 0\n    option = vote_info.options[0]\n    assert option.vote_num > 0\n    assert option.text != \"\"\n\n    # FragText\n    frag = sthread.contents.texts[1]\n    assert frag.text != \"\"\n\n    # FragAt\n    frag = sthread.contents.ats[0]\n    assert frag.text != \"\"\n    assert frag.user_id > 0\n\n    # FragVoice\n    frag = sthread.contents.voice\n    assert frag.md5 != \"\"\n    assert frag.duration > 0\n\n    # FragImage\n    frag = sthread.contents.imgs[0]\n    assert frag.src != \"\"\n    assert frag.big_src != \"\"\n    assert frag.origin_src != \"\"\n    assert len(frag.hash) == 40\n    assert frag.show_width > 0\n    assert frag.show_height > 0\n\n    # FragLink\n    frag = sthread.contents.links[0]\n    assert frag.title != \"\"\n    assert frag.url.host == \"tieba.baidu.com\"\n    assert frag.is_external is False\n"
  },
  {
    "path": "tests/test_get_recovers.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Recovers(client: tb.Client):\n    recovers = await client.get_recovers(21841105)\n\n    ##### Recover #####\n    recover = recovers[0]\n    assert recover.tid > 0\n    assert recover.op_show_name != \"\"\n    assert recover.op_time != 0\n"
  },
  {
    "path": "tests/test_get_threads.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_Threads(client: tb.Client):\n    fname = \"starry\"\n    threads = await client.get_threads(fname)\n\n    ##### Forum_t #####\n    forum = threads.forum\n    assert forum.fid == 37574\n    assert forum.fname == fname\n    assert forum.category != \"\"\n    assert forum.subcategory != \"\"\n    assert forum.member_num > 0\n    assert forum.post_num > 0\n    assert forum.thread_num > 0\n    assert forum.has_bawu is True\n    assert forum.has_rule is False\n\n    ##### Thread #####\n    assert len(threads) >= 2\n    for thread in threads:\n        # Normal Thread\n        if thread.tid == 8211419000:\n            # UserInfo_t\n            user = thread.user\n            assert user.user_id > 0\n            assert user.portrait != \"\"\n            assert user.user_name != \"\"\n            assert user.nick_name_new != \"\"\n            assert user.nick_name == user.nick_name_new\n            assert user.show_name == user.nick_name_new\n            assert user.level > 0\n            assert user.glevel > 0\n            assert user.priv_like != 0\n            assert user.priv_reply != 0\n\n            # VoteInfo\n            vote_info = thread.vote_info\n            assert vote_info.title != \"\"\n            option = vote_info.options[0]\n            assert option.vote_num > 0\n            assert option.text != \"\"\n\n            # Thread\n            assert thread.text != \"\"\n            assert thread.title != \"\"\n            assert thread.fid > 0\n            assert thread.fname != \"\"\n            assert thread.pid > 0\n            assert thread.author_id == user.user_id\n            assert thread.view_num > 0\n            assert thread.reply_num > 0\n            assert thread.create_time > 0\n            assert thread.last_time > 0\n\n            # FragText\n            frag = thread.contents.texts[0]\n            assert frag.text != \"\"\n\n            # FragAt\n            frag = thread.contents.ats[0]\n            assert frag.text != \"\"\n            assert frag.user_id > 0\n\n            # FragVoice\n            frag = thread.contents.voice\n            assert frag.md5 != \"\"\n            assert frag.duration > 0\n\n            # FragImage\n            frag = thread.contents.imgs[0]\n            assert frag.src != \"\"\n            assert frag.big_src != \"\"\n            assert frag.origin_src != \"\"\n            assert len(frag.hash) == 40\n            assert frag.show_width > 0\n            assert frag.show_height > 0\n\n            # FragEmoji\n            frag = thread.contents.emojis[0]\n            assert frag.id == \"image_emoticon2\"\n            assert frag.desc != \"\"\n\n            # FragTiebaplus\n            frag = thread.contents.tiebapluses[0]\n            assert frag.text != \"\"\n            assert frag.url != \"\"\n            frag = thread.contents.tiebapluses[1]\n            assert frag.text != \"\"\n            assert frag.url != \"\"\n\n            # FragLink\n            frag = thread.contents.links[0]\n            assert frag.title != \"\"\n            assert frag.url.host == \"tieba.baidu.com\"\n            assert frag.is_external is False\n            frag = thread.contents.links[1]\n            assert frag.url.host == \"stackoverflow.com\"\n            assert frag.is_external is True\n\n        # Share Thread\n        elif thread.tid == 8213449397:\n            sthread = thread.share_origin\n\n            # FragText\n            frag = sthread.contents.texts[0]\n            assert frag.text != \"\"\n\n            # FragAt\n            frag = sthread.contents.ats[0]\n            assert frag.text != \"\"\n            assert frag.user_id != 0\n\n            # FragLink\n            frag = sthread.contents.links[0]\n            assert frag.title != \"\"\n            assert frag.url.host == \"tieba.baidu.com\"\n            assert frag.is_external is False\n\n            # FragVoice\n            frag = sthread.contents.voice\n            assert frag.md5 != \"\"\n            assert frag.duration > 0\n\n            # FragImage\n            frag = sthread.contents.imgs[0]\n            assert frag.src != \"\"\n            assert frag.big_src != \"\"\n            assert frag.origin_src != \"\"\n            assert len(frag.hash) == 40\n            assert frag.show_width > 0\n            assert frag.show_height > 0\n\n            # VoteInfo\n            vote_info = sthread.vote_info\n            assert vote_info.title != \"\"\n            option = vote_info.options[0]\n            assert option.vote_num > 0\n            assert option.text != \"\"\n\n        # Share Video\n        elif thread.tid == 8553772146:\n            sthread = thread.share_origin\n\n            # FragVideo\n            frag = sthread.contents.video\n            assert frag.src != \"\"\n            assert frag.cover_src != \"\"\n            assert frag.duration > 0\n            assert frag.width > 0\n            assert frag.height > 0\n            assert frag.view_num > 0\n"
  },
  {
    "path": "tests/test_get_user_info.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_get_user_info(client: tb.Client):\n    self_info = await client.get_self_info(tb.ReqUInfo.BASIC)\n    assert self_info.user_id > 0\n    assert self_info.portrait != \"\"\n    assert self_info.user_name != \"\"\n\n    self_info = await client.get_self_info()\n    assert self_info.user_id > 0\n    assert self_info.portrait != \"\"\n    assert self_info.user_name != \"\"\n    assert self_info.nick_name_new != \"\"\n    assert self_info.tieba_uid > 0\n    assert self_info.glevel > 0\n    assert self_info.age > 0\n    assert self_info.ip != \"\"\n    assert self_info.post_num > 0\n    assert self_info.priv_like != 0\n    assert self_info.priv_reply != 0\n\n    homepage = await client.get_homepage(self_info.user_id)\n    user = homepage.user\n    assert user.user_id == self_info.user_id\n    assert user.portrait == self_info.portrait\n    assert user.user_name == self_info.user_name\n\n    user = await client.get_user_info(self_info.portrait, tb.enums.ReqUInfo.BASIC)\n    assert user.user_id == self_info.user_id\n    assert user.portrait == self_info.portrait\n    assert user.user_name == self_info.user_name\n\n    user = await client.get_user_info(user.user_id, tb.enums.ReqUInfo.BASIC)\n    assert user.user_id == self_info.user_id\n    assert user.portrait == self_info.portrait\n    assert user.user_name == self_info.user_name\n\n    user = await client.get_user_info(user.user_name, tb.enums.ReqUInfo.BASIC)\n    assert user.user_id == self_info.user_id\n    assert user.portrait == self_info.portrait\n    assert user.user_name == self_info.user_name\n\n    user = await client.get_user_info(user.portrait)\n    assert user.user_id == self_info.user_id\n    assert user.portrait == self_info.portrait\n    assert user.user_name == self_info.user_name\n    assert user.tieba_uid > 0\n    assert user.glevel > 0\n    assert user.age > 0\n    assert user.ip != \"\"\n    assert user.post_num > 0\n    assert user.priv_like != 0\n    assert user.priv_reply != 0\n\n    user = await client.get_user_info(user.user_id)\n    assert user.user_id == self_info.user_id\n    assert user.portrait == self_info.portrait\n    assert user.user_name == self_info.user_name\n    assert user.tieba_uid > 0\n    assert user.glevel > 0\n    assert user.age > 0\n    assert user.ip != \"\"\n    assert user.post_num > 0\n    assert user.priv_like != 0\n    assert user.priv_reply != 0\n\n    user = await client.tieba_uid2user_info(3356245857)\n    assert user.user_id == self_info.user_id\n    assert user.portrait == self_info.portrait\n    assert user.user_name == self_info.user_name\n    assert user.tieba_uid > 0\n    assert user.age > 0\n"
  },
  {
    "path": "tests/test_get_user_posts.py",
    "content": "import pytest\n\nimport aiotieba as tb\n\n\n@pytest.mark.flaky(reruns=2, reruns_delay=5.0)\n@pytest.mark.asyncio(loop_scope=\"session\")\nasync def test_get_user_posts(client: tb.Client):\n    user_id = 4954297652\n    postss = await client.get_user_posts(user_id)\n\n    ##### UserPosts #####\n    posts = postss[0]\n    assert posts.fid > 0\n    assert posts.tid > 0\n\n    ##### UserPost #####\n    post = posts[0]\n    assert len(post.contents) > 0\n    assert post.fid > 0\n    assert post.tid > 0\n    assert post.pid > 0\n    assert post.create_time > 0\n\n    ##### UserInfo_u #####\n    user = post.user\n    assert user.user_id == post.author_id\n    assert user.portrait != \"\"\n    assert user.user_name != \"\"\n    assert user.nick_name_new != \"\"\n"
  }
]