├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature-request.yaml │ └── bug-report.yaml └── workflows │ ├── dpm-update.yml │ └── codeql-analysis.yml ├── src ├── console.py ├── __init__.py ├── live_exit.py ├── up_to_date.py ├── login.py ├── agent.py └── getGift.py ├── .gitattributes ├── images ├── BGRGS.png └── Demo.png ├── .flake8 ├── pyproject.toml ├── LICENSE ├── main.py ├── .gitignore ├── README.md └── poetry.lock /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /src/console.py: -------------------------------------------------------------------------------- 1 | from rich.console import Console 2 | 3 | console = Console() -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /images/BGRGS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boxie123/Bilibili_GetReceivedGiftStream/HEAD/images/BGRGS.png -------------------------------------------------------------------------------- /images/Demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boxie123/Bilibili_GetReceivedGiftStream/HEAD/images/Demo.png -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ( # noqa: F401 2 | agent, 3 | console, 4 | getGift, 5 | live_exit, 6 | login, 7 | up_to_date, 8 | ) 9 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = 3 | __pycache__, 4 | .github, 5 | .venv, 6 | build, 7 | dist, 8 | agent.py, 9 | max-line-length = 120 10 | statistics = True 11 | ; ignore = 12 | ; E501, 13 | -------------------------------------------------------------------------------- /.github/workflows/dpm-update.yml: -------------------------------------------------------------------------------- 1 | name: Update dependencies 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | 7 | jobs: 8 | update-dependencies: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - name: Update dependencies 14 | uses: pdm-project/update-deps-action@main 15 | with: 16 | # The update strategy, can be 'reuse', 'eager' or 'all' 17 | update-strategy: all -------------------------------------------------------------------------------- /src/live_exit.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | from sys import exit 3 | 4 | from rich.live import Live 5 | 6 | from .console import console 7 | 8 | 9 | def live_exit(seconds: int = 5): 10 | console.print("\n") 11 | with Live(f"感谢使用,程序将在 [b green]{seconds}[/b green] 秒后退出", console=console) as live: 12 | for i in range(seconds): 13 | sleep(1) 14 | live.update(f"感谢使用,程序将在 [b green]{seconds - 1 - i}[/b green] 秒后退出") 15 | exit(0) 16 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "bilibili-getreceivedgiftstream" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["铂屑 <80164656+boxie123@users.noreply.github.com>"] 6 | readme = "README.md" 7 | packages = [{include = "bilibili_getreceivedgiftstream"}] 8 | 9 | [tool.poetry.dependencies] 10 | python = "~3.11.4" 11 | qrcode = "^7.4.2" 12 | pillow = "^9.5.0" 13 | httpx = "^0.24.0" 14 | xlsxwriter = "^3.1.0" 15 | rich = "^13.3.5" 16 | pyinstaller = "^5.10.1" 17 | 18 | [tool.poetry.dev-dependencies] 19 | flake8 = "^5.0.4" 20 | black = {version = "^22.6.0", allow-prereleases = true} 21 | 22 | [build-system] 23 | requires = ["poetry-core"] 24 | build-backend = "poetry.core.masonry.api" 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: 功能建议 2 | description: 改进现有功能或提出新功能 3 | title: '【功能建议】' 4 | labels: [ "feature" ] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## 请补全标题并认真填写下方内容 11 | 12 | - type: input 13 | id: version 14 | attributes: 15 | label: Bilibili_GetReceivedGiftStream 版本 16 | description: 可在文件名或 `main.py` 中查看 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | id: feature_description 22 | attributes: 23 | label: 功能建议 24 | description: 想要改进或者添加的功能 25 | validations: 26 | required: true 27 | 28 | - type: textarea 29 | id: extra_info 30 | attributes: 31 | label: 额外信息(可选) 32 | description: 如:为什么需要这个功能?在什么场景下适用? 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 铂屑 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/up_to_date.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | 3 | from .console import console 4 | 5 | 6 | def main(now_tag): 7 | console.print("\n\n") 8 | console.rule("[b]检测更新", characters="=") 9 | header = {"accept": "application/vnd.github.v3+json"} 10 | url = ( 11 | "https://api.github.com/repos/boxie123/Bilibili_GetReceivedGiftStream/releases" 12 | ) 13 | param = {"per_page": 1, "page": 1} 14 | with console.status("正在检测更新(此步骤不影响上述已生成的文件,若无需检测可直接关闭程序)", spinner="line"): 15 | releases_info = httpx.get( 16 | url, headers=header, params=param, timeout=None, verify=False 17 | ).json() 18 | tag_name = releases_info[0]["tag_name"] 19 | if tag_name == now_tag: 20 | console.print("提示:当前已是最新版本") 21 | else: 22 | new_html_url = releases_info[0]["html_url"] 23 | console.print( 24 | "提示:检测到更新,当前版本[blue]{}[/blue],最新版本[green]{}[/green]".format( 25 | now_tag, tag_name 26 | ) 27 | ) 28 | console.print("\n可按住 [b]Ctrl[/b] 并单击下面的链接到浏览器下载最新版本:") 29 | console.print(new_html_url, style=f"link {new_html_url}") 30 | 31 | 32 | # if __name__ == "__main__": 33 | # main("v0.7.0") 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: 错误回报 2 | description: 在使用 Bilibili_GetReceivedGiftStream 的过程中遇到了错误 3 | title: '【错误回报】' 4 | labels: [ "bug?" ] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## 请补全标题并认真填写下方内容 11 | 12 | 请清晰描述问题,不必严格套用模板。 13 | 14 | - type: input 15 | id: system_version 16 | attributes: 17 | label: 运行环境 18 | description: 如 Windows Server 2012、CentOS 8.2 等 19 | validations: 20 | required: true 21 | 22 | - type: input 23 | id: version 24 | attributes: 25 | label: Bilibili_GetReceivedGiftStream 版本 26 | description: 可在文件名或 `main.py` 中查看 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | id: bug_info 32 | attributes: 33 | label: 错误描述 34 | description: 描述你遇到的问题,需要怎么复现?期望的结果是什么? 35 | validations: 36 | required: true 37 | 38 | - type: textarea 39 | id: log 40 | attributes: 41 | label: 报错日志以及其他描述(如果有) 42 | description: 通过 CMD 打开程序而非双击 43 | value: | 44 | ### 日志 45 | 46 |
47 | 48 | 49 | 50 | 51 | ```bash 52 | # 在这里填写你的报错日志 53 | ``` 54 | 55 |
56 | 57 | ### 其他补充描述 58 | 59 | 60 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from rich.panel import Panel 4 | from rich.prompt import Prompt 5 | from rich.tree import Tree 6 | 7 | from src import getGift, login, up_to_date 8 | from src.console import console 9 | from src.live_exit import live_exit 10 | 11 | if __name__ == "__main__": 12 | # 获取用户登录状态 13 | console.print( 14 | Panel.fit( 15 | r""" 16 | ,---, ,---, ,----.. ,---,. 17 | ' .' \ ,`--.' | / / \ ,' .' | 18 | / ; '. | : : | : : ,---.' | 19 | : : \ : | ' . | ;. / | | .' 20 | : | /\ \ | : | . ; /--` : : |-, 21 | | : ' ;. : ' ' ; ; | ; __ : | ;/| 22 | | | ;/ \ \ | | | | : |.' .' | : .' 23 | ' : | \ \ ,' ' : ; . | '_.' : | | |-, 24 | | | ' '--' | | ' ' ; : \ | ' : ;/| 25 | | : : ' : | ' | '/ .' | | \ 26 | | | ,' ; |.' | : / | : .' 27 | `--'' '---' \ \ .' | | ,' 28 | `---` `----' 29 | """, 30 | title="本程序美术资源由 [b bright_magenta]艾鸽泰尔德[/b bright_magenta] 友情提供", 31 | subtitle="UID 1485569", 32 | ) 33 | ) 34 | client = login.main() 35 | 36 | function_dict = { 37 | 0: "退出", 38 | 1: "生成[b green]大航海[/b green]信息记录([b blue]xlsx[/b blue]格式)", 39 | 2: "生成可直接导入 [b green]BiliMessenger[/b green] 使用的大航海数据列表([b blue]csv[/b blue]格式)", 40 | 3: "生成[b green]所有礼物流水[/b green]列表([b blue]xlsx[/b blue]格式)", 41 | 4: "同时生成[b yellow]上述三个[/b yellow]文件", 42 | } 43 | 44 | # 询问用户使用什么功能 45 | console.print("\n本程序目前拥有的功能:\n") 46 | 47 | function_list_tree = Tree("[b]功能列表[/b]") 48 | for key, values in function_dict.items(): 49 | function_list_tree.add(f"[b][[cyan]{key}[/cyan]][/b] {values}") 50 | console.print(function_list_tree) 51 | 52 | console.print("之后会要求您输入想要查询的日期区间([b red]闭区间[/b red],统计结果包含开始和结束日期的礼物数据)") 53 | console.print("\n[i]下列所有问题直接回车将默认为 [b cyan]括号中[/b cyan] 结果[/i]") 54 | 55 | choice = Prompt.ask( 56 | "\n请输入[b blue]相应数字[/b blue]来使用相对应的功能:", 57 | choices=[str(key) for key in function_dict.keys()], 58 | default="4", 59 | ) 60 | if choice == "0": 61 | live_exit() 62 | 63 | # 使用功能 64 | gift_info = getGift.GiftInfo(client) 65 | console.rule("[b]获取礼物信息", characters="=") 66 | asyncio.run(gift_info.main(choice)) 67 | 68 | # 检测更新 69 | try: 70 | up_to_date.main("v0.8.4") 71 | except Exception: 72 | console.print("检测失败") 73 | 74 | # 防止快速退出 75 | console.input("\n\n按回车 [b blue]退出[/b blue] 程序\n") 76 | live_exit() 77 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '27 21 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # PyCharm 148 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 149 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 150 | # and can be added to the global gitignore or merged into this file. For a more nuclear 151 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 152 | .idea/ 153 | 154 | # My 155 | bzcookies.txt 156 | bzcookies 157 | *.xls 158 | *.csv 159 | *.ico 160 | *.json 161 | *.xlsx 162 | test/ 163 | .pdm.toml 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | [![Bilibili_GetReceivedGiftStream](https://user-images.githubusercontent.com/74038190/212747903-e9bdf048-2dc8-41f9-b973-0e72ff07bfba.gif)](https://boxie123.github.io/Bilibili-GetReceivedGiftStream/) 4 | 5 | # Bilibili_GetReceivedGiftStream 6 | 7 | 登录并获取bilibili账号某段时间礼物流水数据,生成表格。 8 | 9 | ![GitHub](https://img.shields.io/github/license/boxie123/Bilibili_GetReceivedGiftStream) 10 | ![Python](https://img.shields.io/badge/Python-3.10+-blue) 11 | ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/boxie123/Bilibili_GetReceivedGiftStream?include_prereleases) 12 | 13 | [![qq group](https://img.shields.io/badge/QQ%E7%BE%A4-1054608979-hotpink)](https://jq.qq.com/?_wv=1027&k=mEy0fIIq) 14 | 15 |
16 | 17 | ## 功能列表 18 | 19 | 1. 统计指定时间段的大航海记录(开通时间,用户名,uid),附表生成各大航海数量与总积分(xlsx格式) 20 | 21 | 例表: 22 | 23 | 表1:上舰时间 24 | 25 | | ID | UID | 舰长 | 提督 | 总督 | 26 | | ---- | -------- | ------------------- | ---- | ---- | 27 | | 铂屑 | 35192025 | 2022-04-29 09:24:39 | | | 28 | 29 | 表2:积分计算 30 | 31 | | ID | UID | 总积分 | 舰长 | 提督 | 总督 | 32 | | ---- | -------- | ------ | ---- | ---- | ---- | 33 | | 铂屑 | 35192025 | 1 | 1 | 0 | 0 | 34 | 35 | 2. 统计指定时间段的大航海记录(用户,uid,大航海类型)。生成可直接导入 [BiliMessenger](https://github.com/Xinrea/BiliMessengerElectron)(私信群发助手)使用的数据列表(csv格式) 36 | 37 | 3. 生成指定时间段收到所有礼物流水列表(用户,uid,礼物名,电池数),附表为(用户,uid,礼物名,数量)(xlsx格式) 38 | 39 | 例表: 40 | 41 | 表1:电池数量 42 | 43 | | ID | UID | 小花花(id:31036) | 辣条(id:1) | 这个好诶(id:31213) | 舰长(id:10003) | SUM | 44 | | ------ | -------- | ---------------- | ---------- | ------------------ | -------------- | ---- | 45 | | hyt658 | 23262005 | 5 | 0 | 10 | | 15 | 46 | | 铂屑 | 35192025 | | | | 1380 | 1380 | 47 | 48 | 表2:数目 49 | 50 | | ID | UID | 小花花(id:31036) | 辣条(id:1) | 这个好诶(id:31213) | 舰长(id:10003) | 51 | | ------ | -------- | ---------------- | ---------- | ------------------ | -------------- | 52 | | hyt658 | 23262005 | 5 | 2 | 1 | | 53 | | 铂屑 | 35192025 | | | | 1 | 54 | 55 | 56 | 4. 同时生成上述三个文件 57 | 58 | ********************************* 59 | 60 | ## 注意事项 61 | 62 | > [!CAUTION] 63 | > 1. 由于本api仅提供**近180天**的数据,超出范围的数据均为0,请勿使用本程序查询过于久远的记录,防止造成误导。 64 | > 65 | > 1. 本程序会自动生成数个文件,分别为 66 | > 67 | > - `bzcookies` 68 | > 69 | > - `yyyy年mm月nn日至yyyy年mm月nn日礼物统计(大航海).xlsx/csv` 70 | > 71 | > - `yyyy年mm月nn日至yyyy年mm月nn日礼物统计.xlsx` 72 | > 73 | > 请确保运行本程序前,当前文件夹中**无同名文件**,否则会直接覆盖造成数据损失。 74 | > 75 | > 1. **其中 `bzcookies` 为重要账号登录信息,请谨慎保管、切勿泄漏,否则可能导致账号被盗用等后果。** 76 | 77 | **************************************** 78 | 79 | ## 使用方法 80 | 81 | ### 方法一:下载打包好的程序(推荐) 82 | 83 | 下载最新的 [Release](https://github.com/boxie123/Bilibili_GetReceivedGiftStream/releases) 中的 `.exe` 文件,双击运行。 84 | 85 | Demo: 86 | 87 | ![Demo](images/Demo.png) 88 | 89 | ### 方法二:手动构建 90 | 91 | > 需使用 [git](https://git-scm.com/)、[pdm](https://github.com/pdm-project/pdm)、[python](https://www.python.org/) 等,不建议小白手动构建。 92 | 93 | #### 克隆Git仓库: 94 | 95 | ```sh 96 | git clone https://github.com/boxie123/Bilibili_GetReceivedGiftStream.git 97 | cd Bilibili_GetReceivedGiftStream 98 | ``` 99 | 100 | #### 构建虚拟环境: 101 | 102 | 使用 [pdm](https://github.com/pdm-project/pdm): 103 | 104 | ```sh 105 | pdm install --prod 106 | ``` 107 | 108 | #### 运行: 109 | 110 | ```sh 111 | pdm run python main.py 112 | ``` 113 | 114 | ************************************ 115 | 116 | ## Q & A 117 | 118 | Q: 程序闪退? 119 | 120 | A: 大概率是网络问题导致爬取过程中链接超时。 121 | 122 | -------------------------------------- 123 | 124 | Q: 大航海表格可以生成,但礼物表格生成失败? 125 | 126 | A: 0.7.0及以下版本使用 xls 的 `SUM` 求和公式,但 xls 为 Excel 2007 之前的版本,求和公式最大参数限制为30个, 127 | 目前已弃用 xls 改用 xlsx ,请及时更新至0.8.0及以上版本。 128 | 129 | ---------------------------------------- 130 | 131 | 若排除以上原因后仍然闪退,请打开 CMD 窗口手动运行程序,带着**报错信息**来提 [Issues](https://github.com/boxie123/Bilibili_GetReceivedGiftStream/issues) 吧! 132 | 133 | ## 支持与贡献 134 | 135 | 觉得好用可以给这个项目点个 Star 或者去关注非常可爱的 [艾鸽泰尔德](https://space.bilibili.com/1485569)。 136 | 137 | 有意见或者建议也欢迎提交 [Issues](https://github.com/boxie123/Bilibili_GetReceivedGiftStream/issues) 和 [Pull requests](https://github.com/boxie123/Bilibili_GetReceivedGiftStream/pulls)。 138 | 139 | ************************************** 140 | 141 | 开发者:[铂屑](https://github.com/boxie123)、[hyt658](https://github.com/hyt658) 142 | 143 | 开发思路:详见[《Bilibili GetReceivedGiftStream》](https://boxie123.github.io/Bilibili-GetReceivedGiftStream/)。 144 | ~~在写了再写了(目移)~~ 145 | 146 | ————致 [@艾鸽泰尔德](https://space.bilibili.com/1485569) ,希望可爱的鸽宝统计礼物不再辛苦。 147 | -------------------------------------------------------------------------------- /src/login.py: -------------------------------------------------------------------------------- 1 | import http.cookiejar as cookielib 2 | import json 3 | import os 4 | import tempfile 5 | import tkinter 6 | import tkinter.font 7 | 8 | import httpx 9 | import qrcode 10 | from PIL.ImageTk import PhotoImage 11 | 12 | from . import agent 13 | from .console import console 14 | 15 | API = { 16 | "qrcode": { 17 | "get_qrcode_and_token": { 18 | "url": "https://passport.bilibili.com/x/passport-login/web/qrcode/generate", 19 | "method": "GET", 20 | "verify": False, 21 | "comment": "请求二维码及登录密钥", 22 | }, 23 | "get_events": { 24 | "url": "https://passport.bilibili.com/x/passport-login/web/qrcode/poll", 25 | "method": "GET", 26 | "verify": False, 27 | "data": {"qrcode_key": "str: 登陆密钥"}, 28 | "comment": "获取最新信息", 29 | }, 30 | } 31 | } 32 | 33 | headers = { 34 | "User-Agent": agent.get_user_agents(), 35 | "Referer": "https://www.bilibili.com/", 36 | } 37 | headerss = { 38 | "User-Agent": agent.get_user_agents(), 39 | "Host": "passport.bilibili.com", 40 | "Referer": "https://passport.bilibili.com/login", 41 | } 42 | 43 | # ---------------------------------------------------------------- 44 | # 二维码登录 45 | # ---------------------------------------------------------------- 46 | 47 | photo = None # 图片的全局变量 48 | client = httpx.Client(verify=False) 49 | login_key = "" 50 | qrcode_image = None 51 | is_destroy = False 52 | id_ = 0 # 事件 id,用于取消 after 绑定 53 | 54 | 55 | def islogin(): 56 | """检查登陆是否有效 57 | 58 | Returns: 59 | client: httpx.Client 60 | """ 61 | global client 62 | try: 63 | client.cookies.jar.load(ignore_discard=True) 64 | except Exception: 65 | console.print("[b green]bzcookies[/b green] 载入失败") 66 | loginurl = client.get( 67 | "https://api.bilibili.com/x/web-interface/nav", headers=headers 68 | ).json() 69 | if loginurl["code"] == 0: 70 | console.print( 71 | "Cookies值有效,[b blue]{}[/b blue],已登录!".format(loginurl["data"]["uname"]) 72 | ) 73 | return client, True 74 | else: 75 | console.print("Cookies值已经失效,[blue]已弹出二维码[/blue],请重新扫码登录!") 76 | return client, False 77 | 78 | 79 | def make_qrcode(url) -> str: 80 | qr = qrcode.QRCode() 81 | qr.add_data(url) 82 | img = qr.make_image() 83 | img.save(os.path.join(tempfile.gettempdir(), "qrcode.png")) 84 | return os.path.join(tempfile.gettempdir(), "qrcode.png") 85 | 86 | 87 | def login_with_qrcode(): 88 | """通过二维码登陆 89 | 90 | Raises: 91 | Exception: 登陆失败 92 | """ 93 | 94 | global photo 95 | global login_key, qrcode_image 96 | global id_ 97 | global client 98 | 99 | root = tkinter.Tk() 100 | root.title("扫码登录") 101 | qrcode_image = update_qrcode() 102 | photo = PhotoImage(file=qrcode_image) 103 | qrcode_label = tkinter.Label(root, image=photo, width=500, height=500) 104 | qrcode_label.pack() 105 | big_font = tkinter.font.Font(root, size=25) 106 | log = tkinter.Label(root, text="二维码未失效,请扫码!", font=big_font, fg="red") 107 | log.pack() 108 | 109 | def update_events(): 110 | global id_ 111 | global is_destroy, client 112 | events_api = API["qrcode"]["get_events"] 113 | try: 114 | events = client.get( 115 | events_api["url"], 116 | params={"qrcode_key": login_key}, 117 | headers=headerss, 118 | ).json() 119 | except json.decoder.JSONDecodeError: 120 | console.print("[b green]更新状态失败[/b green] 正在重试") 121 | id_ = root.after(500, update_events) 122 | root.update() 123 | return 124 | if "code" in events.keys() and events["code"] == -412: 125 | raise Exception(events["message"]) 126 | if events["data"]["code"] == 86101: 127 | log.configure(text="二维码未失效,请扫码!", fg="red", font=big_font) 128 | elif events["data"]["code"] == 86090: 129 | log.configure(text="已扫码,请确认!", fg="orange", font=big_font) 130 | elif events["data"]["code"] == 86038: 131 | update_qrcode() 132 | log.configure(text="二维码已刷新,请重新扫码", fg="blue", font=big_font) 133 | elif isinstance(events["data"], dict): 134 | url = events["data"]["url"] 135 | client.get(url, headers=headers) 136 | client.cookies.jar.save() 137 | 138 | log.configure(text="登入成功!", fg="green", font=big_font) 139 | 140 | root.after(2000, destroy) 141 | id_ = root.after(2000, update_events) 142 | root.update() 143 | 144 | def destroy(): 145 | global id_ 146 | root.after_cancel(id_) # type: ignore 147 | root.destroy() 148 | 149 | root.after(500, update_events) 150 | root.mainloop() 151 | root.after_cancel(id_) # type: ignore 152 | 153 | 154 | def update_qrcode() -> str: 155 | """更新二维码 156 | 157 | Returns: 158 | str: 二维码 159 | """ 160 | global login_key, qrcode_image, client 161 | api = API["qrcode"]["get_qrcode_and_token"] 162 | qrcode_login_data = json.loads(client.get(api["url"], headers=headers).text)["data"] 163 | login_key = qrcode_login_data["qrcode_key"] 164 | qrcode = qrcode_login_data["url"] 165 | qrcode_image = make_qrcode(qrcode) 166 | return qrcode_image 167 | 168 | 169 | def main(): 170 | global client 171 | 172 | nowdir = os.getcwd() 173 | result_file = os.path.join(nowdir, "bzcookies") 174 | if not os.path.exists(result_file): 175 | try: 176 | with open(result_file, "w") as f: 177 | f.write("") 178 | except PermissionError as e: 179 | console.print("当前所在文件夹无写入权限,请将本程序移至其他文件夹再打开") 180 | console.print(e) 181 | console.input("\n按回车退出程序") 182 | client.cookies = cookielib.LWPCookieJar(filename=result_file) 183 | while True: 184 | client, status = islogin() 185 | if not status: 186 | login_with_qrcode() 187 | if status: 188 | return client 189 | 190 | 191 | # if __name__ == "__main__": 192 | # main() 193 | -------------------------------------------------------------------------------- /src/agent.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | agent = [ 4 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60", 5 | "Opera/8.0 (Windows NT 5.1; U; en)", 6 | "Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50", 7 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50", 8 | "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11", 9 | "Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11", 10 | "Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10", 11 | "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0", 12 | "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10", 13 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv,2.0.1) Gecko/20100101 Firefox/4.0.1", 14 | "Mozilla/5.0 (Windows NT 6.1; rv,2.0.1) Gecko/20100101 Firefox/4.0.1", 15 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", 16 | "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5", 17 | "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5", 18 | "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5", 19 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36", 20 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11", 21 | "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16", 22 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", 23 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36", 24 | "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko", 25 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)", 26 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11", 27 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER", 28 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)", 29 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)" 30 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)", 31 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)", 32 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0", 33 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)", 34 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)", 35 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.3.4000 Chrome/30.0.1599.101 Safari/537.36", 36 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", 37 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36", 38 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.4094.1 Safari/537.36", 39 | "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5", 40 | "Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5", 41 | "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5", 42 | "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5", 43 | "Mozilla/5.0 (Linux; U; Android 2.2.1; zh-cn; HTC_Wildfire_A3333 Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", 44 | "Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", 45 | "MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", 46 | "Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10", 47 | "Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13", 48 | "Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.337 Mobile Safari/534.1+", 49 | "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0", 50 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;", 51 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)", 52 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)", 53 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", 54 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)", 55 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)", 56 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)", 57 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser)", 58 | "Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", 59 | "Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124", 60 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)", 61 | "UCWEB7.0.2.37/28/999", 62 | "NOKIA5700/ UCWEB7.0.2.37/28/999", 63 | "Openwave/ UCWEB7.0.2.37/28/999", 64 | "Openwave/ UCWEB7.0.2.37/28/999", 65 | ] 66 | 67 | 68 | # 随机获取一个认证头 69 | def get_user_agents(): 70 | return random.choice(agent).encode("utf-8").decode("latin-1") 71 | -------------------------------------------------------------------------------- /src/getGift.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import datetime 3 | from sys import maxsize 4 | 5 | import httpx 6 | import xlsxwriter 7 | from rich.progress import track 8 | from rich.prompt import Confirm, IntPrompt 9 | 10 | from . import agent 11 | from .console import console 12 | from .live_exit import live_exit 13 | 14 | 15 | # 处理全部礼物数据生成字典以及{uid:name}字典 16 | def all_info_handle(gifts_list): 17 | id_index = {} 18 | gift_result = {} 19 | for gift in gifts_list: 20 | key = str(gift["uid"]) 21 | id_index[key] = gift["uname"] 22 | gift_name = gift["gift_name"] 23 | gift_id = gift["gift_id"] 24 | gold = gift["normal_gold"] / 100 25 | gift_num = gift["gift_num"] 26 | time = gift["time"] 27 | 28 | if key in gift_result: 29 | if gift_id in gift_result[key]: 30 | gift_result[key][gift_id]["gold"] += gold 31 | gift_result[key][gift_id]["gift_num"] += gift_num 32 | gift_result[key][gift_id]["time"].insert(0, gift["time"]) 33 | else: 34 | gift_result[key][gift_id] = { 35 | "gift_name": gift_name, 36 | "gold": gold, 37 | "gift_num": gift_num, 38 | "time": [time], 39 | } 40 | else: 41 | gift_result[key] = { 42 | gift_id: { 43 | "gift_name": gift_name, 44 | "gold": gold, 45 | "gift_num": gift_num, 46 | "time": [time], 47 | } 48 | } 49 | return gift_result, id_index 50 | 51 | 52 | # 处理礼物信息生成大航海字典以及{uid:name}字典 53 | def guard_info(gifts_list): 54 | id_index = {} 55 | gift_result = {} 56 | for gift in gifts_list: 57 | key = str(gift["uid"]) 58 | id_index[key] = gift["uname"] 59 | if gift["gift_name"] in ("舰长", "提督", "总督"): 60 | title = gift["gift_name"] 61 | else: 62 | continue 63 | 64 | if key in gift_result: 65 | gift_result[key][title].insert(0, gift["time"]) 66 | else: 67 | val = {"总督": [], "提督": [], "舰长": [], title: [gift["time"]]} 68 | gift_result[key] = val 69 | 70 | return gift_result, id_index 71 | 72 | 73 | class GiftInfo: 74 | # 构造函数,生成特定格式的日期列表 75 | def __init__(self, client: httpx.Client): 76 | self.cookies = client.cookies 77 | client.close() 78 | ask_list = ["年份", "月份", "日期"] 79 | datetime_today = datetime.datetime.today() 80 | today_date_list = [ 81 | datetime_today.year, 82 | datetime_today.month, 83 | datetime_today.day, 84 | ] 85 | while True: 86 | console.rule("[b]输入查询开始日期") 87 | 88 | begin_date_list = [0 for _ in range(3)] 89 | for i in range(3): 90 | ask = "请输入想查询的开始[b blue]{}[/b blue]".format(ask_list[i]) 91 | begin_date_list[i] = IntPrompt.ask( 92 | ask, 93 | console=console, 94 | default=today_date_list[i], 95 | ) 96 | 97 | self.year_begin, self.month_begin, self.day_begin = begin_date_list 98 | 99 | try: 100 | date_begin = datetime.date( 101 | self.year_begin, self.month_begin, self.day_begin 102 | ) 103 | break 104 | except ValueError as e: 105 | console.print(f"\n[bold red]Warning:[/bold red] 输入日期错误:{e}") 106 | console.print("请重新输入\n") 107 | 108 | most_early_date = datetime.date.today() - datetime.timedelta(179) 109 | if date_begin < most_early_date: 110 | console.print( 111 | "\n[bold red]Warning:[/bold red] b站仅提供近180天数据,可查询最早日期为:{}".format( 112 | most_early_date.strftime("%Y-%m-%d") 113 | ) 114 | ) 115 | console.print("您查询的日期超出范围的部分数据将[b red]全部为0[/b red]\n") 116 | if not Confirm.ask("是否确认继续?", console=console): 117 | live_exit() 118 | 119 | while True: 120 | console.rule("[b]输入查询末尾日期") 121 | 122 | end_date_list = [0 for _ in range(3)] 123 | for i in range(3): 124 | ask = "请输入想查询的末尾[b blue]{}[/b blue]".format(ask_list[i]) 125 | end_date_list[i] = IntPrompt.ask( 126 | ask, 127 | console=console, 128 | default=today_date_list[i], 129 | ) 130 | 131 | self.year_end, self.month_end, self.day_end = end_date_list 132 | 133 | try: 134 | date_end = datetime.date(self.year_end, self.month_end, self.day_end) 135 | break 136 | except ValueError as e: 137 | console.print(f"\n[bold red]Warning:[/bold red] 输入日期错误:{e}") 138 | console.print("请重新输入\n") 139 | 140 | if date_begin > date_end: 141 | console.print("\n[bold red]Warning:[/bold red] 开始日期晚于结束日期,将生成空表格\n") 142 | if not Confirm.ask("是否确认继续?", console=console): 143 | live_exit() 144 | 145 | day_list_range = [] 146 | delta = datetime.timedelta(days=1) 147 | while date_begin <= date_end: 148 | day_str = date_begin.strftime("%Y-%m-%d") 149 | day_list_range.append(day_str) 150 | date_begin += delta 151 | 152 | self.day_list = day_list_range 153 | self.name = "{}年{}月{}日至{}年{}月{}日礼物统计".format( 154 | self.year_begin, 155 | self.month_begin, 156 | self.day_begin, 157 | self.year_end, 158 | self.month_end, 159 | self.day_end, 160 | ) 161 | 162 | # 获取某一段时间礼物信息 163 | async def getGiftInfoOneDay(self): 164 | url = "https://api.live.bilibili.com/xlive/revenue/v1/giftStream/getReceivedGiftStreamNextList" 165 | headers = { 166 | "User-Agent": agent.get_user_agents(), 167 | "Referer": "https://link.bilibili.com/p/center/index", 168 | } 169 | all_gifts_list = [] 170 | async with httpx.AsyncClient( 171 | cookies=self.cookies, headers=headers 172 | ) as async_client: 173 | for date in track(self.day_list, description="正在抓取礼物信息", console=console): 174 | params = { 175 | "limit": maxsize - 1, 176 | "coin_type": 0, 177 | "gift_id": "", 178 | "begin_time": date, 179 | "uname": "", 180 | } 181 | resp = await async_client.get(url, params=params) 182 | all_info = resp.json() 183 | gifts_list = all_info["data"]["list"] 184 | has_more = all_info["data"]["has_more"] 185 | if gifts_list: 186 | last_id = gifts_list[-1]["id"] 187 | all_gifts_list.extend(gifts_list) 188 | 189 | while has_more: 190 | console.print("[b red]已触发“has_more”[/b red]") 191 | params = { 192 | "last_id": last_id, 193 | "limit": maxsize - 1, 194 | "coin_type": 0, 195 | "gift_id": "", 196 | "begin_time": date, 197 | "uname": "", 198 | } 199 | resp = await async_client.get(url, params=params) 200 | all_info = resp.json() 201 | gifts_list = all_info["data"]["list"] 202 | has_more = all_info["data"]["has_more"] 203 | if gifts_list: 204 | last_id = gifts_list[-1]["id"] 205 | all_gifts_list.extend(gifts_list) 206 | 207 | return all_gifts_list 208 | 209 | # 全部礼物信息写入xlsx文件 210 | def xlsWrite(self, gift_result, id_index): 211 | with console.status(f"正在写入[b green]{self.name}.xlsx[/b green]", spinner="line"): 212 | wb = xlsxwriter.Workbook(self.name + ".xlsx") 213 | sheet = wb.add_worksheet("电池数量") 214 | sheet_num = wb.add_worksheet("礼物数目") 215 | sheet_header_list = ["ID", "UID"] 216 | sheet.write_row(0, 0, sheet_header_list) 217 | sheet_num.write_row(0, 0, sheet_header_list) 218 | 219 | row = 1 220 | gold_sum_list = [ 221 | "SUM", 222 | ] 223 | for uid in gift_result: 224 | sheet.write(row, 1, uid) 225 | sheet.write(row, 0, id_index[uid]) 226 | sheet_num.write(row, 1, uid) 227 | sheet_num.write(row, 0, id_index[uid]) 228 | gold_sum = 0 229 | for gift_id in gift_result[uid]: 230 | if gift_id not in sheet_header_list: 231 | sheet_header_list.append(gift_id) 232 | column = sheet_header_list.index(gift_id) 233 | gift_name = gift_result[uid][gift_id]["gift_name"] 234 | sheet.write(0, column, f"{gift_name}(id:{gift_id})") 235 | sheet_num.write(0, column, f"{gift_name}(id:{gift_id})") 236 | column = sheet_header_list.index(gift_id) 237 | gold_temp = gift_result[uid][gift_id]["gold"] 238 | sheet.write(row, column, gold_temp) 239 | gold_sum += gold_temp 240 | sheet_num.write(row, column, gift_result[uid][gift_id]["gift_num"]) 241 | row += 1 242 | gold_sum_list.append(gold_sum) 243 | 244 | # 求和 245 | column = len(sheet_header_list) 246 | sheet.write_column(0, column, gold_sum_list) 247 | 248 | wb.close() 249 | 250 | console.print("[b green]{}.xlsx[/b green] 已生成!".format(self.name)) 251 | 252 | # 根据大航海礼物信息生成xlsx统计结果 253 | def generateXlsFile(self, guard_dict, id_index): 254 | with console.status( 255 | f"正在写入[b green]{self.name}(大航海).xlsx[/b green]", spinner="line" 256 | ): 257 | wb = xlsxwriter.Workbook(filename=self.name + "(大航海).xlsx") 258 | 259 | text_wrap = wb.add_format({"text_wrap": True}) 260 | sheet = wb.add_worksheet("上舰时间") 261 | sheet_header_list = ["ID", "UID", "舰长", "提督", "总督"] 262 | 263 | sheet.write_row(0, 0, sheet_header_list) 264 | sheet.set_column(1, len(sheet_header_list), 25) 265 | 266 | row = 1 267 | sheet1 = wb.add_worksheet("上舰具体数量") 268 | sheet1_head = ["ID", "UID", "总积分", "舰长", "提督", "总督"] 269 | sheet1.write_row(0, 0, sheet1_head) 270 | row1 = 1 271 | 272 | for uid in guard_dict: 273 | scores = ( 274 | len(guard_dict[uid]["舰长"]) 275 | + 15 * len(guard_dict[uid]["提督"]) 276 | + 200 * len(guard_dict[uid]["总督"]) 277 | ) 278 | sheet.write(row, 1, uid) 279 | sheet.write(row, 0, id_index[uid]) 280 | sheet1.write(row1, 1, uid) 281 | sheet1.write(row1, 0, id_index[uid]) 282 | sheet1.write(row1, 2, scores) 283 | for title in guard_dict[uid]: 284 | all_dates = guard_dict[uid][title] 285 | column1 = sheet1_head.index(title) 286 | sheet1.write(row1, column1, len(all_dates)) 287 | if len(all_dates) == 0: 288 | continue 289 | column = sheet_header_list.index(title) 290 | time_str = "\n".join(all_dates) 291 | sheet.write(row, column, time_str, text_wrap) 292 | row += 1 293 | row1 += 1 294 | 295 | wb.close() 296 | 297 | console.print("[b green]{}(大航海).xlsx[/b green] 已生成!".format(self.name)) 298 | 299 | # 根据礼物信息生成csv统计结果,可直接导入BiliMessenger使用 300 | def generateCsvFile(self, guard_dict, id_index): 301 | with console.status( 302 | f"正在写入[b green]{self.name}(大航海).csv[/b green]", spinner="line" 303 | ): 304 | csv_list = [] 305 | for uid, gifts in guard_dict.items(): 306 | usr_name = id_index[uid] 307 | gifts_name = "" 308 | gifts_list = ["舰长", "提督", "总督"] 309 | for i in gifts_list: 310 | if gifts[i]: 311 | gifts_name = i 312 | 313 | row_list = [gifts_name, uid, usr_name] 314 | csv_list.append(row_list) 315 | 316 | with open( 317 | self.name + "(大航海).csv", mode="w", encoding="utf-8-sig", newline="" 318 | ) as f: 319 | writer = csv.writer(f) 320 | writer.writerows(csv_list) 321 | 322 | console.print("[b green]{}(大航海).csv[/b green] 已生成!".format(self.name)) 323 | 324 | async def main(self, choice): 325 | gifts_list = await self.getGiftInfoOneDay() 326 | if choice == "1": 327 | guard_dict, id_index = guard_info(gifts_list) 328 | self.generateXlsFile(guard_dict, id_index) 329 | elif choice == "2": 330 | guard_dict, id_index = guard_info(gifts_list) 331 | self.generateCsvFile(guard_dict, id_index) 332 | elif choice == "3": 333 | gift_result, id_index = all_info_handle(gifts_list) 334 | self.xlsWrite(gift_result, id_index) 335 | elif choice == "4": 336 | guard_dict, id_index = guard_info(gifts_list) 337 | self.generateXlsFile(guard_dict, id_index) 338 | self.generateCsvFile(guard_dict, id_index) 339 | gift_result, id_index = all_info_handle(gifts_list) 340 | self.xlsWrite(gift_result, id_index) 341 | console.print("\n已全部生成完成!") 342 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "altgraph" 5 | version = "0.17.4" 6 | description = "Python graph (network) package" 7 | optional = false 8 | python-versions = "*" 9 | files = [ 10 | {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, 11 | {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.1.0" 17 | description = "High level compatibility layer for multiple asynchronous event loop implementations" 18 | optional = false 19 | python-versions = ">=3.8" 20 | files = [ 21 | {file = "anyio-4.1.0-py3-none-any.whl", hash = "sha256:56a415fbc462291813a94528a779597226619c8e78af7de0507333f700011e5f"}, 22 | {file = "anyio-4.1.0.tar.gz", hash = "sha256:5a0bec7085176715be77df87fc66d6c9d70626bd752fcc85f57cdbee5b3760da"}, 23 | ] 24 | 25 | [package.dependencies] 26 | idna = ">=2.8" 27 | sniffio = ">=1.1" 28 | 29 | [package.extras] 30 | doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] 31 | test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] 32 | trio = ["trio (>=0.23)"] 33 | 34 | [[package]] 35 | name = "black" 36 | version = "22.12.0" 37 | description = "The uncompromising code formatter." 38 | optional = false 39 | python-versions = ">=3.7" 40 | files = [ 41 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 42 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 43 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 44 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 45 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 46 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 47 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 48 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 49 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 50 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 51 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 52 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 53 | ] 54 | 55 | [package.dependencies] 56 | click = ">=8.0.0" 57 | mypy-extensions = ">=0.4.3" 58 | pathspec = ">=0.9.0" 59 | platformdirs = ">=2" 60 | 61 | [package.extras] 62 | colorama = ["colorama (>=0.4.3)"] 63 | d = ["aiohttp (>=3.7.4)"] 64 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 65 | uvloop = ["uvloop (>=0.15.2)"] 66 | 67 | [[package]] 68 | name = "certifi" 69 | version = "2023.11.17" 70 | description = "Python package for providing Mozilla's CA Bundle." 71 | optional = false 72 | python-versions = ">=3.6" 73 | files = [ 74 | {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, 75 | {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, 76 | ] 77 | 78 | [[package]] 79 | name = "click" 80 | version = "8.1.7" 81 | description = "Composable command line interface toolkit" 82 | optional = false 83 | python-versions = ">=3.7" 84 | files = [ 85 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 86 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 87 | ] 88 | 89 | [package.dependencies] 90 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 91 | 92 | [[package]] 93 | name = "colorama" 94 | version = "0.4.6" 95 | description = "Cross-platform colored terminal text." 96 | optional = false 97 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 98 | files = [ 99 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 100 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 101 | ] 102 | 103 | [[package]] 104 | name = "flake8" 105 | version = "5.0.4" 106 | description = "the modular source code checker: pep8 pyflakes and co" 107 | optional = false 108 | python-versions = ">=3.6.1" 109 | files = [ 110 | {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, 111 | {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, 112 | ] 113 | 114 | [package.dependencies] 115 | mccabe = ">=0.7.0,<0.8.0" 116 | pycodestyle = ">=2.9.0,<2.10.0" 117 | pyflakes = ">=2.5.0,<2.6.0" 118 | 119 | [[package]] 120 | name = "h11" 121 | version = "0.14.0" 122 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 123 | optional = false 124 | python-versions = ">=3.7" 125 | files = [ 126 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 127 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 128 | ] 129 | 130 | [[package]] 131 | name = "httpcore" 132 | version = "0.17.3" 133 | description = "A minimal low-level HTTP client." 134 | optional = false 135 | python-versions = ">=3.7" 136 | files = [ 137 | {file = "httpcore-0.17.3-py3-none-any.whl", hash = "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"}, 138 | {file = "httpcore-0.17.3.tar.gz", hash = "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888"}, 139 | ] 140 | 141 | [package.dependencies] 142 | anyio = ">=3.0,<5.0" 143 | certifi = "*" 144 | h11 = ">=0.13,<0.15" 145 | sniffio = "==1.*" 146 | 147 | [package.extras] 148 | http2 = ["h2 (>=3,<5)"] 149 | socks = ["socksio (==1.*)"] 150 | 151 | [[package]] 152 | name = "httpx" 153 | version = "0.24.1" 154 | description = "The next generation HTTP client." 155 | optional = false 156 | python-versions = ">=3.7" 157 | files = [ 158 | {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"}, 159 | {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"}, 160 | ] 161 | 162 | [package.dependencies] 163 | certifi = "*" 164 | httpcore = ">=0.15.0,<0.18.0" 165 | idna = "*" 166 | sniffio = "*" 167 | 168 | [package.extras] 169 | brotli = ["brotli", "brotlicffi"] 170 | cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] 171 | http2 = ["h2 (>=3,<5)"] 172 | socks = ["socksio (==1.*)"] 173 | 174 | [[package]] 175 | name = "idna" 176 | version = "3.6" 177 | description = "Internationalized Domain Names in Applications (IDNA)" 178 | optional = false 179 | python-versions = ">=3.5" 180 | files = [ 181 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 182 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 183 | ] 184 | 185 | [[package]] 186 | name = "macholib" 187 | version = "1.16.3" 188 | description = "Mach-O header analysis and editing" 189 | optional = false 190 | python-versions = "*" 191 | files = [ 192 | {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, 193 | {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, 194 | ] 195 | 196 | [package.dependencies] 197 | altgraph = ">=0.17" 198 | 199 | [[package]] 200 | name = "markdown-it-py" 201 | version = "3.0.0" 202 | description = "Python port of markdown-it. Markdown parsing, done right!" 203 | optional = false 204 | python-versions = ">=3.8" 205 | files = [ 206 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 207 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 208 | ] 209 | 210 | [package.dependencies] 211 | mdurl = ">=0.1,<1.0" 212 | 213 | [package.extras] 214 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 215 | code-style = ["pre-commit (>=3.0,<4.0)"] 216 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 217 | linkify = ["linkify-it-py (>=1,<3)"] 218 | plugins = ["mdit-py-plugins"] 219 | profiling = ["gprof2dot"] 220 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 221 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 222 | 223 | [[package]] 224 | name = "mccabe" 225 | version = "0.7.0" 226 | description = "McCabe checker, plugin for flake8" 227 | optional = false 228 | python-versions = ">=3.6" 229 | files = [ 230 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 231 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 232 | ] 233 | 234 | [[package]] 235 | name = "mdurl" 236 | version = "0.1.2" 237 | description = "Markdown URL utilities" 238 | optional = false 239 | python-versions = ">=3.7" 240 | files = [ 241 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 242 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 243 | ] 244 | 245 | [[package]] 246 | name = "mypy-extensions" 247 | version = "1.0.0" 248 | description = "Type system extensions for programs checked with the mypy type checker." 249 | optional = false 250 | python-versions = ">=3.5" 251 | files = [ 252 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 253 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 254 | ] 255 | 256 | [[package]] 257 | name = "pathspec" 258 | version = "0.11.2" 259 | description = "Utility library for gitignore style pattern matching of file paths." 260 | optional = false 261 | python-versions = ">=3.7" 262 | files = [ 263 | {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, 264 | {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, 265 | ] 266 | 267 | [[package]] 268 | name = "pefile" 269 | version = "2023.2.7" 270 | description = "Python PE parsing module" 271 | optional = false 272 | python-versions = ">=3.6.0" 273 | files = [ 274 | {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, 275 | {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, 276 | ] 277 | 278 | [[package]] 279 | name = "pillow" 280 | version = "9.5.0" 281 | description = "Python Imaging Library (Fork)" 282 | optional = false 283 | python-versions = ">=3.7" 284 | files = [ 285 | {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, 286 | {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, 287 | {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, 288 | {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, 289 | {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, 290 | {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, 291 | {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, 292 | {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, 293 | {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, 294 | {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, 295 | {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, 296 | {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, 297 | {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, 298 | {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, 299 | {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, 300 | {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, 301 | {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, 302 | {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, 303 | {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, 304 | {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, 305 | {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, 306 | {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, 307 | {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, 308 | {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, 309 | {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, 310 | {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, 311 | {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, 312 | {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, 313 | {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, 314 | {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, 315 | {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, 316 | {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, 317 | {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, 318 | {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, 319 | {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, 320 | {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, 321 | {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, 322 | {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, 323 | {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, 324 | {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, 325 | {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, 326 | {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, 327 | {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, 328 | {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, 329 | {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, 330 | {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, 331 | {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, 332 | {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, 333 | {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, 334 | {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, 335 | {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, 336 | {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, 337 | {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, 338 | {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, 339 | {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, 340 | {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, 341 | {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, 342 | {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, 343 | {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, 344 | {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, 345 | {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, 346 | {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, 347 | {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, 348 | {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, 349 | {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, 350 | {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, 351 | ] 352 | 353 | [package.extras] 354 | docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] 355 | tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] 356 | 357 | [[package]] 358 | name = "platformdirs" 359 | version = "4.0.0" 360 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 361 | optional = false 362 | python-versions = ">=3.7" 363 | files = [ 364 | {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, 365 | {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, 366 | ] 367 | 368 | [package.extras] 369 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] 370 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] 371 | 372 | [[package]] 373 | name = "pycodestyle" 374 | version = "2.9.1" 375 | description = "Python style guide checker" 376 | optional = false 377 | python-versions = ">=3.6" 378 | files = [ 379 | {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, 380 | {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, 381 | ] 382 | 383 | [[package]] 384 | name = "pyflakes" 385 | version = "2.5.0" 386 | description = "passive checker of Python programs" 387 | optional = false 388 | python-versions = ">=3.6" 389 | files = [ 390 | {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, 391 | {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, 392 | ] 393 | 394 | [[package]] 395 | name = "pygments" 396 | version = "2.17.2" 397 | description = "Pygments is a syntax highlighting package written in Python." 398 | optional = false 399 | python-versions = ">=3.7" 400 | files = [ 401 | {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, 402 | {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, 403 | ] 404 | 405 | [package.extras] 406 | plugins = ["importlib-metadata"] 407 | windows-terminal = ["colorama (>=0.4.6)"] 408 | 409 | [[package]] 410 | name = "pyinstaller" 411 | version = "5.13.2" 412 | description = "PyInstaller bundles a Python application and all its dependencies into a single package." 413 | optional = false 414 | python-versions = "<3.13,>=3.7" 415 | files = [ 416 | {file = "pyinstaller-5.13.2-py3-none-macosx_10_13_universal2.whl", hash = "sha256:16cbd66b59a37f4ee59373a003608d15df180a0d9eb1a29ff3bfbfae64b23d0f"}, 417 | {file = "pyinstaller-5.13.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8f6dd0e797ae7efdd79226f78f35eb6a4981db16c13325e962a83395c0ec7420"}, 418 | {file = "pyinstaller-5.13.2-py3-none-manylinux2014_i686.whl", hash = "sha256:65133ed89467edb2862036b35d7c5ebd381670412e1e4361215e289c786dd4e6"}, 419 | {file = "pyinstaller-5.13.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:7d51734423685ab2a4324ab2981d9781b203dcae42839161a9ee98bfeaabdade"}, 420 | {file = "pyinstaller-5.13.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:2c2fe9c52cb4577a3ac39626b84cf16cf30c2792f785502661286184f162ae0d"}, 421 | {file = "pyinstaller-5.13.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c63ef6133eefe36c4b2f4daf4cfea3d6412ece2ca218f77aaf967e52a95ac9b8"}, 422 | {file = "pyinstaller-5.13.2-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:aadafb6f213549a5906829bb252e586e2cf72a7fbdb5731810695e6516f0ab30"}, 423 | {file = "pyinstaller-5.13.2-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:b2e1c7f5cceb5e9800927ddd51acf9cc78fbaa9e79e822c48b0ee52d9ce3c892"}, 424 | {file = "pyinstaller-5.13.2-py3-none-win32.whl", hash = "sha256:421cd24f26144f19b66d3868b49ed673176765f92fa9f7914cd2158d25b6d17e"}, 425 | {file = "pyinstaller-5.13.2-py3-none-win_amd64.whl", hash = "sha256:ddcc2b36052a70052479a9e5da1af067b4496f43686ca3cdda99f8367d0627e4"}, 426 | {file = "pyinstaller-5.13.2-py3-none-win_arm64.whl", hash = "sha256:27cd64e7cc6b74c5b1066cbf47d75f940b71356166031deb9778a2579bb874c6"}, 427 | {file = "pyinstaller-5.13.2.tar.gz", hash = "sha256:c8e5d3489c3a7cc5f8401c2d1f48a70e588f9967e391c3b06ddac1f685f8d5d2"}, 428 | ] 429 | 430 | [package.dependencies] 431 | altgraph = "*" 432 | macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} 433 | pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} 434 | pyinstaller-hooks-contrib = ">=2021.4" 435 | pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} 436 | setuptools = ">=42.0.0" 437 | 438 | [package.extras] 439 | encryption = ["tinyaes (>=1.0.0)"] 440 | hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] 441 | 442 | [[package]] 443 | name = "pyinstaller-hooks-contrib" 444 | version = "2023.10" 445 | description = "Community maintained hooks for PyInstaller" 446 | optional = false 447 | python-versions = ">=3.7" 448 | files = [ 449 | {file = "pyinstaller-hooks-contrib-2023.10.tar.gz", hash = "sha256:4b4a998036abb713774cb26534ca06b7e6e09e4c628196017a10deb11a48747f"}, 450 | {file = "pyinstaller_hooks_contrib-2023.10-py2.py3-none-any.whl", hash = "sha256:6dc1786a8f452941245d5bb85893e2a33632ebdcbc4c23eea41f2ee08281b0c0"}, 451 | ] 452 | 453 | [[package]] 454 | name = "pypng" 455 | version = "0.20220715.0" 456 | description = "Pure Python library for saving and loading PNG images" 457 | optional = false 458 | python-versions = "*" 459 | files = [ 460 | {file = "pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"}, 461 | {file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"}, 462 | ] 463 | 464 | [[package]] 465 | name = "pywin32-ctypes" 466 | version = "0.2.2" 467 | description = "A (partial) reimplementation of pywin32 using ctypes/cffi" 468 | optional = false 469 | python-versions = ">=3.6" 470 | files = [ 471 | {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, 472 | {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, 473 | ] 474 | 475 | [[package]] 476 | name = "qrcode" 477 | version = "7.4.2" 478 | description = "QR Code image generator" 479 | optional = false 480 | python-versions = ">=3.7" 481 | files = [ 482 | {file = "qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"}, 483 | {file = "qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"}, 484 | ] 485 | 486 | [package.dependencies] 487 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 488 | pypng = "*" 489 | typing-extensions = "*" 490 | 491 | [package.extras] 492 | all = ["pillow (>=9.1.0)", "pytest", "pytest-cov", "tox", "zest.releaser[recommended]"] 493 | dev = ["pytest", "pytest-cov", "tox"] 494 | maintainer = ["zest.releaser[recommended]"] 495 | pil = ["pillow (>=9.1.0)"] 496 | test = ["coverage", "pytest"] 497 | 498 | [[package]] 499 | name = "rich" 500 | version = "13.7.0" 501 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 502 | optional = false 503 | python-versions = ">=3.7.0" 504 | files = [ 505 | {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, 506 | {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, 507 | ] 508 | 509 | [package.dependencies] 510 | markdown-it-py = ">=2.2.0" 511 | pygments = ">=2.13.0,<3.0.0" 512 | 513 | [package.extras] 514 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 515 | 516 | [[package]] 517 | name = "setuptools" 518 | version = "69.0.2" 519 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 520 | optional = false 521 | python-versions = ">=3.8" 522 | files = [ 523 | {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, 524 | {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, 525 | ] 526 | 527 | [package.extras] 528 | docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] 529 | testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] 530 | testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] 531 | 532 | [[package]] 533 | name = "sniffio" 534 | version = "1.3.0" 535 | description = "Sniff out which async library your code is running under" 536 | optional = false 537 | python-versions = ">=3.7" 538 | files = [ 539 | {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, 540 | {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, 541 | ] 542 | 543 | [[package]] 544 | name = "typing-extensions" 545 | version = "4.8.0" 546 | description = "Backported and Experimental Type Hints for Python 3.8+" 547 | optional = false 548 | python-versions = ">=3.8" 549 | files = [ 550 | {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, 551 | {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, 552 | ] 553 | 554 | [[package]] 555 | name = "xlsxwriter" 556 | version = "3.1.9" 557 | description = "A Python module for creating Excel XLSX files." 558 | optional = false 559 | python-versions = ">=3.6" 560 | files = [ 561 | {file = "XlsxWriter-3.1.9-py3-none-any.whl", hash = "sha256:b61c1a0c786f82644936c0936ec96ee96cd3afb9440094232f7faef9b38689f0"}, 562 | {file = "XlsxWriter-3.1.9.tar.gz", hash = "sha256:de810bf328c6a4550f4ffd6b0b34972aeb7ffcf40f3d285a0413734f9b63a929"}, 563 | ] 564 | 565 | [metadata] 566 | lock-version = "2.0" 567 | python-versions = "~3.11.4" 568 | content-hash = "9415f34e899731018f41e2dba0d2985e31946233134fcc3035fb01d2fd6286a3" 569 | --------------------------------------------------------------------------------