diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..818f6bd --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,56 @@ +{ + "name": "OSU Lazer API", + "dockerComposeFile": "docker-compose.yml", + "service": "devcontainer", + "shutdownAction": "stopCompose", + "workspaceFolder": "/workspaces/osu_lazer_api", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "detachhead.basedpyright", + "charliermarsh.ruff", + "ms-python.debugpy", + "ms-vscode.vscode-json", + "redhat.vscode-yaml", + "ms-vscode.docker" + ], + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.terminal.activateEnvironment": true, + "python.linting.enabled": true, + "python.linting.pylintEnabled": false, + "python.linting.flake8Enabled": false, + "python.formatting.provider": "none", + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + }, + "ruff.enable": true, + "ruff.lint.enable": true, + "ruff.format.enable": true, + "ruff.importStrategy": "fromEnvironment", + "files.exclude": { + "**/__pycache__": true, + "**/*.pyc": true + }, + "python.testing.pytestEnabled": true, + "python.testing.unittestEnabled": false, + "python.testing.pytestArgs": [ + "." + ], + "terminal.integrated.defaultProfile.linux": "bash" + } + } + }, + "features": { + "ghcr.io/va-h/devcontainers-features/uv:1": {} + }, + "forwardPorts": [8000, 3306, 6379], + "postCreateCommand": "uv sync --dev && uv run pre-commit install", + "remoteUser": "vscode" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..be9261e --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,56 @@ +version: '3.8' + +services: + devcontainer: + image: mcr.microsoft.com/devcontainers/python:3.13 + volumes: + - ../..:/workspaces:cached + - ~/.ssh:/home/vscode/.ssh:ro + command: sleep infinity + networks: + - devcontainer-network + depends_on: + - mysql + - redis + environment: + DATABASE_URL: mysql+aiomysql://osu_user:osu_password@mysql:3306/osu_api + REDIS_URL: redis://redis:6379/0 + SECRET_KEY: dev-secret-key-change-in-production + OSU_CLIENT_ID: "5" + OSU_CLIENT_SECRET: "FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk" + + mysql: + image: mysql:8.0 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: osu_api + MYSQL_USER: osu_user + MYSQL_PASSWORD: osu_password + ports: + - "3306:3306" + volumes: + - mysql-data:/var/lib/mysql + - ../mysql-init:/docker-entrypoint-initdb.d:cached + networks: + - devcontainer-network + command: --default-authentication-plugin=mysql_native_password + + redis: + image: redis:7-alpine + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - redis-data:/data + networks: + - devcontainer-network + command: redis-server --appendonly yes + +networks: + devcontainer-network: + driver: bridge + +volumes: + mysql-data: + redis-data: diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..766ab75 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,28 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# Makefiles always use tabs for indentation +[Makefile] +indent_style = tab + +# Batch files use tabs for indentation +[*.bat] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_size = 2 + +[{*.py,*.pyi}] +indent_size = 4 diff --git a/.env.client b/.env.client index 331898d..eac9b22 100644 --- a/.env.client +++ b/.env.client @@ -1,4 +1,4 @@ -# osu! API 客户端配置 -OSU_CLIENT_ID=5 -OSU_CLIENT_SECRET=FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk -OSU_API_URL=http://localhost:8000 +# osu! API 客户端配置 +OSU_CLIENT_ID=5 +OSU_CLIENT_SECRET=FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk +OSU_API_URL=http://localhost:8000 diff --git a/.gitignore b/.gitignore index a6bf882..14d64a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,209 +1,209 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[codz] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py.cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# UV -# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -#uv.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock -#poetry.toml - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. -# https://pdm-project.org/en/latest/usage/project/#working-with-version-control -#pdm.lock -#pdm.toml -.pdm-python -.pdm-build/ - -# pixi -# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. -#pixi.lock -# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one -# in the .venv directory. It is recommended not to include this directory in version control. -.pixi - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.envrc -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# Abstra -# Abstra is an AI-powered process automation framework. -# Ignore directories containing user credentials, local state, and settings. -# Learn more at https://abstra.io/docs -.abstra/ - -# Visual Studio Code -# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore -# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore -# and can be added to the global gitignore or merged into this file. However, if you prefer, -# you could uncomment the following to ignore the entire vscode folder -# .vscode/ - -# Ruff stuff: -.ruff_cache/ - -# PyPI configuration file -.pypirc - -# Cursor -# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to -# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data -# refer to https://docs.cursor.com/context/ignore-files -.cursorignore -.cursorindexingignore - -# Marimo -marimo/_static/ -marimo/_lsp/ -__marimo__/ -bancho.py-master/* -.vscode/settings.json +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock +#poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +#pdm.lock +#pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +#pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Cursor +# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to +# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data +# refer to https://docs.cursor.com/context/ignore-files +.cursorignore +.cursorindexingignore + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ +bancho.py-master/* +.vscode/settings.json diff --git a/.idea/.gitignore b/.idea/.gitignore index 359bb53..50d9d22 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -1,3 +1,3 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index d4add55..86f861d 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,17 +1,17 @@ - - - + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml index 105ce2d..20fc29e 100644 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index d7ee3e6..c311805 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,10 +1,10 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 92ff71d..5fd5691 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -1,8 +1,8 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/.idea/osu_lazer_api.iml b/.idea/osu_lazer_api.iml index 5e25259..32e115a 100644 --- a/.idea/osu_lazer_api.iml +++ b/.idea/osu_lazer_api.iml @@ -1,14 +1,14 @@ - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..c8397c9 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c63db5e..ea41bde 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,16 @@ -default_install_hook_types: [pre-commit, prepare-commit-msg] -ci: - autofix_commit_msg: "chore(deps): auto fix by pre-commit hooks" - autofix_prs: true - autoupdate_branch: master - autoupdate_schedule: monthly - autoupdate_commit_msg: "chore(deps): auto update by pre-commit hooks" -repos: - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.2 - hooks: - - id: ruff-check - args: [--fix] - stages: [pre-commit] - - id: ruff-format - stages: [pre-commit] +default_install_hook_types: [pre-commit, prepare-commit-msg] +ci: + autofix_commit_msg: "chore(deps): auto fix by pre-commit hooks" + autofix_prs: true + autoupdate_branch: master + autoupdate_schedule: monthly + autoupdate_commit_msg: "chore(deps): auto update by pre-commit hooks" +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.2 + hooks: + - id: ruff-check + args: [--fix] + stages: [pre-commit] + - id: ruff-format + stages: [pre-commit] diff --git a/.vscode/launch.json b/.vscode/launch.json index a2f6416..f6849b6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,14 +1,13 @@ -{ - "version": "0.2.0", - "configurations": [ - - { - "name": "Server", - "type": "debugpy", - "request": "launch", - "program": "main.py", - "console": "integratedTerminal", - "justMyCode": true - } - ] +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Server", + "type": "debugpy", + "request": "launch", + "program": "main.py", + "console": "integratedTerminal", + "justMyCode": true + } + ] } \ No newline at end of file diff --git a/DATA_SYNC_GUIDE.md b/DATA_SYNC_GUIDE.md index 86188eb..dfd0928 100644 --- a/DATA_SYNC_GUIDE.md +++ b/DATA_SYNC_GUIDE.md @@ -1,140 +1,140 @@ -# Lazer API 数据同步指南 - -本指南将帮助您将现有的 bancho.py 数据库数据同步到新的 Lazer API 专用表中。 - -## 文件说明 - -1. **`migrations/add_missing_fields.sql`** - 创建 Lazer API 专用表结构 -2. **`migrations/sync_legacy_data.sql`** - 数据同步脚本 -3. **`sync_data.py`** - 交互式数据同步工具 -4. **`quick_sync.py`** - 快速同步脚本(使用项目配置) - -## 使用方法 - -### 方法一:快速同步(推荐) - -如果您已经配置好了项目的数据库连接,可以直接使用快速同步脚本: - -```bash -python quick_sync.py -``` - -此脚本会: -1. 自动读取项目配置中的数据库连接信息 -2. 创建 Lazer API 专用表结构 -3. 同步现有数据到新表 - -### 方法二:交互式同步 - -如果需要使用不同的数据库连接配置: - -```bash -python sync_data.py -``` - -此脚本会: -1. 交互式地询问数据库连接信息 -2. 检查必要表是否存在 -3. 显示详细的同步过程和结果 - -### 方法三:手动执行 SQL - -如果您熟悉 SQL 操作,可以手动执行: - -```bash -# 1. 创建表结构 -mysql -u username -p database_name < migrations/add_missing_fields.sql - -# 2. 同步数据 -mysql -u username -p database_name < migrations/sync_legacy_data.sql -``` - -## 同步内容 - -### 创建的新表 - -- `lazer_user_profiles` - 用户扩展资料 -- `lazer_user_countries` - 用户国家信息 -- `lazer_user_kudosu` - 用户 Kudosu 统计 -- `lazer_user_counts` - 用户各项计数统计 -- `lazer_user_statistics` - 用户游戏统计(按模式) -- `lazer_user_achievements` - 用户成就 -- `lazer_oauth_tokens` - OAuth 访问令牌 -- 其他相关表... - -### 同步的数据 - -1. **用户基本信息** - - 从 `users` 表同步基本资料 - - 自动转换时间戳格式 - - 设置合理的默认值 - -2. **游戏统计** - - 从 `stats` 表同步各模式的游戏数据 - - 计算命中精度和其他衍生统计 - -3. **用户成就** - - 从 `user_achievements` 表同步成就数据(如果存在) - -## 注意事项 - -1. **安全性** - - 脚本只会创建新表和插入数据 - - 不会修改或删除现有的原始表数据 - - 使用 `ON DUPLICATE KEY UPDATE` 避免重复插入 - -2. **兼容性** - - 兼容现有的 bancho.py 数据库结构 - - 支持标准的 osu! 数据格式 - -3. **性能** - - 大量数据可能需要较长时间 - - 建议在维护窗口期间执行 - -## 故障排除 - -### 常见错误 - -1. **"Unknown column" 错误** - ``` - ERROR 1054: Unknown column 'users.is_active' in 'field list' - ``` - **解决方案**: 确保先执行了 `add_missing_fields.sql` 创建表结构 - -2. **"Table doesn't exist" 错误** - ``` - ERROR 1146: Table 'database.users' doesn't exist - ``` - **解决方案**: 确认数据库中存在 bancho.py 的原始表 - -3. **连接错误** - ``` - ERROR 2003: Can't connect to MySQL server - ``` - **解决方案**: 检查数据库连接配置和权限 - -### 验证同步结果 - -同步完成后,可以执行以下查询验证结果: - -```sql --- 检查同步的用户数量 -SELECT COUNT(*) FROM lazer_user_profiles; - --- 查看样本数据 -SELECT - u.id, u.name, - lup.playmode, lup.is_supporter, - lus.pp, lus.play_count -FROM users u -LEFT JOIN lazer_user_profiles lup ON u.id = lup.user_id -LEFT JOIN lazer_user_statistics lus ON u.id = lus.user_id AND lus.mode = 'osu' -LIMIT 5; -``` - -## 支持 - -如果遇到问题,请: -1. 检查日志文件 `data_sync.log` -2. 确认数据库权限 -3. 验证原始表数据完整性 +# Lazer API 数据同步指南 + +本指南将帮助您将现有的 bancho.py 数据库数据同步到新的 Lazer API 专用表中。 + +## 文件说明 + +1. **`migrations/add_missing_fields.sql`** - 创建 Lazer API 专用表结构 +2. **`migrations/sync_legacy_data.sql`** - 数据同步脚本 +3. **`sync_data.py`** - 交互式数据同步工具 +4. **`quick_sync.py`** - 快速同步脚本(使用项目配置) + +## 使用方法 + +### 方法一:快速同步(推荐) + +如果您已经配置好了项目的数据库连接,可以直接使用快速同步脚本: + +```bash +python quick_sync.py +``` + +此脚本会: +1. 自动读取项目配置中的数据库连接信息 +2. 创建 Lazer API 专用表结构 +3. 同步现有数据到新表 + +### 方法二:交互式同步 + +如果需要使用不同的数据库连接配置: + +```bash +python sync_data.py +``` + +此脚本会: +1. 交互式地询问数据库连接信息 +2. 检查必要表是否存在 +3. 显示详细的同步过程和结果 + +### 方法三:手动执行 SQL + +如果您熟悉 SQL 操作,可以手动执行: + +```bash +# 1. 创建表结构 +mysql -u username -p database_name < migrations/add_missing_fields.sql + +# 2. 同步数据 +mysql -u username -p database_name < migrations/sync_legacy_data.sql +``` + +## 同步内容 + +### 创建的新表 + +- `lazer_user_profiles` - 用户扩展资料 +- `lazer_user_countries` - 用户国家信息 +- `lazer_user_kudosu` - 用户 Kudosu 统计 +- `lazer_user_counts` - 用户各项计数统计 +- `lazer_user_statistics` - 用户游戏统计(按模式) +- `lazer_user_achievements` - 用户成就 +- `lazer_oauth_tokens` - OAuth 访问令牌 +- 其他相关表... + +### 同步的数据 + +1. **用户基本信息** + - 从 `users` 表同步基本资料 + - 自动转换时间戳格式 + - 设置合理的默认值 + +2. **游戏统计** + - 从 `stats` 表同步各模式的游戏数据 + - 计算命中精度和其他衍生统计 + +3. **用户成就** + - 从 `user_achievements` 表同步成就数据(如果存在) + +## 注意事项 + +1. **安全性** + - 脚本只会创建新表和插入数据 + - 不会修改或删除现有的原始表数据 + - 使用 `ON DUPLICATE KEY UPDATE` 避免重复插入 + +2. **兼容性** + - 兼容现有的 bancho.py 数据库结构 + - 支持标准的 osu! 数据格式 + +3. **性能** + - 大量数据可能需要较长时间 + - 建议在维护窗口期间执行 + +## 故障排除 + +### 常见错误 + +1. **"Unknown column" 错误** + ``` + ERROR 1054: Unknown column 'users.is_active' in 'field list' + ``` + **解决方案**: 确保先执行了 `add_missing_fields.sql` 创建表结构 + +2. **"Table doesn't exist" 错误** + ``` + ERROR 1146: Table 'database.users' doesn't exist + ``` + **解决方案**: 确认数据库中存在 bancho.py 的原始表 + +3. **连接错误** + ``` + ERROR 2003: Can't connect to MySQL server + ``` + **解决方案**: 检查数据库连接配置和权限 + +### 验证同步结果 + +同步完成后,可以执行以下查询验证结果: + +```sql +-- 检查同步的用户数量 +SELECT COUNT(*) FROM lazer_user_profiles; + +-- 查看样本数据 +SELECT + u.id, u.name, + lup.playmode, lup.is_supporter, + lus.pp, lus.play_count +FROM users u +LEFT JOIN lazer_user_profiles lup ON u.id = lup.user_id +LEFT JOIN lazer_user_statistics lus ON u.id = lus.user_id AND lus.mode = 'osu' +LIMIT 5; +``` + +## 支持 + +如果遇到问题,请: +1. 检查日志文件 `data_sync.log` +2. 确认数据库权限 +3. 验证原始表数据完整性 diff --git a/Dockerfile b/Dockerfile index 0713304..c1ed5c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,28 +1,28 @@ -FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim - -WORKDIR /app -ENV UV_PROJECT_ENVIRONMENT=syncvenv - -# 安装系统依赖 -RUN apt-get update && apt-get install -y \ - gcc \ - pkg-config \ - default-libmysqlclient-dev \ - && rm -rf /var/lib/apt/lists/* - -# 复制依赖文件 -COPY uv.lock . -COPY pyproject.toml . -COPY requirements.txt . - -# 安装Python依赖 -RUN pip install -r requirements.txt - -# 复制应用代码 -COPY . . - -# 暴露端口 -EXPOSE 8000 - -# 启动命令 -CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim + +WORKDIR /app +ENV UV_PROJECT_ENVIRONMENT=syncvenv + +# 安装系统依赖 +RUN apt-get update && apt-get install -y \ + gcc \ + pkg-config \ + default-libmysqlclient-dev \ + && rm -rf /var/lib/apt/lists/* + +# 复制依赖文件 +COPY uv.lock . +COPY pyproject.toml . +COPY requirements.txt . + +# 安装Python依赖 +RUN pip install -r requirements.txt + +# 复制应用代码 +COPY . . + +# 暴露端口 +EXPOSE 8000 + +# 启动命令 +CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/README.md b/README.md index e1431e0..6cdf3be 100644 --- a/README.md +++ b/README.md @@ -1,214 +1,214 @@ -# osu! API 模拟服务器 - -这是一个使用 FastAPI + MySQL + Redis 实现的 osu! API 模拟服务器,提供了完整的用户认证和数据管理功能。 - -## 功能特性 - -- **OAuth 2.0 认证**: 支持密码流和刷新令牌流 -- **用户数据管理**: 完整的用户信息、统计数据、成就等 -- **多游戏模式支持**: osu!, taiko, fruits, mania -- **数据库持久化**: MySQL 存储用户数据 -- **缓存支持**: Redis 缓存令牌和会话信息 -- **容器化部署**: Docker 和 Docker Compose 支持 - -## API 端点 - -### 认证端点 -- `POST /oauth/token` - OAuth 令牌获取/刷新 - -### 用户端点 -- `GET /api/v2/me/{ruleset}` - 获取当前用户信息 - -### 其他端点 -- `GET /` - 根端点 -- `GET /health` - 健康检查 - -## 快速开始 - -### 使用 Docker Compose (推荐) - -1. 克隆项目 -```bash -git clone -cd osu_lazer_api -``` - -2. 启动服务 -```bash -docker-compose up -d -``` - -3. 创建示例数据 -```bash -docker-compose exec api python create_sample_data.py -``` - -4. 测试 API -```bash -# 获取访问令牌 -curl -X POST http://localhost:8000/oauth/token \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "grant_type=password&username=Googujiang&password=password123&client_id=5&client_secret=FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk&scope=*" - -# 使用令牌获取用户信息 -curl -X GET http://localhost:8000/api/v2/me/osu \ - -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -``` - -### 本地开发 - -1. 安装依赖 -```bash -pip install -r requirements.txt -``` - -2. 配置环境变量 -```bash -# 复制服务器配置文件 -cp .env .env.local - -# 复制客户端配置文件(用于测试脚本) -cp .env.client .env.client.local -``` - -3. 启动 MySQL 和 Redis -```bash -# 使用 Docker -docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=osu_api -p 3306:3306 mysql:8.0 -docker run -d --name redis -p 6379:6379 redis:7-alpine -``` - -4. 创建示例数据 -```bash -python create_sample_data.py -``` - -5. 启动应用 -```bash -uvicorn main:app --reload -``` - -6. 测试 API -```bash -# 使用测试脚本(会自动加载 .env 文件) -python test_api.py - -# 或使用原始示例脚本 -python osu_api_example.py -``` - -## 项目结构 - -``` -osu_lazer_api/ -├── app/ -│ ├── __init__.py -│ ├── models.py # Pydantic 数据模型 -│ ├── database.py # SQLAlchemy 数据库模型 -│ ├── config.py # 配置设置 -│ ├── dependencies.py # 依赖注入 -│ ├── auth.py # 认证和令牌管理 -│ └── utils.py # 工具函数 -├── main.py # FastAPI 应用主文件 -├── create_sample_data.py # 示例数据创建脚本 -├── requirements.txt # Python 依赖 -├── .env # 环境变量配置 -├── docker-compose.yml # Docker Compose 配置 -├── Dockerfile # Docker 镜像配置 -└── README.md # 项目说明 -``` - -## 示例用户 - -创建示例数据后,您可以使用以下凭据进行测试: - -- **用户名**: `Googujiang` -- **密码**: `password123` -- **用户ID**: `15651670` - -## 环境变量配置 - -项目包含两个环境配置文件: - -### 服务器配置 (`.env`) -用于配置 FastAPI 服务器的运行参数: - -| 变量名 | 描述 | 默认值 | -|--------|------|--------| -| `DATABASE_URL` | MySQL 数据库连接字符串 | `mysql+pymysql://root:password@localhost:3306/osu_api` | -| `REDIS_URL` | Redis 连接字符串 | `redis://localhost:6379/0` | -| `SECRET_KEY` | JWT 签名密钥 | `your-secret-key-here` | -| `ACCESS_TOKEN_EXPIRE_MINUTES` | 访问令牌过期时间(分钟) | `1440` | -| `OSU_CLIENT_ID` | OAuth 客户端 ID | `5` | -| `OSU_CLIENT_SECRET` | OAuth 客户端密钥 | `FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk` | -| `HOST` | 服务器监听地址 | `0.0.0.0` | -| `PORT` | 服务器监听端口 | `8000` | -| `DEBUG` | 调试模式 | `True` | - -### 客户端配置 (`.env.client`) -用于配置客户端脚本的 API 连接参数: - -| 变量名 | 描述 | 默认值 | -|--------|------|--------| -| `OSU_CLIENT_ID` | OAuth 客户端 ID | `5` | -| `OSU_CLIENT_SECRET` | OAuth 客户端密钥 | `FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk` | -| `OSU_API_URL` | API 服务器地址 | `http://localhost:8000` | - -> **注意**: 在生产环境中,请务必更改默认的密钥和密码! - -## API 使用示例 - -### 获取访问令牌 - -```bash -curl -X POST http://localhost:8000/oauth/token \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "grant_type=password&username=Googujiang&password=password123&client_id=5&client_secret=FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk&scope=*" -``` - -响应: -```json -{ - "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", - "token_type": "Bearer", - "expires_in": 86400, - "refresh_token": "abc123...", - "scope": "*" -} -``` - -### 获取用户信息 - -```bash -curl -X GET http://localhost:8000/api/v2/me/osu \ - -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -``` - -### 刷新令牌 - -```bash -curl -X POST http://localhost:8000/oauth/token \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN&client_id=5&client_secret=FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk" -``` - -## 开发 - -### 添加新用户 - -您可以通过修改 `create_sample_data.py` 文件来添加更多示例用户,或者扩展 API 来支持用户注册功能。 - -### 扩展功能 - -- 添加更多 API 端点(排行榜、谱面信息等) -- 实现实时功能(WebSocket) -- 添加管理面板 -- 实现数据导入/导出功能 - -## 许可证 - -MIT License - -## 贡献 - -欢迎提交 Issue 和 Pull Request! +# osu! API 模拟服务器 + +这是一个使用 FastAPI + MySQL + Redis 实现的 osu! API 模拟服务器,提供了完整的用户认证和数据管理功能。 + +## 功能特性 + +- **OAuth 2.0 认证**: 支持密码流和刷新令牌流 +- **用户数据管理**: 完整的用户信息、统计数据、成就等 +- **多游戏模式支持**: osu!, taiko, fruits, mania +- **数据库持久化**: MySQL 存储用户数据 +- **缓存支持**: Redis 缓存令牌和会话信息 +- **容器化部署**: Docker 和 Docker Compose 支持 + +## API 端点 + +### 认证端点 +- `POST /oauth/token` - OAuth 令牌获取/刷新 + +### 用户端点 +- `GET /api/v2/me/{ruleset}` - 获取当前用户信息 + +### 其他端点 +- `GET /` - 根端点 +- `GET /health` - 健康检查 + +## 快速开始 + +### 使用 Docker Compose (推荐) + +1. 克隆项目 +```bash +git clone +cd osu_lazer_api +``` + +2. 启动服务 +```bash +docker-compose up -d +``` + +3. 创建示例数据 +```bash +docker-compose exec api python create_sample_data.py +``` + +4. 测试 API +```bash +# 获取访问令牌 +curl -X POST http://localhost:8000/oauth/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=password&username=Googujiang&password=password123&client_id=5&client_secret=FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk&scope=*" + +# 使用令牌获取用户信息 +curl -X GET http://localhost:8000/api/v2/me/osu \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" +``` + +### 本地开发 + +1. 安装依赖 +```bash +pip install -r requirements.txt +``` + +2. 配置环境变量 +```bash +# 复制服务器配置文件 +cp .env .env.local + +# 复制客户端配置文件(用于测试脚本) +cp .env.client .env.client.local +``` + +3. 启动 MySQL 和 Redis +```bash +# 使用 Docker +docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=osu_api -p 3306:3306 mysql:8.0 +docker run -d --name redis -p 6379:6379 redis:7-alpine +``` + +4. 创建示例数据 +```bash +python create_sample_data.py +``` + +5. 启动应用 +```bash +uvicorn main:app --reload +``` + +6. 测试 API +```bash +# 使用测试脚本(会自动加载 .env 文件) +python test_api.py + +# 或使用原始示例脚本 +python osu_api_example.py +``` + +## 项目结构 + +``` +osu_lazer_api/ +├── app/ +│ ├── __init__.py +│ ├── models.py # Pydantic 数据模型 +│ ├── database.py # SQLAlchemy 数据库模型 +│ ├── config.py # 配置设置 +│ ├── dependencies.py # 依赖注入 +│ ├── auth.py # 认证和令牌管理 +│ └── utils.py # 工具函数 +├── main.py # FastAPI 应用主文件 +├── create_sample_data.py # 示例数据创建脚本 +├── requirements.txt # Python 依赖 +├── .env # 环境变量配置 +├── docker-compose.yml # Docker Compose 配置 +├── Dockerfile # Docker 镜像配置 +└── README.md # 项目说明 +``` + +## 示例用户 + +创建示例数据后,您可以使用以下凭据进行测试: + +- **用户名**: `Googujiang` +- **密码**: `password123` +- **用户ID**: `15651670` + +## 环境变量配置 + +项目包含两个环境配置文件: + +### 服务器配置 (`.env`) +用于配置 FastAPI 服务器的运行参数: + +| 变量名 | 描述 | 默认值 | +|--------|------|--------| +| `DATABASE_URL` | MySQL 数据库连接字符串 | `mysql+pymysql://root:password@localhost:3306/osu_api` | +| `REDIS_URL` | Redis 连接字符串 | `redis://localhost:6379/0` | +| `SECRET_KEY` | JWT 签名密钥 | `your-secret-key-here` | +| `ACCESS_TOKEN_EXPIRE_MINUTES` | 访问令牌过期时间(分钟) | `1440` | +| `OSU_CLIENT_ID` | OAuth 客户端 ID | `5` | +| `OSU_CLIENT_SECRET` | OAuth 客户端密钥 | `FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk` | +| `HOST` | 服务器监听地址 | `0.0.0.0` | +| `PORT` | 服务器监听端口 | `8000` | +| `DEBUG` | 调试模式 | `True` | + +### 客户端配置 (`.env.client`) +用于配置客户端脚本的 API 连接参数: + +| 变量名 | 描述 | 默认值 | +|--------|------|--------| +| `OSU_CLIENT_ID` | OAuth 客户端 ID | `5` | +| `OSU_CLIENT_SECRET` | OAuth 客户端密钥 | `FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk` | +| `OSU_API_URL` | API 服务器地址 | `http://localhost:8000` | + +> **注意**: 在生产环境中,请务必更改默认的密钥和密码! + +## API 使用示例 + +### 获取访问令牌 + +```bash +curl -X POST http://localhost:8000/oauth/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=password&username=Googujiang&password=password123&client_id=5&client_secret=FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk&scope=*" +``` + +响应: +```json +{ + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", + "token_type": "Bearer", + "expires_in": 86400, + "refresh_token": "abc123...", + "scope": "*" +} +``` + +### 获取用户信息 + +```bash +curl -X GET http://localhost:8000/api/v2/me/osu \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" +``` + +### 刷新令牌 + +```bash +curl -X POST http://localhost:8000/oauth/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN&client_id=5&client_secret=FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk" +``` + +## 开发 + +### 添加新用户 + +您可以通过修改 `create_sample_data.py` 文件来添加更多示例用户,或者扩展 API 来支持用户注册功能。 + +### 扩展功能 + +- 添加更多 API 端点(排行榜、谱面信息等) +- 实现实时功能(WebSocket) +- 添加管理面板 +- 实现数据导入/导出功能 + +## 许可证 + +MIT License + +## 贡献 + +欢迎提交 Issue 和 Pull Request! diff --git a/app/router/signalr/__init__.py b/app/router/signalr/__init__.py index 881b00e..5938238 100644 --- a/app/router/signalr/__init__.py +++ b/app/router/signalr/__init__.py @@ -1,3 +1,5 @@ from __future__ import annotations from .router import router as signalr_router + +__all__ = ["signalr_router"] diff --git a/app/router/signalr/hub/hub.py b/app/router/signalr/hub/hub.py index c7717a1..f025aa2 100644 --- a/app/router/signalr/hub/hub.py +++ b/app/router/signalr/hub/hub.py @@ -127,7 +127,7 @@ class Hub: args: list[Any] | None = packet[3] if args is None: args = [] - streams: list[str] | None = packet[4] # TODO: stream support + # streams: list[str] | None = packet[4] # TODO: stream support code = ResultKind.VOID result = None try: diff --git a/docker-compose.yml b/docker-compose.yml index a62114d..8c109c5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,50 +1,50 @@ -version: '3.8' - -services: - mysql: - image: mysql:8.0 - container_name: osu_api_mysql - environment: - MYSQL_ROOT_PASSWORD: password - MYSQL_DATABASE: osu_api - MYSQL_USER: osu_user - MYSQL_PASSWORD: osu_password - ports: - - "3306:3306" - volumes: - - mysql_data:/var/lib/mysql - - ./mysql-init:/docker-entrypoint-initdb.d - restart: unless-stopped - - redis: - image: redis:7-alpine - container_name: osu_api_redis - ports: - - "6379:6379" - volumes: - - redis_data:/data - restart: unless-stopped - command: redis-server --appendonly yes - - api: - build: . - container_name: osu_api_server - ports: - - "8000:8000" - environment: - DATABASE_URL: mysql+aiomysql://osu_user:osu_password@mysql:3306/osu_api - REDIS_URL: redis://redis:6379/0 - SECRET_KEY: your-production-secret-key-here - OSU_CLIENT_ID: "5" - OSU_CLIENT_SECRET: "FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk" - depends_on: - - mysql - - redis - restart: unless-stopped - volumes: - - ./:/app - command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload - -volumes: - mysql_data: - redis_data: +version: '3.8' + +services: + mysql: + image: mysql:8.0 + container_name: osu_api_mysql + environment: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: osu_api + MYSQL_USER: osu_user + MYSQL_PASSWORD: osu_password + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + - ./mysql-init:/docker-entrypoint-initdb.d + restart: unless-stopped + + redis: + image: redis:7-alpine + container_name: osu_api_redis + ports: + - "6379:6379" + volumes: + - redis_data:/data + restart: unless-stopped + command: redis-server --appendonly yes + + api: + build: . + container_name: osu_api_server + ports: + - "8000:8000" + environment: + DATABASE_URL: mysql+aiomysql://osu_user:osu_password@mysql:3306/osu_api + REDIS_URL: redis://redis:6379/0 + SECRET_KEY: your-production-secret-key-here + OSU_CLIENT_ID: "5" + OSU_CLIENT_SECRET: "FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk" + depends_on: + - mysql + - redis + restart: unless-stopped + volumes: + - ./:/app + command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload + +volumes: + mysql_data: + redis_data: diff --git a/migrations/add_lazer_rank_fields.sql b/migrations/add_lazer_rank_fields.sql index 1878b96..d811c90 100644 --- a/migrations/add_lazer_rank_fields.sql +++ b/migrations/add_lazer_rank_fields.sql @@ -1,16 +1,16 @@ --- 创建迁移日志表(如果不存在) -CREATE TABLE IF NOT EXISTS `migration_logs` ( - `id` INT AUTO_INCREMENT PRIMARY KEY, - `version` VARCHAR(50) NOT NULL, - `description` VARCHAR(255) NOT NULL, - `timestamp` DATETIME NOT NULL -); - --- 向 lazer_user_statistics 表添加缺失的字段 -ALTER TABLE `lazer_user_statistics` -ADD COLUMN IF NOT EXISTS `rank_highest` INT NULL COMMENT '最高排名' AFTER `grade_a`, -ADD COLUMN IF NOT EXISTS `rank_highest_updated_at` DATETIME NULL COMMENT '最高排名更新时间' AFTER `rank_highest`; - --- 更新日志 -INSERT INTO `migration_logs` (`version`, `description`, `timestamp`) -VALUES ('20250719', '向 lazer_user_statistics 表添加缺失的字段', NOW()); +-- 创建迁移日志表(如果不存在) +CREATE TABLE IF NOT EXISTS `migration_logs` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `version` VARCHAR(50) NOT NULL, + `description` VARCHAR(255) NOT NULL, + `timestamp` DATETIME NOT NULL +); + +-- 向 lazer_user_statistics 表添加缺失的字段 +ALTER TABLE `lazer_user_statistics` +ADD COLUMN IF NOT EXISTS `rank_highest` INT NULL COMMENT '最高排名' AFTER `grade_a`, +ADD COLUMN IF NOT EXISTS `rank_highest_updated_at` DATETIME NULL COMMENT '最高排名更新时间' AFTER `rank_highest`; + +-- 更新日志 +INSERT INTO `migration_logs` (`version`, `description`, `timestamp`) +VALUES ('20250719', '向 lazer_user_statistics 表添加缺失的字段', NOW()); diff --git a/migrations/add_missing_fields.sql b/migrations/add_missing_fields.sql index 4627f84..464e5fa 100644 --- a/migrations/add_missing_fields.sql +++ b/migrations/add_missing_fields.sql @@ -1,421 +1,421 @@ --- Lazer API 专用数据表创建脚本 --- 基于真实 osu! API 返回数据设计的表结构 --- 完全不修改 bancho.py 原有表结构,创建全新的 lazer 专用表 - --- ============================================ --- Lazer API 专用扩展表 --- ============================================ - --- Lazer 用户扩展信息表 -CREATE TABLE IF NOT EXISTS lazer_user_profiles ( - user_id INT PRIMARY KEY COMMENT '关联 users.id', - - -- 基本状态字段 - is_active TINYINT(1) DEFAULT 1 COMMENT '用户是否激活', - is_bot TINYINT(1) DEFAULT 0 COMMENT '是否为机器人账户', - is_deleted TINYINT(1) DEFAULT 0 COMMENT '是否已删除', - is_online TINYINT(1) DEFAULT 1 COMMENT '是否在线', - is_supporter TINYINT(1) DEFAULT 0 COMMENT '是否为支持者', - is_restricted TINYINT(1) DEFAULT 0 COMMENT '是否被限制', - session_verified TINYINT(1) DEFAULT 0 COMMENT '会话是否已验证', - has_supported TINYINT(1) DEFAULT 0 COMMENT '是否曾经支持过', - pm_friends_only TINYINT(1) DEFAULT 0 COMMENT '是否只接受好友私信', - - -- 基本资料字段 - default_group VARCHAR(50) DEFAULT 'default' COMMENT '默认用户组', - last_visit DATETIME NULL COMMENT '最后访问时间', - join_date DATETIME NULL COMMENT '加入日期', - profile_colour VARCHAR(7) NULL COMMENT '个人资料颜色', - profile_hue INT NULL COMMENT '个人资料色调', - - -- 社交媒体和个人资料字段 - avatar_url VARCHAR(500) NULL COMMENT '头像URL', - cover_url VARCHAR(500) NULL COMMENT '封面URL', - discord VARCHAR(100) NULL COMMENT 'Discord用户名', - twitter VARCHAR(100) NULL COMMENT 'Twitter用户名', - website VARCHAR(500) NULL COMMENT '个人网站', - title VARCHAR(100) NULL COMMENT '用户称号', - title_url VARCHAR(500) NULL COMMENT '称号链接', - interests TEXT NULL COMMENT '兴趣爱好', - location VARCHAR(100) NULL COMMENT '地理位置', - occupation VARCHAR(100) NULL COMMENT '职业', - - -- 游戏相关字段 - playmode VARCHAR(10) DEFAULT 'osu' COMMENT '主要游戏模式', - support_level INT DEFAULT 0 COMMENT '支持者等级', - max_blocks INT DEFAULT 100 COMMENT '最大屏蔽数量', - max_friends INT DEFAULT 500 COMMENT '最大好友数量', - post_count INT DEFAULT 0 COMMENT '帖子数量', - - -- 页面内容 - page_html TEXT NULL COMMENT '个人页面HTML', - page_raw TEXT NULL COMMENT '个人页面原始内容', - - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lazer API 用户扩展资料表'; - --- 用户封面信息表 -CREATE TABLE IF NOT EXISTS lazer_user_covers ( - user_id INT PRIMARY KEY COMMENT '关联 users.id', - custom_url VARCHAR(500) NULL COMMENT '自定义封面URL', - url VARCHAR(500) NULL COMMENT '封面URL', - cover_id INT NULL COMMENT '封面ID', - - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户封面信息表'; - --- 用户国家信息表 -CREATE TABLE IF NOT EXISTS lazer_user_countries ( - user_id INT PRIMARY KEY COMMENT '关联 users.id', - code VARCHAR(2) NOT NULL COMMENT '国家代码', - name VARCHAR(100) NOT NULL COMMENT '国家名称', - - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户国家信息表'; - --- 用户 Kudosu 表 -CREATE TABLE IF NOT EXISTS lazer_user_kudosu ( - user_id INT PRIMARY KEY COMMENT '关联 users.id', - available INT DEFAULT 0 COMMENT '可用 Kudosu', - total INT DEFAULT 0 COMMENT '总 Kudosu', - - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户 Kudosu 表'; - --- 用户统计计数表 -CREATE TABLE IF NOT EXISTS lazer_user_counts ( - user_id INT PRIMARY KEY COMMENT '关联 users.id', - - -- 统计计数字段 - beatmap_playcounts_count INT DEFAULT 0 COMMENT '谱面游玩次数统计', - comments_count INT DEFAULT 0 COMMENT '评论数量', - favourite_beatmapset_count INT DEFAULT 0 COMMENT '收藏谱面集数量', - follower_count INT DEFAULT 0 COMMENT '关注者数量', - graveyard_beatmapset_count INT DEFAULT 0 COMMENT '坟场谱面集数量', - guest_beatmapset_count INT DEFAULT 0 COMMENT '客串谱面集数量', - loved_beatmapset_count INT DEFAULT 0 COMMENT '被喜爱谱面集数量', - mapping_follower_count INT DEFAULT 0 COMMENT '作图关注者数量', - nominated_beatmapset_count INT DEFAULT 0 COMMENT '提名谱面集数量', - pending_beatmapset_count INT DEFAULT 0 COMMENT '待审核谱面集数量', - ranked_beatmapset_count INT DEFAULT 0 COMMENT 'Ranked谱面集数量', - ranked_and_approved_beatmapset_count INT DEFAULT 0 COMMENT 'Ranked+Approved谱面集数量', - unranked_beatmapset_count INT DEFAULT 0 COMMENT '未Ranked谱面集数量', - scores_best_count INT DEFAULT 0 COMMENT '最佳成绩数量', - scores_first_count INT DEFAULT 0 COMMENT '第一名成绩数量', - scores_pinned_count INT DEFAULT 0 COMMENT '置顶成绩数量', - scores_recent_count INT DEFAULT 0 COMMENT '最近成绩数量', - - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lazer API 用户统计计数表'; - --- 用户游戏风格表 (替代 playstyle JSON) -CREATE TABLE IF NOT EXISTS lazer_user_playstyles ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - style VARCHAR(50) NOT NULL COMMENT '游戏风格: mouse, keyboard, tablet, touch', - - INDEX idx_user_id (user_id), - UNIQUE KEY unique_user_style (user_id, style), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户游戏风格表'; - --- 用户个人资料显示顺序表 (替代 profile_order JSON) -CREATE TABLE IF NOT EXISTS lazer_user_profile_sections ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - section_name VARCHAR(50) NOT NULL COMMENT '部分名称', - display_order INT DEFAULT 0 COMMENT '显示顺序', - - INDEX idx_user_id (user_id), - INDEX idx_order (user_id, display_order), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户个人资料显示顺序表'; - --- 用户账户历史表 (替代 account_history JSON) -CREATE TABLE IF NOT EXISTS lazer_user_account_history ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - event_type VARCHAR(50) NOT NULL COMMENT '事件类型', - description TEXT COMMENT '事件描述', - length INT COMMENT '持续时间(秒)', - permanent TINYINT(1) DEFAULT 0 COMMENT '是否永久', - event_time DATETIME NOT NULL COMMENT '事件时间', - - INDEX idx_user_id (user_id), - INDEX idx_event_time (event_time), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户账户历史表'; - --- 用户历史用户名表 (替代 previous_usernames JSON) -CREATE TABLE IF NOT EXISTS lazer_user_previous_usernames ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - username VARCHAR(32) NOT NULL COMMENT '历史用户名', - changed_at DATETIME NOT NULL COMMENT '更改时间', - - INDEX idx_user_id (user_id), - INDEX idx_changed_at (changed_at), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户历史用户名表'; - --- 用户月度游戏次数表 (替代 monthly_playcounts JSON) -CREATE TABLE IF NOT EXISTS lazer_user_monthly_playcounts ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - start_date DATE NOT NULL COMMENT '月份开始日期', - play_count INT DEFAULT 0 COMMENT '游戏次数', - - INDEX idx_user_id (user_id), - INDEX idx_start_date (start_date), - UNIQUE KEY unique_user_month (user_id, start_date), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户月度游戏次数表'; - --- 用户最高排名表 (rank_highest) -CREATE TABLE IF NOT EXISTS lazer_user_rank_highest ( - user_id INT PRIMARY KEY COMMENT '关联 users.id', - rank_position INT NOT NULL COMMENT '最高排名位置', - updated_at DATETIME NOT NULL COMMENT '更新时间', - - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户最高排名表'; - --- ============================================ --- OAuth 令牌表 (Lazer API 专用) --- ============================================ -CREATE TABLE IF NOT EXISTS lazer_oauth_tokens ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL, - access_token VARCHAR(255) NOT NULL, - refresh_token VARCHAR(255) NOT NULL, - expires_at DATETIME NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_user_id (user_id), - INDEX idx_access_token (access_token), - INDEX idx_refresh_token (refresh_token), - INDEX idx_expires_at (expires_at), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lazer API OAuth访问令牌表'; - --- ============================================ --- 用户统计数据表 (基于真实 API 数据结构) --- ============================================ - --- 用户主要统计表 (statistics 字段) -CREATE TABLE IF NOT EXISTS lazer_user_statistics ( - user_id INT NOT NULL, - mode VARCHAR(10) NOT NULL DEFAULT 'osu' COMMENT '游戏模式: osu, taiko, fruits, mania', - - -- 基本命中统计 - count_100 INT DEFAULT 0 COMMENT '100分命中数', - count_300 INT DEFAULT 0 COMMENT '300分命中数', - count_50 INT DEFAULT 0 COMMENT '50分命中数', - count_miss INT DEFAULT 0 COMMENT 'Miss数', - - -- 等级信息 - level_current INT DEFAULT 1 COMMENT '当前等级', - level_progress INT DEFAULT 0 COMMENT '等级进度', - - -- 排名信息 - global_rank INT NULL COMMENT '全球排名', - global_rank_exp INT NULL COMMENT '全球排名(实验性)', - country_rank INT NULL COMMENT '国家/地区排名', - - -- PP 和分数 - pp DECIMAL(10,2) DEFAULT 0.00 COMMENT 'Performance Points', - pp_exp DECIMAL(10,2) DEFAULT 0.00 COMMENT 'PP(实验性)', - ranked_score BIGINT DEFAULT 0 COMMENT 'Ranked分数', - hit_accuracy DECIMAL(5,2) DEFAULT 0.00 COMMENT '命中精度', - total_score BIGINT DEFAULT 0 COMMENT '总分数', - total_hits BIGINT DEFAULT 0 COMMENT '总命中数', - maximum_combo INT DEFAULT 0 COMMENT '最大连击', - - -- 游戏统计 - play_count INT DEFAULT 0 COMMENT '游戏次数', - play_time INT DEFAULT 0 COMMENT '游戏时间(秒)', - replays_watched_by_others INT DEFAULT 0 COMMENT '被观看的Replay次数', - is_ranked TINYINT(1) DEFAULT 0 COMMENT '是否有排名', - - -- 成绩等级计数 (grade_counts) - grade_ss INT DEFAULT 0 COMMENT 'SS等级数', - grade_ssh INT DEFAULT 0 COMMENT 'SSH等级数', - grade_s INT DEFAULT 0 COMMENT 'S等级数', - grade_sh INT DEFAULT 0 COMMENT 'SH等级数', - grade_a INT DEFAULT 0 COMMENT 'A等级数', - - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - PRIMARY KEY (user_id, mode), - INDEX idx_mode (mode), - INDEX idx_global_rank (global_rank), - INDEX idx_country_rank (country_rank), - INDEX idx_pp (pp), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lazer API 用户游戏统计表'; - --- 每日挑战用户统计表 (daily_challenge_user_stats) -CREATE TABLE IF NOT EXISTS lazer_daily_challenge_stats ( - user_id INT PRIMARY KEY COMMENT '关联 users.id', - daily_streak_best INT DEFAULT 0 COMMENT '最佳每日连击', - daily_streak_current INT DEFAULT 0 COMMENT '当前每日连击', - last_update DATE NULL COMMENT '最后更新日期', - last_weekly_streak DATE NULL COMMENT '最后周连击日期', - playcount INT DEFAULT 0 COMMENT '游戏次数', - top_10p_placements INT DEFAULT 0 COMMENT 'Top 10% 位置数', - top_50p_placements INT DEFAULT 0 COMMENT 'Top 50% 位置数', - weekly_streak_best INT DEFAULT 0 COMMENT '最佳周连击', - weekly_streak_current INT DEFAULT 0 COMMENT '当前周连击', - - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='每日挑战用户统计表'; - --- 用户团队信息表 (team 字段) -CREATE TABLE IF NOT EXISTS lazer_user_teams ( - user_id INT PRIMARY KEY COMMENT '关联 users.id', - team_id INT NOT NULL COMMENT '团队ID', - team_name VARCHAR(100) NOT NULL COMMENT '团队名称', - team_short_name VARCHAR(10) NOT NULL COMMENT '团队简称', - flag_url VARCHAR(500) NULL COMMENT '团队旗帜URL', - - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户团队信息表'; - --- 用户成就表 (user_achievements) -CREATE TABLE IF NOT EXISTS lazer_user_achievements ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - achievement_id INT NOT NULL COMMENT '成就ID', - achieved_at DATETIME NOT NULL COMMENT '获得时间', - - INDEX idx_user_id (user_id), - INDEX idx_achievement_id (achievement_id), - INDEX idx_achieved_at (achieved_at), - UNIQUE KEY unique_user_achievement (user_id, achievement_id), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户成就表'; - --- 用户排名历史表 (rank_history) -CREATE TABLE IF NOT EXISTS lazer_user_rank_history ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - mode VARCHAR(10) NOT NULL DEFAULT 'osu' COMMENT '游戏模式', - day_offset INT NOT NULL COMMENT '天数偏移量(从某个基准日期开始)', - rank_position INT NOT NULL COMMENT '排名位置', - - INDEX idx_user_mode (user_id, mode), - INDEX idx_day_offset (day_offset), - UNIQUE KEY unique_user_mode_day (user_id, mode, day_offset), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户排名历史表'; - --- Replay 观看次数表 (replays_watched_counts) -CREATE TABLE IF NOT EXISTS lazer_user_replays_watched ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - start_date DATE NOT NULL COMMENT '开始日期', - count INT DEFAULT 0 COMMENT '观看次数', - - INDEX idx_user_id (user_id), - INDEX idx_start_date (start_date), - UNIQUE KEY unique_user_date (user_id, start_date), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户Replay观看次数表'; - --- 用户徽章表 (badges) -CREATE TABLE IF NOT EXISTS lazer_user_badges ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - badge_id INT NOT NULL COMMENT '徽章ID', - awarded_at DATETIME NULL COMMENT '授予时间', - description TEXT NULL COMMENT '徽章描述', - image_url VARCHAR(500) NULL COMMENT '徽章图片URL', - url VARCHAR(500) NULL COMMENT '徽章链接', - - INDEX idx_user_id (user_id), - INDEX idx_badge_id (badge_id), - UNIQUE KEY unique_user_badge (user_id, badge_id), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户徽章表'; - --- 用户组表 (groups) -CREATE TABLE IF NOT EXISTS lazer_user_groups ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - group_id INT NOT NULL COMMENT '用户组ID', - group_name VARCHAR(100) NOT NULL COMMENT '用户组名称', - group_identifier VARCHAR(50) NULL COMMENT '用户组标识符', - colour VARCHAR(7) NULL COMMENT '用户组颜色', - is_probationary TINYINT(1) DEFAULT 0 COMMENT '是否为试用期', - has_listing TINYINT(1) DEFAULT 1 COMMENT '是否显示在列表中', - has_playmodes TINYINT(1) DEFAULT 0 COMMENT '是否有游戏模式', - - INDEX idx_user_id (user_id), - INDEX idx_group_id (group_id), - UNIQUE KEY unique_user_group (user_id, group_id), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户组表'; - --- 锦标赛横幅表 (active_tournament_banners) -CREATE TABLE IF NOT EXISTS lazer_user_tournament_banners ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - tournament_id INT NOT NULL COMMENT '锦标赛ID', - image_url VARCHAR(500) NOT NULL COMMENT '横幅图片URL', - is_active TINYINT(1) DEFAULT 1 COMMENT '是否为当前活跃横幅', - - INDEX idx_user_id (user_id), - INDEX idx_tournament_id (tournament_id), - INDEX idx_is_active (is_active), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户锦标赛横幅表'; - --- ============================================ --- 占位表 (未来功能扩展用) --- ============================================ - --- 当前赛季统计占位表 -CREATE TABLE IF NOT EXISTS lazer_current_season_stats ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL COMMENT '关联 users.id', - season_id VARCHAR(50) NOT NULL COMMENT '赛季ID', - data_placeholder TEXT COMMENT '赛季数据占位', - - INDEX idx_user_id (user_id), - INDEX idx_season_id (season_id), - UNIQUE KEY unique_user_season (user_id, season_id), - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='当前赛季统计占位表'; - --- 其他功能占位表 -CREATE TABLE IF NOT EXISTS lazer_feature_placeholder ( - id INT AUTO_INCREMENT PRIMARY KEY, - feature_type VARCHAR(50) NOT NULL COMMENT '功能类型', - entity_id INT NOT NULL COMMENT '实体ID', - data_placeholder TEXT COMMENT '功能数据占位', - - INDEX idx_feature_type (feature_type), - INDEX idx_entity_id (entity_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='功能扩展占位表'; +-- Lazer API 专用数据表创建脚本 +-- 基于真实 osu! API 返回数据设计的表结构 +-- 完全不修改 bancho.py 原有表结构,创建全新的 lazer 专用表 + +-- ============================================ +-- Lazer API 专用扩展表 +-- ============================================ + +-- Lazer 用户扩展信息表 +CREATE TABLE IF NOT EXISTS lazer_user_profiles ( + user_id INT PRIMARY KEY COMMENT '关联 users.id', + + -- 基本状态字段 + is_active TINYINT(1) DEFAULT 1 COMMENT '用户是否激活', + is_bot TINYINT(1) DEFAULT 0 COMMENT '是否为机器人账户', + is_deleted TINYINT(1) DEFAULT 0 COMMENT '是否已删除', + is_online TINYINT(1) DEFAULT 1 COMMENT '是否在线', + is_supporter TINYINT(1) DEFAULT 0 COMMENT '是否为支持者', + is_restricted TINYINT(1) DEFAULT 0 COMMENT '是否被限制', + session_verified TINYINT(1) DEFAULT 0 COMMENT '会话是否已验证', + has_supported TINYINT(1) DEFAULT 0 COMMENT '是否曾经支持过', + pm_friends_only TINYINT(1) DEFAULT 0 COMMENT '是否只接受好友私信', + + -- 基本资料字段 + default_group VARCHAR(50) DEFAULT 'default' COMMENT '默认用户组', + last_visit DATETIME NULL COMMENT '最后访问时间', + join_date DATETIME NULL COMMENT '加入日期', + profile_colour VARCHAR(7) NULL COMMENT '个人资料颜色', + profile_hue INT NULL COMMENT '个人资料色调', + + -- 社交媒体和个人资料字段 + avatar_url VARCHAR(500) NULL COMMENT '头像URL', + cover_url VARCHAR(500) NULL COMMENT '封面URL', + discord VARCHAR(100) NULL COMMENT 'Discord用户名', + twitter VARCHAR(100) NULL COMMENT 'Twitter用户名', + website VARCHAR(500) NULL COMMENT '个人网站', + title VARCHAR(100) NULL COMMENT '用户称号', + title_url VARCHAR(500) NULL COMMENT '称号链接', + interests TEXT NULL COMMENT '兴趣爱好', + location VARCHAR(100) NULL COMMENT '地理位置', + occupation VARCHAR(100) NULL COMMENT '职业', + + -- 游戏相关字段 + playmode VARCHAR(10) DEFAULT 'osu' COMMENT '主要游戏模式', + support_level INT DEFAULT 0 COMMENT '支持者等级', + max_blocks INT DEFAULT 100 COMMENT '最大屏蔽数量', + max_friends INT DEFAULT 500 COMMENT '最大好友数量', + post_count INT DEFAULT 0 COMMENT '帖子数量', + + -- 页面内容 + page_html TEXT NULL COMMENT '个人页面HTML', + page_raw TEXT NULL COMMENT '个人页面原始内容', + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lazer API 用户扩展资料表'; + +-- 用户封面信息表 +CREATE TABLE IF NOT EXISTS lazer_user_covers ( + user_id INT PRIMARY KEY COMMENT '关联 users.id', + custom_url VARCHAR(500) NULL COMMENT '自定义封面URL', + url VARCHAR(500) NULL COMMENT '封面URL', + cover_id INT NULL COMMENT '封面ID', + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户封面信息表'; + +-- 用户国家信息表 +CREATE TABLE IF NOT EXISTS lazer_user_countries ( + user_id INT PRIMARY KEY COMMENT '关联 users.id', + code VARCHAR(2) NOT NULL COMMENT '国家代码', + name VARCHAR(100) NOT NULL COMMENT '国家名称', + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户国家信息表'; + +-- 用户 Kudosu 表 +CREATE TABLE IF NOT EXISTS lazer_user_kudosu ( + user_id INT PRIMARY KEY COMMENT '关联 users.id', + available INT DEFAULT 0 COMMENT '可用 Kudosu', + total INT DEFAULT 0 COMMENT '总 Kudosu', + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户 Kudosu 表'; + +-- 用户统计计数表 +CREATE TABLE IF NOT EXISTS lazer_user_counts ( + user_id INT PRIMARY KEY COMMENT '关联 users.id', + + -- 统计计数字段 + beatmap_playcounts_count INT DEFAULT 0 COMMENT '谱面游玩次数统计', + comments_count INT DEFAULT 0 COMMENT '评论数量', + favourite_beatmapset_count INT DEFAULT 0 COMMENT '收藏谱面集数量', + follower_count INT DEFAULT 0 COMMENT '关注者数量', + graveyard_beatmapset_count INT DEFAULT 0 COMMENT '坟场谱面集数量', + guest_beatmapset_count INT DEFAULT 0 COMMENT '客串谱面集数量', + loved_beatmapset_count INT DEFAULT 0 COMMENT '被喜爱谱面集数量', + mapping_follower_count INT DEFAULT 0 COMMENT '作图关注者数量', + nominated_beatmapset_count INT DEFAULT 0 COMMENT '提名谱面集数量', + pending_beatmapset_count INT DEFAULT 0 COMMENT '待审核谱面集数量', + ranked_beatmapset_count INT DEFAULT 0 COMMENT 'Ranked谱面集数量', + ranked_and_approved_beatmapset_count INT DEFAULT 0 COMMENT 'Ranked+Approved谱面集数量', + unranked_beatmapset_count INT DEFAULT 0 COMMENT '未Ranked谱面集数量', + scores_best_count INT DEFAULT 0 COMMENT '最佳成绩数量', + scores_first_count INT DEFAULT 0 COMMENT '第一名成绩数量', + scores_pinned_count INT DEFAULT 0 COMMENT '置顶成绩数量', + scores_recent_count INT DEFAULT 0 COMMENT '最近成绩数量', + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lazer API 用户统计计数表'; + +-- 用户游戏风格表 (替代 playstyle JSON) +CREATE TABLE IF NOT EXISTS lazer_user_playstyles ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + style VARCHAR(50) NOT NULL COMMENT '游戏风格: mouse, keyboard, tablet, touch', + + INDEX idx_user_id (user_id), + UNIQUE KEY unique_user_style (user_id, style), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户游戏风格表'; + +-- 用户个人资料显示顺序表 (替代 profile_order JSON) +CREATE TABLE IF NOT EXISTS lazer_user_profile_sections ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + section_name VARCHAR(50) NOT NULL COMMENT '部分名称', + display_order INT DEFAULT 0 COMMENT '显示顺序', + + INDEX idx_user_id (user_id), + INDEX idx_order (user_id, display_order), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户个人资料显示顺序表'; + +-- 用户账户历史表 (替代 account_history JSON) +CREATE TABLE IF NOT EXISTS lazer_user_account_history ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + event_type VARCHAR(50) NOT NULL COMMENT '事件类型', + description TEXT COMMENT '事件描述', + length INT COMMENT '持续时间(秒)', + permanent TINYINT(1) DEFAULT 0 COMMENT '是否永久', + event_time DATETIME NOT NULL COMMENT '事件时间', + + INDEX idx_user_id (user_id), + INDEX idx_event_time (event_time), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户账户历史表'; + +-- 用户历史用户名表 (替代 previous_usernames JSON) +CREATE TABLE IF NOT EXISTS lazer_user_previous_usernames ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + username VARCHAR(32) NOT NULL COMMENT '历史用户名', + changed_at DATETIME NOT NULL COMMENT '更改时间', + + INDEX idx_user_id (user_id), + INDEX idx_changed_at (changed_at), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户历史用户名表'; + +-- 用户月度游戏次数表 (替代 monthly_playcounts JSON) +CREATE TABLE IF NOT EXISTS lazer_user_monthly_playcounts ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + start_date DATE NOT NULL COMMENT '月份开始日期', + play_count INT DEFAULT 0 COMMENT '游戏次数', + + INDEX idx_user_id (user_id), + INDEX idx_start_date (start_date), + UNIQUE KEY unique_user_month (user_id, start_date), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户月度游戏次数表'; + +-- 用户最高排名表 (rank_highest) +CREATE TABLE IF NOT EXISTS lazer_user_rank_highest ( + user_id INT PRIMARY KEY COMMENT '关联 users.id', + rank_position INT NOT NULL COMMENT '最高排名位置', + updated_at DATETIME NOT NULL COMMENT '更新时间', + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户最高排名表'; + +-- ============================================ +-- OAuth 令牌表 (Lazer API 专用) +-- ============================================ +CREATE TABLE IF NOT EXISTS lazer_oauth_tokens ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + access_token VARCHAR(255) NOT NULL, + refresh_token VARCHAR(255) NOT NULL, + expires_at DATETIME NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_user_id (user_id), + INDEX idx_access_token (access_token), + INDEX idx_refresh_token (refresh_token), + INDEX idx_expires_at (expires_at), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lazer API OAuth访问令牌表'; + +-- ============================================ +-- 用户统计数据表 (基于真实 API 数据结构) +-- ============================================ + +-- 用户主要统计表 (statistics 字段) +CREATE TABLE IF NOT EXISTS lazer_user_statistics ( + user_id INT NOT NULL, + mode VARCHAR(10) NOT NULL DEFAULT 'osu' COMMENT '游戏模式: osu, taiko, fruits, mania', + + -- 基本命中统计 + count_100 INT DEFAULT 0 COMMENT '100分命中数', + count_300 INT DEFAULT 0 COMMENT '300分命中数', + count_50 INT DEFAULT 0 COMMENT '50分命中数', + count_miss INT DEFAULT 0 COMMENT 'Miss数', + + -- 等级信息 + level_current INT DEFAULT 1 COMMENT '当前等级', + level_progress INT DEFAULT 0 COMMENT '等级进度', + + -- 排名信息 + global_rank INT NULL COMMENT '全球排名', + global_rank_exp INT NULL COMMENT '全球排名(实验性)', + country_rank INT NULL COMMENT '国家/地区排名', + + -- PP 和分数 + pp DECIMAL(10,2) DEFAULT 0.00 COMMENT 'Performance Points', + pp_exp DECIMAL(10,2) DEFAULT 0.00 COMMENT 'PP(实验性)', + ranked_score BIGINT DEFAULT 0 COMMENT 'Ranked分数', + hit_accuracy DECIMAL(5,2) DEFAULT 0.00 COMMENT '命中精度', + total_score BIGINT DEFAULT 0 COMMENT '总分数', + total_hits BIGINT DEFAULT 0 COMMENT '总命中数', + maximum_combo INT DEFAULT 0 COMMENT '最大连击', + + -- 游戏统计 + play_count INT DEFAULT 0 COMMENT '游戏次数', + play_time INT DEFAULT 0 COMMENT '游戏时间(秒)', + replays_watched_by_others INT DEFAULT 0 COMMENT '被观看的Replay次数', + is_ranked TINYINT(1) DEFAULT 0 COMMENT '是否有排名', + + -- 成绩等级计数 (grade_counts) + grade_ss INT DEFAULT 0 COMMENT 'SS等级数', + grade_ssh INT DEFAULT 0 COMMENT 'SSH等级数', + grade_s INT DEFAULT 0 COMMENT 'S等级数', + grade_sh INT DEFAULT 0 COMMENT 'SH等级数', + grade_a INT DEFAULT 0 COMMENT 'A等级数', + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + PRIMARY KEY (user_id, mode), + INDEX idx_mode (mode), + INDEX idx_global_rank (global_rank), + INDEX idx_country_rank (country_rank), + INDEX idx_pp (pp), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lazer API 用户游戏统计表'; + +-- 每日挑战用户统计表 (daily_challenge_user_stats) +CREATE TABLE IF NOT EXISTS lazer_daily_challenge_stats ( + user_id INT PRIMARY KEY COMMENT '关联 users.id', + daily_streak_best INT DEFAULT 0 COMMENT '最佳每日连击', + daily_streak_current INT DEFAULT 0 COMMENT '当前每日连击', + last_update DATE NULL COMMENT '最后更新日期', + last_weekly_streak DATE NULL COMMENT '最后周连击日期', + playcount INT DEFAULT 0 COMMENT '游戏次数', + top_10p_placements INT DEFAULT 0 COMMENT 'Top 10% 位置数', + top_50p_placements INT DEFAULT 0 COMMENT 'Top 50% 位置数', + weekly_streak_best INT DEFAULT 0 COMMENT '最佳周连击', + weekly_streak_current INT DEFAULT 0 COMMENT '当前周连击', + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='每日挑战用户统计表'; + +-- 用户团队信息表 (team 字段) +CREATE TABLE IF NOT EXISTS lazer_user_teams ( + user_id INT PRIMARY KEY COMMENT '关联 users.id', + team_id INT NOT NULL COMMENT '团队ID', + team_name VARCHAR(100) NOT NULL COMMENT '团队名称', + team_short_name VARCHAR(10) NOT NULL COMMENT '团队简称', + flag_url VARCHAR(500) NULL COMMENT '团队旗帜URL', + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户团队信息表'; + +-- 用户成就表 (user_achievements) +CREATE TABLE IF NOT EXISTS lazer_user_achievements ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + achievement_id INT NOT NULL COMMENT '成就ID', + achieved_at DATETIME NOT NULL COMMENT '获得时间', + + INDEX idx_user_id (user_id), + INDEX idx_achievement_id (achievement_id), + INDEX idx_achieved_at (achieved_at), + UNIQUE KEY unique_user_achievement (user_id, achievement_id), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户成就表'; + +-- 用户排名历史表 (rank_history) +CREATE TABLE IF NOT EXISTS lazer_user_rank_history ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + mode VARCHAR(10) NOT NULL DEFAULT 'osu' COMMENT '游戏模式', + day_offset INT NOT NULL COMMENT '天数偏移量(从某个基准日期开始)', + rank_position INT NOT NULL COMMENT '排名位置', + + INDEX idx_user_mode (user_id, mode), + INDEX idx_day_offset (day_offset), + UNIQUE KEY unique_user_mode_day (user_id, mode, day_offset), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户排名历史表'; + +-- Replay 观看次数表 (replays_watched_counts) +CREATE TABLE IF NOT EXISTS lazer_user_replays_watched ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + start_date DATE NOT NULL COMMENT '开始日期', + count INT DEFAULT 0 COMMENT '观看次数', + + INDEX idx_user_id (user_id), + INDEX idx_start_date (start_date), + UNIQUE KEY unique_user_date (user_id, start_date), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户Replay观看次数表'; + +-- 用户徽章表 (badges) +CREATE TABLE IF NOT EXISTS lazer_user_badges ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + badge_id INT NOT NULL COMMENT '徽章ID', + awarded_at DATETIME NULL COMMENT '授予时间', + description TEXT NULL COMMENT '徽章描述', + image_url VARCHAR(500) NULL COMMENT '徽章图片URL', + url VARCHAR(500) NULL COMMENT '徽章链接', + + INDEX idx_user_id (user_id), + INDEX idx_badge_id (badge_id), + UNIQUE KEY unique_user_badge (user_id, badge_id), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户徽章表'; + +-- 用户组表 (groups) +CREATE TABLE IF NOT EXISTS lazer_user_groups ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + group_id INT NOT NULL COMMENT '用户组ID', + group_name VARCHAR(100) NOT NULL COMMENT '用户组名称', + group_identifier VARCHAR(50) NULL COMMENT '用户组标识符', + colour VARCHAR(7) NULL COMMENT '用户组颜色', + is_probationary TINYINT(1) DEFAULT 0 COMMENT '是否为试用期', + has_listing TINYINT(1) DEFAULT 1 COMMENT '是否显示在列表中', + has_playmodes TINYINT(1) DEFAULT 0 COMMENT '是否有游戏模式', + + INDEX idx_user_id (user_id), + INDEX idx_group_id (group_id), + UNIQUE KEY unique_user_group (user_id, group_id), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户组表'; + +-- 锦标赛横幅表 (active_tournament_banners) +CREATE TABLE IF NOT EXISTS lazer_user_tournament_banners ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + tournament_id INT NOT NULL COMMENT '锦标赛ID', + image_url VARCHAR(500) NOT NULL COMMENT '横幅图片URL', + is_active TINYINT(1) DEFAULT 1 COMMENT '是否为当前活跃横幅', + + INDEX idx_user_id (user_id), + INDEX idx_tournament_id (tournament_id), + INDEX idx_is_active (is_active), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户锦标赛横幅表'; + +-- ============================================ +-- 占位表 (未来功能扩展用) +-- ============================================ + +-- 当前赛季统计占位表 +CREATE TABLE IF NOT EXISTS lazer_current_season_stats ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT '关联 users.id', + season_id VARCHAR(50) NOT NULL COMMENT '赛季ID', + data_placeholder TEXT COMMENT '赛季数据占位', + + INDEX idx_user_id (user_id), + INDEX idx_season_id (season_id), + UNIQUE KEY unique_user_season (user_id, season_id), + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='当前赛季统计占位表'; + +-- 其他功能占位表 +CREATE TABLE IF NOT EXISTS lazer_feature_placeholder ( + id INT AUTO_INCREMENT PRIMARY KEY, + feature_type VARCHAR(50) NOT NULL COMMENT '功能类型', + entity_id INT NOT NULL COMMENT '实体ID', + data_placeholder TEXT COMMENT '功能数据占位', + + INDEX idx_feature_type (feature_type), + INDEX idx_entity_id (entity_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='功能扩展占位表'; diff --git a/migrations/base.sql b/migrations/base.sql index 79bba0a..665723f 100644 --- a/migrations/base.sql +++ b/migrations/base.sql @@ -1,486 +1,486 @@ -create table achievements -( - id int auto_increment - primary key, - file varchar(128) not null, - name varchar(128) charset utf8 not null, - `desc` varchar(256) charset utf8 not null, - cond varchar(64) not null, - constraint achievements_desc_uindex - unique (`desc`), - constraint achievements_file_uindex - unique (file), - constraint achievements_name_uindex - unique (name) -); - -create table channels -( - id int auto_increment - primary key, - name varchar(32) not null, - topic varchar(256) not null, - read_priv int default 1 not null, - write_priv int default 2 not null, - auto_join tinyint(1) default 0 not null, - constraint channels_name_uindex - unique (name) -); -create index channels_auto_join_index - on channels (auto_join); - -create table clans -( - id int auto_increment - primary key, - name varchar(16) charset utf8 not null, - tag varchar(6) charset utf8 not null, - owner int not null, - created_at datetime not null, - constraint clans_name_uindex - unique (name), - constraint clans_owner_uindex - unique (owner), - constraint clans_tag_uindex - unique (tag) -); - -create table client_hashes -( - userid int not null, - osupath char(32) not null, - adapters char(32) not null, - uninstall_id char(32) not null, - disk_serial char(32) not null, - latest_time datetime not null, - occurrences int default 0 not null, - primary key (userid, osupath, adapters, uninstall_id, disk_serial) -); - -create table comments -( - id int auto_increment - primary key, - target_id int not null comment 'replay, map, or set id', - target_type enum('replay', 'map', 'song') not null, - userid int not null, - time int not null, - comment varchar(80) charset utf8 not null, - colour char(6) null comment 'rgb hex string' -); - -create table favourites -( - userid int not null, - setid int not null, - created_at int default 0 not null, - primary key (userid, setid) -); - -create table ingame_logins -( - id int auto_increment - primary key, - userid int not null, - ip varchar(45) not null comment 'maxlen for ipv6', - osu_ver date not null, - osu_stream varchar(11) not null, - datetime datetime not null -); - -create table relationships -( - user1 int not null, - user2 int not null, - type enum('friend', 'block') not null, - primary key (user1, user2) -); - -create table logs -( - id int auto_increment - primary key, - `from` int not null comment 'both from and to are playerids', - `to` int not null, - `action` varchar(32) not null, - msg varchar(2048) charset utf8 null, - time datetime not null on update CURRENT_TIMESTAMP -); - -create table mail -( - id int auto_increment - primary key, - from_id int not null, - to_id int not null, - msg varchar(2048) charset utf8 not null, - time int null, - `read` tinyint(1) default 0 not null -); - -create table maps -( - server enum('osu!', 'private') default 'osu!' not null, - id int not null, - set_id int not null, - status int not null, - md5 char(32) not null, - artist varchar(128) charset utf8 not null, - title varchar(128) charset utf8 not null, - version varchar(128) charset utf8 not null, - creator varchar(19) charset utf8 not null, - filename varchar(256) charset utf8 not null, - last_update datetime not null, - total_length int not null, - max_combo int not null, - frozen tinyint(1) default 0 not null, - plays int default 0 not null, - passes int default 0 not null, - mode tinyint(1) default 0 not null, - bpm float(12,2) default 0.00 not null, - cs float(4,2) default 0.00 not null, - ar float(4,2) default 0.00 not null, - od float(4,2) default 0.00 not null, - hp float(4,2) default 0.00 not null, - diff float(6,3) default 0.000 not null, - primary key (server, id), - constraint maps_id_uindex - unique (id), - constraint maps_md5_uindex - unique (md5) -); -create index maps_set_id_index - on maps (set_id); -create index maps_status_index - on maps (status); -create index maps_filename_index - on maps (filename); -create index maps_plays_index - on maps (plays); -create index maps_mode_index - on maps (mode); -create index maps_frozen_index - on maps (frozen); - -create table mapsets -( - server enum('osu!', 'private') default 'osu!' not null, - id int not null, - last_osuapi_check datetime default CURRENT_TIMESTAMP not null, - primary key (server, id), - constraint nmapsets_id_uindex - unique (id) -); - -create table map_requests -( - id int auto_increment - primary key, - map_id int not null, - player_id int not null, - datetime datetime not null, - active tinyint(1) not null -); - -create table performance_reports -( - scoreid bigint(20) unsigned not null, - mod_mode enum('vanilla', 'relax', 'autopilot') default 'vanilla' not null, - os varchar(64) not null, - fullscreen tinyint(1) not null, - fps_cap varchar(16) not null, - compatibility tinyint(1) not null, - version varchar(16) not null, - start_time int not null, - end_time int not null, - frame_count int not null, - spike_frames int not null, - aim_rate int not null, - completion tinyint(1) not null, - identifier varchar(128) null comment 'really don''t know much about this yet', - average_frametime int not null, - primary key (scoreid, mod_mode) -); - -create table ratings -( - userid int not null, - map_md5 char(32) not null, - rating tinyint(2) not null, - primary key (userid, map_md5) -); - -create table scores -( - id bigint unsigned auto_increment - primary key, - map_md5 char(32) not null, - score int not null, - pp float(7,3) not null, - acc float(6,3) not null, - max_combo int not null, - mods int not null, - n300 int not null, - n100 int not null, - n50 int not null, - nmiss int not null, - ngeki int not null, - nkatu int not null, - grade varchar(2) default 'N' not null, - status tinyint not null, - mode tinyint not null, - play_time datetime not null, - time_elapsed int not null, - client_flags int not null, - userid int not null, - perfect tinyint(1) not null, - online_checksum char(32) not null -); -create index scores_map_md5_index - on scores (map_md5); -create index scores_score_index - on scores (score); -create index scores_pp_index - on scores (pp); -create index scores_mods_index - on scores (mods); -create index scores_status_index - on scores (status); -create index scores_mode_index - on scores (mode); -create index scores_play_time_index - on scores (play_time); -create index scores_userid_index - on scores (userid); -create index scores_online_checksum_index - on scores (online_checksum); -create index scores_fetch_leaderboard_generic_index - on scores (map_md5, status, mode); - -create table startups -( - id int auto_increment - primary key, - ver_major tinyint not null, - ver_minor tinyint not null, - ver_micro tinyint not null, - datetime datetime not null -); - -create table stats -( - id int auto_increment, - mode tinyint(1) not null, - tscore bigint unsigned default 0 not null, - rscore bigint unsigned default 0 not null, - pp int unsigned default 0 not null, - plays int unsigned default 0 not null, - playtime int unsigned default 0 not null, - acc float(6,3) default 0.000 not null, - max_combo int unsigned default 0 not null, - total_hits int unsigned default 0 not null, - replay_views int unsigned default 0 not null, - xh_count int unsigned default 0 not null, - x_count int unsigned default 0 not null, - sh_count int unsigned default 0 not null, - s_count int unsigned default 0 not null, - a_count int unsigned default 0 not null, - primary key (id, mode) -); -create index stats_mode_index - on stats (mode); -create index stats_pp_index - on stats (pp); -create index stats_tscore_index - on stats (tscore); -create index stats_rscore_index - on stats (rscore); - -create table tourney_pool_maps -( - map_id int not null, - pool_id int not null, - mods int not null, - slot tinyint not null, - primary key (map_id, pool_id) -); -create index tourney_pool_maps_mods_slot_index - on tourney_pool_maps (mods, slot); -create index tourney_pool_maps_tourney_pools_id_fk - on tourney_pool_maps (pool_id); - -create table tourney_pools -( - id int auto_increment - primary key, - name varchar(16) not null, - created_at datetime not null, - created_by int not null -); - -create index tourney_pools_users_id_fk - on tourney_pools (created_by); - -create table user_achievements -( - userid int not null, - achid int not null, - primary key (userid, achid) -); -create index user_achievements_achid_index - on user_achievements (achid); -create index user_achievements_userid_index - on user_achievements (userid); - -create table users -( - id int auto_increment - primary key, - name varchar(32) charset utf8 not null, - safe_name varchar(32) charset utf8 not null, - email varchar(254) not null, - priv int default 1 not null, - pw_bcrypt char(60) not null, - country char(2) default 'xx' not null, - silence_end int default 0 not null, - donor_end int default 0 not null, - creation_time int default 0 not null, - latest_activity int default 0 not null, - clan_id int default 0 not null, - clan_priv tinyint(1) default 0 not null, - preferred_mode int default 0 not null, - play_style int default 0 not null, - custom_badge_name varchar(16) charset utf8 null, - custom_badge_icon varchar(64) null, - userpage_content varchar(2048) charset utf8 null, - api_key char(36) null, - constraint users_api_key_uindex - unique (api_key), - constraint users_email_uindex - unique (email), - constraint users_name_uindex - unique (name), - constraint users_safe_name_uindex - unique (safe_name) -); -create index users_priv_index - on users (priv); -create index users_clan_id_index - on users (clan_id); -create index users_clan_priv_index - on users (clan_priv); -create index users_country_index - on users (country); - -insert into users (id, name, safe_name, priv, country, silence_end, email, pw_bcrypt, creation_time, latest_activity) -values (1, 'BanchoBot', 'banchobot', 1, 'ca', 0, 'bot@akatsuki.pw', - '_______________________my_cool_bcrypt_______________________', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()); - -INSERT INTO stats (id, mode) VALUES (1, 0); # vn!std -INSERT INTO stats (id, mode) VALUES (1, 1); # vn!taiko -INSERT INTO stats (id, mode) VALUES (1, 2); # vn!catch -INSERT INTO stats (id, mode) VALUES (1, 3); # vn!mania -INSERT INTO stats (id, mode) VALUES (1, 4); # rx!std -INSERT INTO stats (id, mode) VALUES (1, 5); # rx!taiko -INSERT INTO stats (id, mode) VALUES (1, 6); # rx!catch -INSERT INTO stats (id, mode) VALUES (1, 8); # ap!std - - -# userid 2 is reserved for ppy in osu!, and the -# client will not allow users to pm this id. -# If you want this, simply remove these two lines. -alter table users auto_increment = 3; -alter table stats auto_increment = 3; - -insert into channels (name, topic, read_priv, write_priv, auto_join) -values ('#osu', 'General discussion.', 1, 2, true), - ('#announce', 'Exemplary performance and public announcements.', 1, 24576, true), - ('#lobby', 'Multiplayer lobby discussion room.', 1, 2, false), - ('#supporter', 'General discussion for supporters.', 48, 48, false), - ('#staff', 'General discussion for staff members.', 28672, 28672, true), - ('#admin', 'General discussion for administrators.', 24576, 24576, true), - ('#dev', 'General discussion for developers.', 16384, 16384, true); - -insert into achievements (id, file, name, `desc`, cond) values (1, 'osu-skill-pass-1', 'Rising Star', 'Can''t go forward without the first steps.', '(score.mods & 1 == 0) and 1 <= score.sr < 2 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (2, 'osu-skill-pass-2', 'Constellation Prize', 'Definitely not a consolation prize. Now things start getting hard!', '(score.mods & 1 == 0) and 2 <= score.sr < 3 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (3, 'osu-skill-pass-3', 'Building Confidence', 'Oh, you''ve SO got this.', '(score.mods & 1 == 0) and 3 <= score.sr < 4 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (4, 'osu-skill-pass-4', 'Insanity Approaches', 'You''re not twitching, you''re just ready.', '(score.mods & 1 == 0) and 4 <= score.sr < 5 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (5, 'osu-skill-pass-5', 'These Clarion Skies', 'Everything seems so clear now.', '(score.mods & 1 == 0) and 5 <= score.sr < 6 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (6, 'osu-skill-pass-6', 'Above and Beyond', 'A cut above the rest.', '(score.mods & 1 == 0) and 6 <= score.sr < 7 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (7, 'osu-skill-pass-7', 'Supremacy', 'All marvel before your prowess.', '(score.mods & 1 == 0) and 7 <= score.sr < 8 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (8, 'osu-skill-pass-8', 'Absolution', 'My god, you''re full of stars!', '(score.mods & 1 == 0) and 8 <= score.sr < 9 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (9, 'osu-skill-pass-9', 'Event Horizon', 'No force dares to pull you under.', '(score.mods & 1 == 0) and 9 <= score.sr < 10 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (10, 'osu-skill-pass-10', 'Phantasm', 'Fevered is your passion, extraordinary is your skill.', '(score.mods & 1 == 0) and 10 <= score.sr < 11 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (11, 'osu-skill-fc-1', 'Totality', 'All the notes. Every single one.', 'score.perfect and 1 <= score.sr < 2 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (12, 'osu-skill-fc-2', 'Business As Usual', 'Two to go, please.', 'score.perfect and 2 <= score.sr < 3 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (13, 'osu-skill-fc-3', 'Building Steam', 'Hey, this isn''t so bad.', 'score.perfect and 3 <= score.sr < 4 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (14, 'osu-skill-fc-4', 'Moving Forward', 'Bet you feel good about that.', 'score.perfect and 4 <= score.sr < 5 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (15, 'osu-skill-fc-5', 'Paradigm Shift', 'Surprisingly difficult.', 'score.perfect and 5 <= score.sr < 6 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (16, 'osu-skill-fc-6', 'Anguish Quelled', 'Don''t choke.', 'score.perfect and 6 <= score.sr < 7 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (17, 'osu-skill-fc-7', 'Never Give Up', 'Excellence is its own reward.', 'score.perfect and 7 <= score.sr < 8 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (18, 'osu-skill-fc-8', 'Aberration', 'They said it couldn''t be done. They were wrong.', 'score.perfect and 8 <= score.sr < 9 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (19, 'osu-skill-fc-9', 'Chosen', 'Reign among the Prometheans, where you belong.', 'score.perfect and 9 <= score.sr < 10 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (20, 'osu-skill-fc-10', 'Unfathomable', 'You have no equal.', 'score.perfect and 10 <= score.sr < 11 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (21, 'osu-combo-500', '500 Combo', '500 big ones! You''re moving up in the world!', '500 <= score.max_combo < 750 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (22, 'osu-combo-750', '750 Combo', '750 notes back to back? Woah.', '750 <= score.max_combo < 1000 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (23, 'osu-combo-1000', '1000 Combo', 'A thousand reasons why you rock at this game.', '1000 <= score.max_combo < 2000 and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (24, 'osu-combo-2000', '2000 Combo', 'Nothing can stop you now.', '2000 <= score.max_combo and mode_vn == 0'); -insert into achievements (id, file, name, `desc`, cond) values (25, 'taiko-skill-pass-1', 'My First Don', 'Marching to the beat of your own drum. Literally.', '(score.mods & 1 == 0) and 1 <= score.sr < 2 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (26, 'taiko-skill-pass-2', 'Katsu Katsu Katsu', 'Hora! Izuko!', '(score.mods & 1 == 0) and 2 <= score.sr < 3 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (27, 'taiko-skill-pass-3', 'Not Even Trying', 'Muzukashii? Not even.', '(score.mods & 1 == 0) and 3 <= score.sr < 4 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (28, 'taiko-skill-pass-4', 'Face Your Demons', 'The first trials are now behind you, but are you a match for the Oni?', '(score.mods & 1 == 0) and 4 <= score.sr < 5 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (29, 'taiko-skill-pass-5', 'The Demon Within', 'No rest for the wicked.', '(score.mods & 1 == 0) and 5 <= score.sr < 6 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (30, 'taiko-skill-pass-6', 'Drumbreaker', 'Too strong.', '(score.mods & 1 == 0) and 6 <= score.sr < 7 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (31, 'taiko-skill-pass-7', 'The Godfather', 'You are the Don of Dons.', '(score.mods & 1 == 0) and 7 <= score.sr < 8 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (32, 'taiko-skill-pass-8', 'Rhythm Incarnate', 'Feel the beat. Become the beat.', '(score.mods & 1 == 0) and 8 <= score.sr < 9 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (33, 'taiko-skill-fc-1', 'Keeping Time', 'Don, then katsu. Don, then katsu..', 'score.perfect and 1 <= score.sr < 2 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (34, 'taiko-skill-fc-2', 'To Your Own Beat', 'Straight and steady.', 'score.perfect and 2 <= score.sr < 3 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (35, 'taiko-skill-fc-3', 'Big Drums', 'Bigger scores to match.', 'score.perfect and 3 <= score.sr < 4 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (36, 'taiko-skill-fc-4', 'Adversity Overcome', 'Difficult? Not for you.', 'score.perfect and 4 <= score.sr < 5 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (37, 'taiko-skill-fc-5', 'Demonslayer', 'An Oni felled forevermore.', 'score.perfect and 5 <= score.sr < 6 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (38, 'taiko-skill-fc-6', 'Rhythm''s Call', 'Heralding true skill.', 'score.perfect and 6 <= score.sr < 7 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (39, 'taiko-skill-fc-7', 'Time Everlasting', 'Not a single beat escapes you.', 'score.perfect and 7 <= score.sr < 8 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (40, 'taiko-skill-fc-8', 'The Drummer''s Throne', 'Percussive brilliance befitting royalty alone.', 'score.perfect and 8 <= score.sr < 9 and mode_vn == 1'); -insert into achievements (id, file, name, `desc`, cond) values (41, 'fruits-skill-pass-1', 'A Slice Of Life', 'Hey, this fruit catching business isn''t bad.', '(score.mods & 1 == 0) and 1 <= score.sr < 2 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (42, 'fruits-skill-pass-2', 'Dashing Ever Forward', 'Fast is how you do it.', '(score.mods & 1 == 0) and 2 <= score.sr < 3 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (43, 'fruits-skill-pass-3', 'Zesty Disposition', 'No scurvy for you, not with that much fruit.', '(score.mods & 1 == 0) and 3 <= score.sr < 4 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (44, 'fruits-skill-pass-4', 'Hyperdash ON!', 'Time and distance is no obstacle to you.', '(score.mods & 1 == 0) and 4 <= score.sr < 5 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (45, 'fruits-skill-pass-5', 'It''s Raining Fruit', 'And you can catch them all.', '(score.mods & 1 == 0) and 5 <= score.sr < 6 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (46, 'fruits-skill-pass-6', 'Fruit Ninja', 'Legendary techniques.', '(score.mods & 1 == 0) and 6 <= score.sr < 7 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (47, 'fruits-skill-pass-7', 'Dreamcatcher', 'No fruit, only dreams now.', '(score.mods & 1 == 0) and 7 <= score.sr < 8 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (48, 'fruits-skill-pass-8', 'Lord of the Catch', 'Your kingdom kneels before you.', '(score.mods & 1 == 0) and 8 <= score.sr < 9 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (49, 'fruits-skill-fc-1', 'Sweet And Sour', 'Apples and oranges, literally.', 'score.perfect and 1 <= score.sr < 2 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (50, 'fruits-skill-fc-2', 'Reaching The Core', 'The seeds of future success.', 'score.perfect and 2 <= score.sr < 3 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (51, 'fruits-skill-fc-3', 'Clean Platter', 'Clean only of failure. It is completely full, otherwise.', 'score.perfect and 3 <= score.sr < 4 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (52, 'fruits-skill-fc-4', 'Between The Rain', 'No umbrella needed.', 'score.perfect and 4 <= score.sr < 5 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (53, 'fruits-skill-fc-5', 'Addicted', 'That was an overdose?', 'score.perfect and 5 <= score.sr < 6 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (54, 'fruits-skill-fc-6', 'Quickening', 'A dash above normal limits.', 'score.perfect and 6 <= score.sr < 7 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (55, 'fruits-skill-fc-7', 'Supersonic', 'Faster than is reasonably necessary.', 'score.perfect and 7 <= score.sr < 8 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (56, 'fruits-skill-fc-8', 'Dashing Scarlet', 'Speed beyond mortal reckoning.', 'score.perfect and 8 <= score.sr < 9 and mode_vn == 2'); -insert into achievements (id, file, name, `desc`, cond) values (57, 'mania-skill-pass-1', 'First Steps', 'It isn''t 9-to-5, but 1-to-9. Keys, that is.', '(score.mods & 1 == 0) and 1 <= score.sr < 2 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (58, 'mania-skill-pass-2', 'No Normal Player', 'Not anymore, at least.', '(score.mods & 1 == 0) and 2 <= score.sr < 3 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (59, 'mania-skill-pass-3', 'Impulse Drive', 'Not quite hyperspeed, but getting close.', '(score.mods & 1 == 0) and 3 <= score.sr < 4 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (60, 'mania-skill-pass-4', 'Hyperspeed', 'Woah.', '(score.mods & 1 == 0) and 4 <= score.sr < 5 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (61, 'mania-skill-pass-5', 'Ever Onwards', 'Another challenge is just around the corner.', '(score.mods & 1 == 0) and 5 <= score.sr < 6 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (62, 'mania-skill-pass-6', 'Another Surpassed', 'Is there no limit to your skills?', '(score.mods & 1 == 0) and 6 <= score.sr < 7 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (63, 'mania-skill-pass-7', 'Extra Credit', 'See me after class.', '(score.mods & 1 == 0) and 7 <= score.sr < 8 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (64, 'mania-skill-pass-8', 'Maniac', 'There''s just no stopping you.', '(score.mods & 1 == 0) and 8 <= score.sr < 9 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (65, 'mania-skill-fc-1', 'Keystruck', 'The beginning of a new story', 'score.perfect and 1 <= score.sr < 2 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (66, 'mania-skill-fc-2', 'Keying In', 'Finding your groove.', 'score.perfect and 2 <= score.sr < 3 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (67, 'mania-skill-fc-3', 'Hyperflow', 'You can *feel* the rhythm.', 'score.perfect and 3 <= score.sr < 4 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (68, 'mania-skill-fc-4', 'Breakthrough', 'Many skills mastered, rolled into one.', 'score.perfect and 4 <= score.sr < 5 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (69, 'mania-skill-fc-5', 'Everything Extra', 'Giving your all is giving everything you have.', 'score.perfect and 5 <= score.sr < 6 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (70, 'mania-skill-fc-6', 'Level Breaker', 'Finesse beyond reason', 'score.perfect and 6 <= score.sr < 7 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (71, 'mania-skill-fc-7', 'Step Up', 'A precipice rarely seen.', 'score.perfect and 7 <= score.sr < 8 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (72, 'mania-skill-fc-8', 'Behind The Veil', 'Supernatural!', 'score.perfect and 8 <= score.sr < 9 and mode_vn == 3'); -insert into achievements (id, file, name, `desc`, cond) values (73, 'all-intro-suddendeath', 'Finality', 'High stakes, no regrets.', 'score.mods == 32'); -insert into achievements (id, file, name, `desc`, cond) values (74, 'all-intro-hidden', 'Blindsight', 'I can see just perfectly', 'score.mods & 8'); -insert into achievements (id, file, name, `desc`, cond) values (75, 'all-intro-perfect', 'Perfectionist', 'Accept nothing but the best.', 'score.mods & 16384'); -insert into achievements (id, file, name, `desc`, cond) values (76, 'all-intro-hardrock', 'Rock Around The Clock', "You can\'t stop the rock.", 'score.mods & 16'); -insert into achievements (id, file, name, `desc`, cond) values (77, 'all-intro-doubletime', 'Time And A Half', "Having a right ol\' time. One and a half of them, almost.", 'score.mods & 64'); -insert into achievements (id, file, name, `desc`, cond) values (78, 'all-intro-flashlight', 'Are You Afraid Of The Dark?', "Harder than it looks, probably because it\'s hard to look.", 'score.mods & 1024'); -insert into achievements (id, file, name, `desc`, cond) values (79, 'all-intro-easy', 'Dial It Right Back', 'Sometimes you just want to take it easy.', 'score.mods & 2'); -insert into achievements (id, file, name, `desc`, cond) values (80, 'all-intro-nofail', 'Risk Averse', 'Safety nets are fun!', 'score.mods & 1'); -insert into achievements (id, file, name, `desc`, cond) values (81, 'all-intro-nightcore', 'Sweet Rave Party', 'Founded in the fine tradition of changing things that were just fine as they were.', 'score.mods & 512'); -insert into achievements (id, file, name, `desc`, cond) values (82, 'all-intro-halftime', 'Slowboat', 'You got there. Eventually.', 'score.mods & 256'); -insert into achievements (id, file, name, `desc`, cond) values (83, 'all-intro-spunout', 'Burned Out', 'One cannot always spin to win.', 'score.mods & 4096'); +create table achievements +( + id int auto_increment + primary key, + file varchar(128) not null, + name varchar(128) charset utf8 not null, + `desc` varchar(256) charset utf8 not null, + cond varchar(64) not null, + constraint achievements_desc_uindex + unique (`desc`), + constraint achievements_file_uindex + unique (file), + constraint achievements_name_uindex + unique (name) +); + +create table channels +( + id int auto_increment + primary key, + name varchar(32) not null, + topic varchar(256) not null, + read_priv int default 1 not null, + write_priv int default 2 not null, + auto_join tinyint(1) default 0 not null, + constraint channels_name_uindex + unique (name) +); +create index channels_auto_join_index + on channels (auto_join); + +create table clans +( + id int auto_increment + primary key, + name varchar(16) charset utf8 not null, + tag varchar(6) charset utf8 not null, + owner int not null, + created_at datetime not null, + constraint clans_name_uindex + unique (name), + constraint clans_owner_uindex + unique (owner), + constraint clans_tag_uindex + unique (tag) +); + +create table client_hashes +( + userid int not null, + osupath char(32) not null, + adapters char(32) not null, + uninstall_id char(32) not null, + disk_serial char(32) not null, + latest_time datetime not null, + occurrences int default 0 not null, + primary key (userid, osupath, adapters, uninstall_id, disk_serial) +); + +create table comments +( + id int auto_increment + primary key, + target_id int not null comment 'replay, map, or set id', + target_type enum('replay', 'map', 'song') not null, + userid int not null, + time int not null, + comment varchar(80) charset utf8 not null, + colour char(6) null comment 'rgb hex string' +); + +create table favourites +( + userid int not null, + setid int not null, + created_at int default 0 not null, + primary key (userid, setid) +); + +create table ingame_logins +( + id int auto_increment + primary key, + userid int not null, + ip varchar(45) not null comment 'maxlen for ipv6', + osu_ver date not null, + osu_stream varchar(11) not null, + datetime datetime not null +); + +create table relationships +( + user1 int not null, + user2 int not null, + type enum('friend', 'block') not null, + primary key (user1, user2) +); + +create table logs +( + id int auto_increment + primary key, + `from` int not null comment 'both from and to are playerids', + `to` int not null, + `action` varchar(32) not null, + msg varchar(2048) charset utf8 null, + time datetime not null on update CURRENT_TIMESTAMP +); + +create table mail +( + id int auto_increment + primary key, + from_id int not null, + to_id int not null, + msg varchar(2048) charset utf8 not null, + time int null, + `read` tinyint(1) default 0 not null +); + +create table maps +( + server enum('osu!', 'private') default 'osu!' not null, + id int not null, + set_id int not null, + status int not null, + md5 char(32) not null, + artist varchar(128) charset utf8 not null, + title varchar(128) charset utf8 not null, + version varchar(128) charset utf8 not null, + creator varchar(19) charset utf8 not null, + filename varchar(256) charset utf8 not null, + last_update datetime not null, + total_length int not null, + max_combo int not null, + frozen tinyint(1) default 0 not null, + plays int default 0 not null, + passes int default 0 not null, + mode tinyint(1) default 0 not null, + bpm float(12,2) default 0.00 not null, + cs float(4,2) default 0.00 not null, + ar float(4,2) default 0.00 not null, + od float(4,2) default 0.00 not null, + hp float(4,2) default 0.00 not null, + diff float(6,3) default 0.000 not null, + primary key (server, id), + constraint maps_id_uindex + unique (id), + constraint maps_md5_uindex + unique (md5) +); +create index maps_set_id_index + on maps (set_id); +create index maps_status_index + on maps (status); +create index maps_filename_index + on maps (filename); +create index maps_plays_index + on maps (plays); +create index maps_mode_index + on maps (mode); +create index maps_frozen_index + on maps (frozen); + +create table mapsets +( + server enum('osu!', 'private') default 'osu!' not null, + id int not null, + last_osuapi_check datetime default CURRENT_TIMESTAMP not null, + primary key (server, id), + constraint nmapsets_id_uindex + unique (id) +); + +create table map_requests +( + id int auto_increment + primary key, + map_id int not null, + player_id int not null, + datetime datetime not null, + active tinyint(1) not null +); + +create table performance_reports +( + scoreid bigint(20) unsigned not null, + mod_mode enum('vanilla', 'relax', 'autopilot') default 'vanilla' not null, + os varchar(64) not null, + fullscreen tinyint(1) not null, + fps_cap varchar(16) not null, + compatibility tinyint(1) not null, + version varchar(16) not null, + start_time int not null, + end_time int not null, + frame_count int not null, + spike_frames int not null, + aim_rate int not null, + completion tinyint(1) not null, + identifier varchar(128) null comment 'really don''t know much about this yet', + average_frametime int not null, + primary key (scoreid, mod_mode) +); + +create table ratings +( + userid int not null, + map_md5 char(32) not null, + rating tinyint(2) not null, + primary key (userid, map_md5) +); + +create table scores +( + id bigint unsigned auto_increment + primary key, + map_md5 char(32) not null, + score int not null, + pp float(7,3) not null, + acc float(6,3) not null, + max_combo int not null, + mods int not null, + n300 int not null, + n100 int not null, + n50 int not null, + nmiss int not null, + ngeki int not null, + nkatu int not null, + grade varchar(2) default 'N' not null, + status tinyint not null, + mode tinyint not null, + play_time datetime not null, + time_elapsed int not null, + client_flags int not null, + userid int not null, + perfect tinyint(1) not null, + online_checksum char(32) not null +); +create index scores_map_md5_index + on scores (map_md5); +create index scores_score_index + on scores (score); +create index scores_pp_index + on scores (pp); +create index scores_mods_index + on scores (mods); +create index scores_status_index + on scores (status); +create index scores_mode_index + on scores (mode); +create index scores_play_time_index + on scores (play_time); +create index scores_userid_index + on scores (userid); +create index scores_online_checksum_index + on scores (online_checksum); +create index scores_fetch_leaderboard_generic_index + on scores (map_md5, status, mode); + +create table startups +( + id int auto_increment + primary key, + ver_major tinyint not null, + ver_minor tinyint not null, + ver_micro tinyint not null, + datetime datetime not null +); + +create table stats +( + id int auto_increment, + mode tinyint(1) not null, + tscore bigint unsigned default 0 not null, + rscore bigint unsigned default 0 not null, + pp int unsigned default 0 not null, + plays int unsigned default 0 not null, + playtime int unsigned default 0 not null, + acc float(6,3) default 0.000 not null, + max_combo int unsigned default 0 not null, + total_hits int unsigned default 0 not null, + replay_views int unsigned default 0 not null, + xh_count int unsigned default 0 not null, + x_count int unsigned default 0 not null, + sh_count int unsigned default 0 not null, + s_count int unsigned default 0 not null, + a_count int unsigned default 0 not null, + primary key (id, mode) +); +create index stats_mode_index + on stats (mode); +create index stats_pp_index + on stats (pp); +create index stats_tscore_index + on stats (tscore); +create index stats_rscore_index + on stats (rscore); + +create table tourney_pool_maps +( + map_id int not null, + pool_id int not null, + mods int not null, + slot tinyint not null, + primary key (map_id, pool_id) +); +create index tourney_pool_maps_mods_slot_index + on tourney_pool_maps (mods, slot); +create index tourney_pool_maps_tourney_pools_id_fk + on tourney_pool_maps (pool_id); + +create table tourney_pools +( + id int auto_increment + primary key, + name varchar(16) not null, + created_at datetime not null, + created_by int not null +); + +create index tourney_pools_users_id_fk + on tourney_pools (created_by); + +create table user_achievements +( + userid int not null, + achid int not null, + primary key (userid, achid) +); +create index user_achievements_achid_index + on user_achievements (achid); +create index user_achievements_userid_index + on user_achievements (userid); + +create table users +( + id int auto_increment + primary key, + name varchar(32) charset utf8 not null, + safe_name varchar(32) charset utf8 not null, + email varchar(254) not null, + priv int default 1 not null, + pw_bcrypt char(60) not null, + country char(2) default 'xx' not null, + silence_end int default 0 not null, + donor_end int default 0 not null, + creation_time int default 0 not null, + latest_activity int default 0 not null, + clan_id int default 0 not null, + clan_priv tinyint(1) default 0 not null, + preferred_mode int default 0 not null, + play_style int default 0 not null, + custom_badge_name varchar(16) charset utf8 null, + custom_badge_icon varchar(64) null, + userpage_content varchar(2048) charset utf8 null, + api_key char(36) null, + constraint users_api_key_uindex + unique (api_key), + constraint users_email_uindex + unique (email), + constraint users_name_uindex + unique (name), + constraint users_safe_name_uindex + unique (safe_name) +); +create index users_priv_index + on users (priv); +create index users_clan_id_index + on users (clan_id); +create index users_clan_priv_index + on users (clan_priv); +create index users_country_index + on users (country); + +insert into users (id, name, safe_name, priv, country, silence_end, email, pw_bcrypt, creation_time, latest_activity) +values (1, 'BanchoBot', 'banchobot', 1, 'ca', 0, 'bot@akatsuki.pw', + '_______________________my_cool_bcrypt_______________________', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()); + +INSERT INTO stats (id, mode) VALUES (1, 0); # vn!std +INSERT INTO stats (id, mode) VALUES (1, 1); # vn!taiko +INSERT INTO stats (id, mode) VALUES (1, 2); # vn!catch +INSERT INTO stats (id, mode) VALUES (1, 3); # vn!mania +INSERT INTO stats (id, mode) VALUES (1, 4); # rx!std +INSERT INTO stats (id, mode) VALUES (1, 5); # rx!taiko +INSERT INTO stats (id, mode) VALUES (1, 6); # rx!catch +INSERT INTO stats (id, mode) VALUES (1, 8); # ap!std + + +# userid 2 is reserved for ppy in osu!, and the +# client will not allow users to pm this id. +# If you want this, simply remove these two lines. +alter table users auto_increment = 3; +alter table stats auto_increment = 3; + +insert into channels (name, topic, read_priv, write_priv, auto_join) +values ('#osu', 'General discussion.', 1, 2, true), + ('#announce', 'Exemplary performance and public announcements.', 1, 24576, true), + ('#lobby', 'Multiplayer lobby discussion room.', 1, 2, false), + ('#supporter', 'General discussion for supporters.', 48, 48, false), + ('#staff', 'General discussion for staff members.', 28672, 28672, true), + ('#admin', 'General discussion for administrators.', 24576, 24576, true), + ('#dev', 'General discussion for developers.', 16384, 16384, true); + +insert into achievements (id, file, name, `desc`, cond) values (1, 'osu-skill-pass-1', 'Rising Star', 'Can''t go forward without the first steps.', '(score.mods & 1 == 0) and 1 <= score.sr < 2 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (2, 'osu-skill-pass-2', 'Constellation Prize', 'Definitely not a consolation prize. Now things start getting hard!', '(score.mods & 1 == 0) and 2 <= score.sr < 3 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (3, 'osu-skill-pass-3', 'Building Confidence', 'Oh, you''ve SO got this.', '(score.mods & 1 == 0) and 3 <= score.sr < 4 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (4, 'osu-skill-pass-4', 'Insanity Approaches', 'You''re not twitching, you''re just ready.', '(score.mods & 1 == 0) and 4 <= score.sr < 5 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (5, 'osu-skill-pass-5', 'These Clarion Skies', 'Everything seems so clear now.', '(score.mods & 1 == 0) and 5 <= score.sr < 6 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (6, 'osu-skill-pass-6', 'Above and Beyond', 'A cut above the rest.', '(score.mods & 1 == 0) and 6 <= score.sr < 7 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (7, 'osu-skill-pass-7', 'Supremacy', 'All marvel before your prowess.', '(score.mods & 1 == 0) and 7 <= score.sr < 8 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (8, 'osu-skill-pass-8', 'Absolution', 'My god, you''re full of stars!', '(score.mods & 1 == 0) and 8 <= score.sr < 9 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (9, 'osu-skill-pass-9', 'Event Horizon', 'No force dares to pull you under.', '(score.mods & 1 == 0) and 9 <= score.sr < 10 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (10, 'osu-skill-pass-10', 'Phantasm', 'Fevered is your passion, extraordinary is your skill.', '(score.mods & 1 == 0) and 10 <= score.sr < 11 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (11, 'osu-skill-fc-1', 'Totality', 'All the notes. Every single one.', 'score.perfect and 1 <= score.sr < 2 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (12, 'osu-skill-fc-2', 'Business As Usual', 'Two to go, please.', 'score.perfect and 2 <= score.sr < 3 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (13, 'osu-skill-fc-3', 'Building Steam', 'Hey, this isn''t so bad.', 'score.perfect and 3 <= score.sr < 4 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (14, 'osu-skill-fc-4', 'Moving Forward', 'Bet you feel good about that.', 'score.perfect and 4 <= score.sr < 5 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (15, 'osu-skill-fc-5', 'Paradigm Shift', 'Surprisingly difficult.', 'score.perfect and 5 <= score.sr < 6 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (16, 'osu-skill-fc-6', 'Anguish Quelled', 'Don''t choke.', 'score.perfect and 6 <= score.sr < 7 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (17, 'osu-skill-fc-7', 'Never Give Up', 'Excellence is its own reward.', 'score.perfect and 7 <= score.sr < 8 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (18, 'osu-skill-fc-8', 'Aberration', 'They said it couldn''t be done. They were wrong.', 'score.perfect and 8 <= score.sr < 9 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (19, 'osu-skill-fc-9', 'Chosen', 'Reign among the Prometheans, where you belong.', 'score.perfect and 9 <= score.sr < 10 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (20, 'osu-skill-fc-10', 'Unfathomable', 'You have no equal.', 'score.perfect and 10 <= score.sr < 11 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (21, 'osu-combo-500', '500 Combo', '500 big ones! You''re moving up in the world!', '500 <= score.max_combo < 750 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (22, 'osu-combo-750', '750 Combo', '750 notes back to back? Woah.', '750 <= score.max_combo < 1000 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (23, 'osu-combo-1000', '1000 Combo', 'A thousand reasons why you rock at this game.', '1000 <= score.max_combo < 2000 and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (24, 'osu-combo-2000', '2000 Combo', 'Nothing can stop you now.', '2000 <= score.max_combo and mode_vn == 0'); +insert into achievements (id, file, name, `desc`, cond) values (25, 'taiko-skill-pass-1', 'My First Don', 'Marching to the beat of your own drum. Literally.', '(score.mods & 1 == 0) and 1 <= score.sr < 2 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (26, 'taiko-skill-pass-2', 'Katsu Katsu Katsu', 'Hora! Izuko!', '(score.mods & 1 == 0) and 2 <= score.sr < 3 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (27, 'taiko-skill-pass-3', 'Not Even Trying', 'Muzukashii? Not even.', '(score.mods & 1 == 0) and 3 <= score.sr < 4 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (28, 'taiko-skill-pass-4', 'Face Your Demons', 'The first trials are now behind you, but are you a match for the Oni?', '(score.mods & 1 == 0) and 4 <= score.sr < 5 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (29, 'taiko-skill-pass-5', 'The Demon Within', 'No rest for the wicked.', '(score.mods & 1 == 0) and 5 <= score.sr < 6 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (30, 'taiko-skill-pass-6', 'Drumbreaker', 'Too strong.', '(score.mods & 1 == 0) and 6 <= score.sr < 7 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (31, 'taiko-skill-pass-7', 'The Godfather', 'You are the Don of Dons.', '(score.mods & 1 == 0) and 7 <= score.sr < 8 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (32, 'taiko-skill-pass-8', 'Rhythm Incarnate', 'Feel the beat. Become the beat.', '(score.mods & 1 == 0) and 8 <= score.sr < 9 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (33, 'taiko-skill-fc-1', 'Keeping Time', 'Don, then katsu. Don, then katsu..', 'score.perfect and 1 <= score.sr < 2 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (34, 'taiko-skill-fc-2', 'To Your Own Beat', 'Straight and steady.', 'score.perfect and 2 <= score.sr < 3 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (35, 'taiko-skill-fc-3', 'Big Drums', 'Bigger scores to match.', 'score.perfect and 3 <= score.sr < 4 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (36, 'taiko-skill-fc-4', 'Adversity Overcome', 'Difficult? Not for you.', 'score.perfect and 4 <= score.sr < 5 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (37, 'taiko-skill-fc-5', 'Demonslayer', 'An Oni felled forevermore.', 'score.perfect and 5 <= score.sr < 6 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (38, 'taiko-skill-fc-6', 'Rhythm''s Call', 'Heralding true skill.', 'score.perfect and 6 <= score.sr < 7 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (39, 'taiko-skill-fc-7', 'Time Everlasting', 'Not a single beat escapes you.', 'score.perfect and 7 <= score.sr < 8 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (40, 'taiko-skill-fc-8', 'The Drummer''s Throne', 'Percussive brilliance befitting royalty alone.', 'score.perfect and 8 <= score.sr < 9 and mode_vn == 1'); +insert into achievements (id, file, name, `desc`, cond) values (41, 'fruits-skill-pass-1', 'A Slice Of Life', 'Hey, this fruit catching business isn''t bad.', '(score.mods & 1 == 0) and 1 <= score.sr < 2 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (42, 'fruits-skill-pass-2', 'Dashing Ever Forward', 'Fast is how you do it.', '(score.mods & 1 == 0) and 2 <= score.sr < 3 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (43, 'fruits-skill-pass-3', 'Zesty Disposition', 'No scurvy for you, not with that much fruit.', '(score.mods & 1 == 0) and 3 <= score.sr < 4 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (44, 'fruits-skill-pass-4', 'Hyperdash ON!', 'Time and distance is no obstacle to you.', '(score.mods & 1 == 0) and 4 <= score.sr < 5 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (45, 'fruits-skill-pass-5', 'It''s Raining Fruit', 'And you can catch them all.', '(score.mods & 1 == 0) and 5 <= score.sr < 6 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (46, 'fruits-skill-pass-6', 'Fruit Ninja', 'Legendary techniques.', '(score.mods & 1 == 0) and 6 <= score.sr < 7 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (47, 'fruits-skill-pass-7', 'Dreamcatcher', 'No fruit, only dreams now.', '(score.mods & 1 == 0) and 7 <= score.sr < 8 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (48, 'fruits-skill-pass-8', 'Lord of the Catch', 'Your kingdom kneels before you.', '(score.mods & 1 == 0) and 8 <= score.sr < 9 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (49, 'fruits-skill-fc-1', 'Sweet And Sour', 'Apples and oranges, literally.', 'score.perfect and 1 <= score.sr < 2 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (50, 'fruits-skill-fc-2', 'Reaching The Core', 'The seeds of future success.', 'score.perfect and 2 <= score.sr < 3 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (51, 'fruits-skill-fc-3', 'Clean Platter', 'Clean only of failure. It is completely full, otherwise.', 'score.perfect and 3 <= score.sr < 4 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (52, 'fruits-skill-fc-4', 'Between The Rain', 'No umbrella needed.', 'score.perfect and 4 <= score.sr < 5 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (53, 'fruits-skill-fc-5', 'Addicted', 'That was an overdose?', 'score.perfect and 5 <= score.sr < 6 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (54, 'fruits-skill-fc-6', 'Quickening', 'A dash above normal limits.', 'score.perfect and 6 <= score.sr < 7 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (55, 'fruits-skill-fc-7', 'Supersonic', 'Faster than is reasonably necessary.', 'score.perfect and 7 <= score.sr < 8 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (56, 'fruits-skill-fc-8', 'Dashing Scarlet', 'Speed beyond mortal reckoning.', 'score.perfect and 8 <= score.sr < 9 and mode_vn == 2'); +insert into achievements (id, file, name, `desc`, cond) values (57, 'mania-skill-pass-1', 'First Steps', 'It isn''t 9-to-5, but 1-to-9. Keys, that is.', '(score.mods & 1 == 0) and 1 <= score.sr < 2 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (58, 'mania-skill-pass-2', 'No Normal Player', 'Not anymore, at least.', '(score.mods & 1 == 0) and 2 <= score.sr < 3 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (59, 'mania-skill-pass-3', 'Impulse Drive', 'Not quite hyperspeed, but getting close.', '(score.mods & 1 == 0) and 3 <= score.sr < 4 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (60, 'mania-skill-pass-4', 'Hyperspeed', 'Woah.', '(score.mods & 1 == 0) and 4 <= score.sr < 5 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (61, 'mania-skill-pass-5', 'Ever Onwards', 'Another challenge is just around the corner.', '(score.mods & 1 == 0) and 5 <= score.sr < 6 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (62, 'mania-skill-pass-6', 'Another Surpassed', 'Is there no limit to your skills?', '(score.mods & 1 == 0) and 6 <= score.sr < 7 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (63, 'mania-skill-pass-7', 'Extra Credit', 'See me after class.', '(score.mods & 1 == 0) and 7 <= score.sr < 8 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (64, 'mania-skill-pass-8', 'Maniac', 'There''s just no stopping you.', '(score.mods & 1 == 0) and 8 <= score.sr < 9 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (65, 'mania-skill-fc-1', 'Keystruck', 'The beginning of a new story', 'score.perfect and 1 <= score.sr < 2 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (66, 'mania-skill-fc-2', 'Keying In', 'Finding your groove.', 'score.perfect and 2 <= score.sr < 3 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (67, 'mania-skill-fc-3', 'Hyperflow', 'You can *feel* the rhythm.', 'score.perfect and 3 <= score.sr < 4 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (68, 'mania-skill-fc-4', 'Breakthrough', 'Many skills mastered, rolled into one.', 'score.perfect and 4 <= score.sr < 5 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (69, 'mania-skill-fc-5', 'Everything Extra', 'Giving your all is giving everything you have.', 'score.perfect and 5 <= score.sr < 6 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (70, 'mania-skill-fc-6', 'Level Breaker', 'Finesse beyond reason', 'score.perfect and 6 <= score.sr < 7 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (71, 'mania-skill-fc-7', 'Step Up', 'A precipice rarely seen.', 'score.perfect and 7 <= score.sr < 8 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (72, 'mania-skill-fc-8', 'Behind The Veil', 'Supernatural!', 'score.perfect and 8 <= score.sr < 9 and mode_vn == 3'); +insert into achievements (id, file, name, `desc`, cond) values (73, 'all-intro-suddendeath', 'Finality', 'High stakes, no regrets.', 'score.mods == 32'); +insert into achievements (id, file, name, `desc`, cond) values (74, 'all-intro-hidden', 'Blindsight', 'I can see just perfectly', 'score.mods & 8'); +insert into achievements (id, file, name, `desc`, cond) values (75, 'all-intro-perfect', 'Perfectionist', 'Accept nothing but the best.', 'score.mods & 16384'); +insert into achievements (id, file, name, `desc`, cond) values (76, 'all-intro-hardrock', 'Rock Around The Clock', "You can\'t stop the rock.", 'score.mods & 16'); +insert into achievements (id, file, name, `desc`, cond) values (77, 'all-intro-doubletime', 'Time And A Half', "Having a right ol\' time. One and a half of them, almost.", 'score.mods & 64'); +insert into achievements (id, file, name, `desc`, cond) values (78, 'all-intro-flashlight', 'Are You Afraid Of The Dark?', "Harder than it looks, probably because it\'s hard to look.", 'score.mods & 1024'); +insert into achievements (id, file, name, `desc`, cond) values (79, 'all-intro-easy', 'Dial It Right Back', 'Sometimes you just want to take it easy.', 'score.mods & 2'); +insert into achievements (id, file, name, `desc`, cond) values (80, 'all-intro-nofail', 'Risk Averse', 'Safety nets are fun!', 'score.mods & 1'); +insert into achievements (id, file, name, `desc`, cond) values (81, 'all-intro-nightcore', 'Sweet Rave Party', 'Founded in the fine tradition of changing things that were just fine as they were.', 'score.mods & 512'); +insert into achievements (id, file, name, `desc`, cond) values (82, 'all-intro-halftime', 'Slowboat', 'You got there. Eventually.', 'score.mods & 256'); +insert into achievements (id, file, name, `desc`, cond) values (83, 'all-intro-spunout', 'Burned Out', 'One cannot always spin to win.', 'score.mods & 4096'); diff --git a/migrations/custom_beatmaps.sql b/migrations/custom_beatmaps.sql index 9148845..b7cd122 100644 --- a/migrations/custom_beatmaps.sql +++ b/migrations/custom_beatmaps.sql @@ -1,209 +1,209 @@ --- 自定义谱面系统迁移 --- 创建自定义谱面表,与官方谱面不冲突 - --- 自定义谱面集表 -CREATE TABLE custom_mapsets ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - creator_id INT NOT NULL, - title VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - artist VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - source VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', - tags TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - description TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - status ENUM('pending', 'approved', 'rejected', 'loved') DEFAULT 'pending', - upload_date DATETIME DEFAULT CURRENT_TIMESTAMP, - last_update DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - osz_filename VARCHAR(255) NOT NULL, - osz_hash CHAR(32) NOT NULL, - download_count INT DEFAULT 0, - favourite_count INT DEFAULT 0, - UNIQUE KEY idx_custom_mapsets_id (id), - KEY idx_custom_mapsets_creator (creator_id), - KEY idx_custom_mapsets_status (status), - KEY idx_custom_mapsets_upload_date (upload_date), - UNIQUE KEY idx_custom_mapsets_osz_hash (osz_hash) -); - --- 自定义谱面难度表 -CREATE TABLE custom_maps ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - mapset_id BIGINT NOT NULL, - md5 CHAR(32) NOT NULL, - difficulty_name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - filename VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - mode TINYINT DEFAULT 0 NOT NULL COMMENT '0=osu!, 1=taiko, 2=catch, 3=mania', - status ENUM('pending', 'approved', 'rejected', 'loved') DEFAULT 'pending', - - -- osu!文件基本信息 - audio_filename VARCHAR(255) DEFAULT '', - audio_lead_in INT DEFAULT 0, - preview_time INT DEFAULT -1, - countdown TINYINT DEFAULT 1, - sample_set VARCHAR(16) DEFAULT 'Normal', - stack_leniency DECIMAL(3,2) DEFAULT 0.70, - letterbox_in_breaks BOOLEAN DEFAULT FALSE, - story_fire_in_front BOOLEAN DEFAULT TRUE, - use_skin_sprites BOOLEAN DEFAULT FALSE, - always_show_playfield BOOLEAN DEFAULT FALSE, - overlay_position VARCHAR(16) DEFAULT 'NoChange', - skin_preference VARCHAR(255) DEFAULT '', - epilepsy_warning BOOLEAN DEFAULT FALSE, - countdown_offset INT DEFAULT 0, - special_style BOOLEAN DEFAULT FALSE, - widescreen_storyboard BOOLEAN DEFAULT FALSE, - samples_match_playback_rate BOOLEAN DEFAULT FALSE, - - -- 编辑器信息 - distance_spacing DECIMAL(6,3) DEFAULT 1.000, - beat_divisor TINYINT DEFAULT 4, - grid_size TINYINT DEFAULT 4, - timeline_zoom DECIMAL(6,3) DEFAULT 1.000, - - -- 谱面元数据 - title_unicode VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', - artist_unicode VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', - creator VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - version VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - source VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', - tags TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - beatmap_id BIGINT DEFAULT 0, - beatmapset_id BIGINT DEFAULT 0, - - -- 难度设定 - hp_drain_rate DECIMAL(3,1) DEFAULT 5.0, - circle_size DECIMAL(3,1) DEFAULT 5.0, - overall_difficulty DECIMAL(3,1) DEFAULT 5.0, - approach_rate DECIMAL(3,1) DEFAULT 5.0, - slider_multiplier DECIMAL(6,3) DEFAULT 1.400, - slider_tick_rate DECIMAL(3,1) DEFAULT 1.0, - - -- 计算得出的信息 - total_length INT DEFAULT 0 COMMENT '总长度(秒)', - hit_length INT DEFAULT 0 COMMENT '击打长度(秒)', - max_combo INT DEFAULT 0, - bpm DECIMAL(8,3) DEFAULT 0.000, - star_rating DECIMAL(6,3) DEFAULT 0.000, - aim_difficulty DECIMAL(6,3) DEFAULT 0.000, - speed_difficulty DECIMAL(6,3) DEFAULT 0.000, - - -- 统计信息 - plays INT DEFAULT 0, - passes INT DEFAULT 0, - - -- 时间戳 - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - - UNIQUE KEY idx_custom_maps_id (id), - UNIQUE KEY idx_custom_maps_md5 (md5), - KEY idx_custom_maps_mapset (mapset_id), - KEY idx_custom_maps_mode (mode), - KEY idx_custom_maps_status (status), - KEY idx_custom_maps_creator (creator), - KEY idx_custom_maps_star_rating (star_rating), - KEY idx_custom_maps_plays (plays), - - FOREIGN KEY (mapset_id) REFERENCES custom_mapsets(id) ON DELETE CASCADE -); - --- 自定义谱面书签表 -CREATE TABLE custom_map_bookmarks ( - user_id INT NOT NULL, - mapset_id BIGINT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (user_id, mapset_id), - FOREIGN KEY (mapset_id) REFERENCES custom_mapsets(id) ON DELETE CASCADE -); - --- 自定义谱面评分表 -CREATE TABLE custom_map_ratings ( - user_id INT NOT NULL, - map_id BIGINT NOT NULL, - rating TINYINT NOT NULL CHECK (rating >= 1 AND rating <= 10), - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (user_id, map_id), - FOREIGN KEY (map_id) REFERENCES custom_maps(id) ON DELETE CASCADE -); - --- 自定义谱面成绩表 (继承原scores表结构) -CREATE TABLE custom_scores ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - map_id BIGINT NOT NULL, - map_md5 CHAR(32) NOT NULL, - user_id INT NOT NULL, - score INT NOT NULL, - pp FLOAT(7,3) NOT NULL, - acc FLOAT(6,3) NOT NULL, - max_combo INT NOT NULL, - mods INT NOT NULL, - n300 INT NOT NULL, - n100 INT NOT NULL, - n50 INT NOT NULL, - nmiss INT NOT NULL, - ngeki INT NOT NULL, - nkatu INT NOT NULL, - grade VARCHAR(2) DEFAULT 'N' NOT NULL, - status TINYINT NOT NULL COMMENT '0=failed, 1=submitted, 2=best', - mode TINYINT NOT NULL, - play_time DATETIME NOT NULL, - time_elapsed INT NOT NULL, - client_flags INT NOT NULL, - perfect BOOLEAN NOT NULL, - online_checksum CHAR(32) NOT NULL, - - KEY idx_custom_scores_map_id (map_id), - KEY idx_custom_scores_map_md5 (map_md5), - KEY idx_custom_scores_user_id (user_id), - KEY idx_custom_scores_score (score), - KEY idx_custom_scores_pp (pp), - KEY idx_custom_scores_mods (mods), - KEY idx_custom_scores_status (status), - KEY idx_custom_scores_mode (mode), - KEY idx_custom_scores_play_time (play_time), - KEY idx_custom_scores_online_checksum (online_checksum), - KEY idx_custom_scores_leaderboard (map_md5, status, mode), - - FOREIGN KEY (map_id) REFERENCES custom_maps(id) ON DELETE CASCADE -); - --- 自定义谱面文件存储表 (用于存储.osu文件内容等) -CREATE TABLE custom_map_files ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - map_id BIGINT NOT NULL, - file_type ENUM('osu', 'audio', 'image', 'video', 'storyboard') NOT NULL, - filename VARCHAR(255) NOT NULL, - file_hash CHAR(32) NOT NULL, - file_size INT NOT NULL, - mime_type VARCHAR(100) DEFAULT '', - storage_path VARCHAR(500) NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - - UNIQUE KEY idx_custom_map_files_id (id), - KEY idx_custom_map_files_map_id (map_id), - KEY idx_custom_map_files_type (file_type), - KEY idx_custom_map_files_hash (file_hash), - - FOREIGN KEY (map_id) REFERENCES custom_maps(id) ON DELETE CASCADE -); - --- 为自定义谱面创建专门的ID生成器,避免与官方ID冲突 --- 自定义谱面ID从1000000开始 -ALTER TABLE custom_mapsets AUTO_INCREMENT = 3000000; -ALTER TABLE custom_maps AUTO_INCREMENT = 3000000; - --- 创建触发器来同步mapset信息到maps表 -DELIMITER $$ - -CREATE TRIGGER update_custom_mapset_on_map_change -AFTER UPDATE ON custom_maps -FOR EACH ROW -BEGIN - IF NEW.status != OLD.status THEN - UPDATE custom_mapsets - SET last_update = CURRENT_TIMESTAMP - WHERE id = NEW.mapset_id; - END IF; -END$$ - -DELIMITER ; +-- 自定义谱面系统迁移 +-- 创建自定义谱面表,与官方谱面不冲突 + +-- 自定义谱面集表 +CREATE TABLE custom_mapsets ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + creator_id INT NOT NULL, + title VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + artist VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + source VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + tags TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + description TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + status ENUM('pending', 'approved', 'rejected', 'loved') DEFAULT 'pending', + upload_date DATETIME DEFAULT CURRENT_TIMESTAMP, + last_update DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + osz_filename VARCHAR(255) NOT NULL, + osz_hash CHAR(32) NOT NULL, + download_count INT DEFAULT 0, + favourite_count INT DEFAULT 0, + UNIQUE KEY idx_custom_mapsets_id (id), + KEY idx_custom_mapsets_creator (creator_id), + KEY idx_custom_mapsets_status (status), + KEY idx_custom_mapsets_upload_date (upload_date), + UNIQUE KEY idx_custom_mapsets_osz_hash (osz_hash) +); + +-- 自定义谱面难度表 +CREATE TABLE custom_maps ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + mapset_id BIGINT NOT NULL, + md5 CHAR(32) NOT NULL, + difficulty_name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + filename VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + mode TINYINT DEFAULT 0 NOT NULL COMMENT '0=osu!, 1=taiko, 2=catch, 3=mania', + status ENUM('pending', 'approved', 'rejected', 'loved') DEFAULT 'pending', + + -- osu!文件基本信息 + audio_filename VARCHAR(255) DEFAULT '', + audio_lead_in INT DEFAULT 0, + preview_time INT DEFAULT -1, + countdown TINYINT DEFAULT 1, + sample_set VARCHAR(16) DEFAULT 'Normal', + stack_leniency DECIMAL(3,2) DEFAULT 0.70, + letterbox_in_breaks BOOLEAN DEFAULT FALSE, + story_fire_in_front BOOLEAN DEFAULT TRUE, + use_skin_sprites BOOLEAN DEFAULT FALSE, + always_show_playfield BOOLEAN DEFAULT FALSE, + overlay_position VARCHAR(16) DEFAULT 'NoChange', + skin_preference VARCHAR(255) DEFAULT '', + epilepsy_warning BOOLEAN DEFAULT FALSE, + countdown_offset INT DEFAULT 0, + special_style BOOLEAN DEFAULT FALSE, + widescreen_storyboard BOOLEAN DEFAULT FALSE, + samples_match_playback_rate BOOLEAN DEFAULT FALSE, + + -- 编辑器信息 + distance_spacing DECIMAL(6,3) DEFAULT 1.000, + beat_divisor TINYINT DEFAULT 4, + grid_size TINYINT DEFAULT 4, + timeline_zoom DECIMAL(6,3) DEFAULT 1.000, + + -- 谱面元数据 + title_unicode VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + artist_unicode VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + creator VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + version VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + source VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + tags TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + beatmap_id BIGINT DEFAULT 0, + beatmapset_id BIGINT DEFAULT 0, + + -- 难度设定 + hp_drain_rate DECIMAL(3,1) DEFAULT 5.0, + circle_size DECIMAL(3,1) DEFAULT 5.0, + overall_difficulty DECIMAL(3,1) DEFAULT 5.0, + approach_rate DECIMAL(3,1) DEFAULT 5.0, + slider_multiplier DECIMAL(6,3) DEFAULT 1.400, + slider_tick_rate DECIMAL(3,1) DEFAULT 1.0, + + -- 计算得出的信息 + total_length INT DEFAULT 0 COMMENT '总长度(秒)', + hit_length INT DEFAULT 0 COMMENT '击打长度(秒)', + max_combo INT DEFAULT 0, + bpm DECIMAL(8,3) DEFAULT 0.000, + star_rating DECIMAL(6,3) DEFAULT 0.000, + aim_difficulty DECIMAL(6,3) DEFAULT 0.000, + speed_difficulty DECIMAL(6,3) DEFAULT 0.000, + + -- 统计信息 + plays INT DEFAULT 0, + passes INT DEFAULT 0, + + -- 时间戳 + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + UNIQUE KEY idx_custom_maps_id (id), + UNIQUE KEY idx_custom_maps_md5 (md5), + KEY idx_custom_maps_mapset (mapset_id), + KEY idx_custom_maps_mode (mode), + KEY idx_custom_maps_status (status), + KEY idx_custom_maps_creator (creator), + KEY idx_custom_maps_star_rating (star_rating), + KEY idx_custom_maps_plays (plays), + + FOREIGN KEY (mapset_id) REFERENCES custom_mapsets(id) ON DELETE CASCADE +); + +-- 自定义谱面书签表 +CREATE TABLE custom_map_bookmarks ( + user_id INT NOT NULL, + mapset_id BIGINT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (user_id, mapset_id), + FOREIGN KEY (mapset_id) REFERENCES custom_mapsets(id) ON DELETE CASCADE +); + +-- 自定义谱面评分表 +CREATE TABLE custom_map_ratings ( + user_id INT NOT NULL, + map_id BIGINT NOT NULL, + rating TINYINT NOT NULL CHECK (rating >= 1 AND rating <= 10), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (user_id, map_id), + FOREIGN KEY (map_id) REFERENCES custom_maps(id) ON DELETE CASCADE +); + +-- 自定义谱面成绩表 (继承原scores表结构) +CREATE TABLE custom_scores ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + map_id BIGINT NOT NULL, + map_md5 CHAR(32) NOT NULL, + user_id INT NOT NULL, + score INT NOT NULL, + pp FLOAT(7,3) NOT NULL, + acc FLOAT(6,3) NOT NULL, + max_combo INT NOT NULL, + mods INT NOT NULL, + n300 INT NOT NULL, + n100 INT NOT NULL, + n50 INT NOT NULL, + nmiss INT NOT NULL, + ngeki INT NOT NULL, + nkatu INT NOT NULL, + grade VARCHAR(2) DEFAULT 'N' NOT NULL, + status TINYINT NOT NULL COMMENT '0=failed, 1=submitted, 2=best', + mode TINYINT NOT NULL, + play_time DATETIME NOT NULL, + time_elapsed INT NOT NULL, + client_flags INT NOT NULL, + perfect BOOLEAN NOT NULL, + online_checksum CHAR(32) NOT NULL, + + KEY idx_custom_scores_map_id (map_id), + KEY idx_custom_scores_map_md5 (map_md5), + KEY idx_custom_scores_user_id (user_id), + KEY idx_custom_scores_score (score), + KEY idx_custom_scores_pp (pp), + KEY idx_custom_scores_mods (mods), + KEY idx_custom_scores_status (status), + KEY idx_custom_scores_mode (mode), + KEY idx_custom_scores_play_time (play_time), + KEY idx_custom_scores_online_checksum (online_checksum), + KEY idx_custom_scores_leaderboard (map_md5, status, mode), + + FOREIGN KEY (map_id) REFERENCES custom_maps(id) ON DELETE CASCADE +); + +-- 自定义谱面文件存储表 (用于存储.osu文件内容等) +CREATE TABLE custom_map_files ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + map_id BIGINT NOT NULL, + file_type ENUM('osu', 'audio', 'image', 'video', 'storyboard') NOT NULL, + filename VARCHAR(255) NOT NULL, + file_hash CHAR(32) NOT NULL, + file_size INT NOT NULL, + mime_type VARCHAR(100) DEFAULT '', + storage_path VARCHAR(500) NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + UNIQUE KEY idx_custom_map_files_id (id), + KEY idx_custom_map_files_map_id (map_id), + KEY idx_custom_map_files_type (file_type), + KEY idx_custom_map_files_hash (file_hash), + + FOREIGN KEY (map_id) REFERENCES custom_maps(id) ON DELETE CASCADE +); + +-- 为自定义谱面创建专门的ID生成器,避免与官方ID冲突 +-- 自定义谱面ID从1000000开始 +ALTER TABLE custom_mapsets AUTO_INCREMENT = 3000000; +ALTER TABLE custom_maps AUTO_INCREMENT = 3000000; + +-- 创建触发器来同步mapset信息到maps表 +DELIMITER $$ + +CREATE TRIGGER update_custom_mapset_on_map_change +AFTER UPDATE ON custom_maps +FOR EACH ROW +BEGIN + IF NEW.status != OLD.status THEN + UPDATE custom_mapsets + SET last_update = CURRENT_TIMESTAMP + WHERE id = NEW.mapset_id; + END IF; +END$$ + +DELIMITER ; diff --git a/migrations/migrations.sql b/migrations/migrations.sql index 95794b6..8cb522d 100644 --- a/migrations/migrations.sql +++ b/migrations/migrations.sql @@ -1,477 +1,477 @@ -# This file contains any sql updates, along with the -# version they are required from. Touching this without -# at least reading utils/updater.py is certainly a bad idea :) - -# v3.0.6 -alter table users change name_safe safe_name varchar(32) not null; -alter table users drop key users_name_safe_uindex; -alter table users add constraint users_safe_name_uindex unique (safe_name); -alter table users change pw_hash pw_bcrypt char(60) not null; -insert into channels (name, topic, read_priv, write_priv, auto_join) values - ('#supporter', 'General discussion for p2w gamers.', 48, 48, false), - ('#staff', 'General discussion for the cool kids.', 28672, 28672, true), - ('#admin', 'General discussion for the cool.', 24576, 24576, true), - ('#dev', 'General discussion for the.', 16384, 16384, true); - -# v3.0.8 -alter table users modify safe_name varchar(32) charset utf8 not null; -alter table users modify name varchar(32) charset utf8 not null; -alter table mail modify msg varchar(2048) charset utf8 not null; -alter table logs modify msg varchar(2048) charset utf8 not null; -drop table if exists comments; -create table comments -( - id int auto_increment - primary key, - target_id int not null comment 'replay, map, or set id', - target_type enum('replay', 'map', 'song') not null, - userid int not null, - time int not null, - comment varchar(80) charset utf8 not null, - colour char(6) null comment 'rgb hex string' -); - -# v3.0.9 -alter table stats modify tscore_vn_std int unsigned default 0 not null; -alter table stats modify tscore_vn_taiko int unsigned default 0 not null; -alter table stats modify tscore_vn_catch int unsigned default 0 not null; -alter table stats modify tscore_vn_mania int unsigned default 0 not null; -alter table stats modify tscore_rx_std int unsigned default 0 not null; -alter table stats modify tscore_rx_taiko int unsigned default 0 not null; -alter table stats modify tscore_rx_catch int unsigned default 0 not null; -alter table stats modify tscore_ap_std int unsigned default 0 not null; -alter table stats modify rscore_vn_std int unsigned default 0 not null; -alter table stats modify rscore_vn_taiko int unsigned default 0 not null; -alter table stats modify rscore_vn_catch int unsigned default 0 not null; -alter table stats modify rscore_vn_mania int unsigned default 0 not null; -alter table stats modify rscore_rx_std int unsigned default 0 not null; -alter table stats modify rscore_rx_taiko int unsigned default 0 not null; -alter table stats modify rscore_rx_catch int unsigned default 0 not null; -alter table stats modify rscore_ap_std int unsigned default 0 not null; -alter table stats modify pp_vn_std smallint unsigned default 0 not null; -alter table stats modify pp_vn_taiko smallint unsigned default 0 not null; -alter table stats modify pp_vn_catch smallint unsigned default 0 not null; -alter table stats modify pp_vn_mania smallint unsigned default 0 not null; -alter table stats modify pp_rx_std smallint unsigned default 0 not null; -alter table stats modify pp_rx_taiko smallint unsigned default 0 not null; -alter table stats modify pp_rx_catch smallint unsigned default 0 not null; -alter table stats modify pp_ap_std smallint unsigned default 0 not null; -alter table stats modify plays_vn_std int unsigned default 0 not null; -alter table stats modify plays_vn_taiko int unsigned default 0 not null; -alter table stats modify plays_vn_catch int unsigned default 0 not null; -alter table stats modify plays_vn_mania int unsigned default 0 not null; -alter table stats modify plays_rx_std int unsigned default 0 not null; -alter table stats modify plays_rx_taiko int unsigned default 0 not null; -alter table stats modify plays_rx_catch int unsigned default 0 not null; -alter table stats modify plays_ap_std int unsigned default 0 not null; -alter table stats modify playtime_vn_std int unsigned default 0 not null; -alter table stats modify playtime_vn_taiko int unsigned default 0 not null; -alter table stats modify playtime_vn_catch int unsigned default 0 not null; -alter table stats modify playtime_vn_mania int unsigned default 0 not null; -alter table stats modify playtime_rx_std int unsigned default 0 not null; -alter table stats modify playtime_rx_taiko int unsigned default 0 not null; -alter table stats modify playtime_rx_catch int unsigned default 0 not null; -alter table stats modify playtime_ap_std int unsigned default 0 not null; -alter table stats modify maxcombo_vn_std int unsigned default 0 not null; -alter table stats modify maxcombo_vn_taiko int unsigned default 0 not null; -alter table stats modify maxcombo_vn_catch int unsigned default 0 not null; -alter table stats modify maxcombo_vn_mania int unsigned default 0 not null; -alter table stats modify maxcombo_rx_std int unsigned default 0 not null; -alter table stats modify maxcombo_rx_taiko int unsigned default 0 not null; -alter table stats modify maxcombo_rx_catch int unsigned default 0 not null; -alter table stats modify maxcombo_ap_std int unsigned default 0 not null; - -# v3.0.10 -update channels set write_priv = 24576 where name = '#announce'; - -# v3.1.0 -alter table maps modify bpm float(12,2) default 0.00 not null; -alter table stats modify tscore_vn_std bigint unsigned default 0 not null; -alter table stats modify tscore_vn_taiko bigint unsigned default 0 not null; -alter table stats modify tscore_vn_catch bigint unsigned default 0 not null; -alter table stats modify tscore_vn_mania bigint unsigned default 0 not null; -alter table stats modify tscore_rx_std bigint unsigned default 0 not null; -alter table stats modify tscore_rx_taiko bigint unsigned default 0 not null; -alter table stats modify tscore_rx_catch bigint unsigned default 0 not null; -alter table stats modify tscore_ap_std bigint unsigned default 0 not null; -alter table stats modify rscore_vn_std bigint unsigned default 0 not null; -alter table stats modify rscore_vn_taiko bigint unsigned default 0 not null; -alter table stats modify rscore_vn_catch bigint unsigned default 0 not null; -alter table stats modify rscore_vn_mania bigint unsigned default 0 not null; -alter table stats modify rscore_rx_std bigint unsigned default 0 not null; -alter table stats modify rscore_rx_taiko bigint unsigned default 0 not null; -alter table stats modify rscore_rx_catch bigint unsigned default 0 not null; -alter table stats modify rscore_ap_std bigint unsigned default 0 not null; -alter table stats modify pp_vn_std int unsigned default 0 not null; -alter table stats modify pp_vn_taiko int unsigned default 0 not null; -alter table stats modify pp_vn_catch int unsigned default 0 not null; -alter table stats modify pp_vn_mania int unsigned default 0 not null; -alter table stats modify pp_rx_std int unsigned default 0 not null; -alter table stats modify pp_rx_taiko int unsigned default 0 not null; -alter table stats modify pp_rx_catch int unsigned default 0 not null; -alter table stats modify pp_ap_std int unsigned default 0 not null; - -# v3.1.2 -create table clans -( - id int auto_increment - primary key, - name varchar(16) not null, - tag varchar(6) not null, - owner int not null, - created_at datetime not null, - constraint clans_name_uindex - unique (name), - constraint clans_owner_uindex - unique (owner), - constraint clans_tag_uindex - unique (tag) -); -alter table users add clan_id int default 0 not null; -alter table users add clan_rank tinyint(1) default 0 not null; -create table achievements -( - id int auto_increment - primary key, - file varchar(128) not null, - name varchar(128) not null, - `desc` varchar(256) not null, - cond varchar(64) not null, - mode tinyint(1) not null, - constraint achievements_desc_uindex - unique (`desc`), - constraint achievements_file_uindex - unique (file), - constraint achievements_name_uindex - unique (name) -); -create table user_achievements -( - userid int not null, - achid int not null, - primary key (userid, achid) -); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (1, 'osu-skill-pass-1', 'Rising Star', 'Can''t go forward without the first steps.', '(score.mods & 259 == 0) and 2 >= score.sr > 1', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (2, 'osu-skill-pass-2', 'Constellation Prize', 'Definitely not a consolation prize. Now things start getting hard!', '(score.mods & 259 == 0) and 3 >= score.sr > 2', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (3, 'osu-skill-pass-3', 'Building Confidence', 'Oh, you''ve SO got this.', '(score.mods & 259 == 0) and 4 >= score.sr > 3', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (4, 'osu-skill-pass-4', 'Insanity Approaches', 'You''re not twitching, you''re just ready.', '(score.mods & 259 == 0) and 5 >= score.sr > 4', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (5, 'osu-skill-pass-5', 'These Clarion Skies', 'Everything seems so clear now.', '(score.mods & 259 == 0) and 6 >= score.sr > 5', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (6, 'osu-skill-pass-6', 'Above and Beyond', 'A cut above the rest.', '(score.mods & 259 == 0) and 7 >= score.sr > 6', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (7, 'osu-skill-pass-7', 'Supremacy', 'All marvel before your prowess.', '(score.mods & 259 == 0) and 8 >= score.sr > 7', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (8, 'osu-skill-pass-8', 'Absolution', 'My god, you''re full of stars!', '(score.mods & 259 == 0) and 9 >= score.sr > 8', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (9, 'osu-skill-pass-9', 'Event Horizon', 'No force dares to pull you under.', '(score.mods & 259 == 0) and 10 >= score.sr > 9', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (10, 'osu-skill-pass-10', 'Phantasm', 'Fevered is your passion, extraordinary is your skill.', '(score.mods & 259 == 0) and 11 >= score.sr > 10', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (11, 'osu-skill-fc-1', 'Totality', 'All the notes. Every single one.', 'score.perfect and 2 >= score.sr > 1', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (12, 'osu-skill-fc-2', 'Business As Usual', 'Two to go, please.', 'score.perfect and 3 >= score.sr > 2', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (13, 'osu-skill-fc-3', 'Building Steam', 'Hey, this isn''t so bad.', 'score.perfect and 4 >= score.sr > 3', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (14, 'osu-skill-fc-4', 'Moving Forward', 'Bet you feel good about that.', 'score.perfect and 5 >= score.sr > 4', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (15, 'osu-skill-fc-5', 'Paradigm Shift', 'Surprisingly difficult.', 'score.perfect and 6 >= score.sr > 5', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (16, 'osu-skill-fc-6', 'Anguish Quelled', 'Don''t choke.', 'score.perfect and 7 >= score.sr > 6', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (17, 'osu-skill-fc-7', 'Never Give Up', 'Excellence is its own reward.', 'score.perfect and 8 >= score.sr > 7', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (18, 'osu-skill-fc-8', 'Aberration', 'They said it couldn''t be done. They were wrong.', 'score.perfect and 9 >= score.sr > 8', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (19, 'osu-skill-fc-9', 'Chosen', 'Reign among the Prometheans, where you belong.', 'score.perfect and 10 >= score.sr > 9', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (20, 'osu-skill-fc-10', 'Unfathomable', 'You have no equal.', 'score.perfect and 11 >= score.sr > 10', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (21, 'osu-combo-500', '500 Combo', '500 big ones! You''re moving up in the world!', '750 >= score.max_combo > 500', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (22, 'osu-combo-750', '750 Combo', '750 notes back to back? Woah.', '1000 >= score.max_combo > 750', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (23, 'osu-combo-1000', '1000 Combo', 'A thousand reasons why you rock at this game.', '2000 >= score.max_combo > 1000', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (24, 'osu-combo-2000', '2000 Combo', 'Nothing can stop you now.', 'score.max_combo >= 2000', 0); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (25, 'taiko-skill-pass-1', 'My First Don', 'Marching to the beat of your own drum. Literally.', '(score.mods & 259 == 0) and 2 >= score.sr > 1', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (26, 'taiko-skill-pass-2', 'Katsu Katsu Katsu', 'Hora! Izuko!', '(score.mods & 259 == 0) and 3 >= score.sr > 2', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (27, 'taiko-skill-pass-3', 'Not Even Trying', 'Muzukashii? Not even.', '(score.mods & 259 == 0) and 4 >= score.sr > 3', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (28, 'taiko-skill-pass-4', 'Face Your Demons', 'The first trials are now behind you, but are you a match for the Oni?', '(score.mods & 259 == 0) and 5 >= score.sr > 4', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (29, 'taiko-skill-pass-5', 'The Demon Within', 'No rest for the wicked.', '(score.mods & 259 == 0) and 6 >= score.sr > 5', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (30, 'taiko-skill-pass-6', 'Drumbreaker', 'Too strong.', '(score.mods & 259 == 0) and 7 >= score.sr > 6', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (31, 'taiko-skill-pass-7', 'The Godfather', 'You are the Don of Dons.', '(score.mods & 259 == 0) and 8 >= score.sr > 7', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (32, 'taiko-skill-pass-8', 'Rhythm Incarnate', 'Feel the beat. Become the beat.', '(score.mods & 259 == 0) and 9 >= score.sr > 8', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (33, 'taiko-skill-fc-1', 'Keeping Time', 'Don, then katsu. Don, then katsu..', 'score.perfect and 2 >= score.sr > 1', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (34, 'taiko-skill-fc-2', 'To Your Own Beat', 'Straight and steady.', 'score.perfect and 3 >= score.sr > 2', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (35, 'taiko-skill-fc-3', 'Big Drums', 'Bigger scores to match.', 'score.perfect and 4 >= score.sr > 3', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (36, 'taiko-skill-fc-4', 'Adversity Overcome', 'Difficult? Not for you.', 'score.perfect and 5 >= score.sr > 4', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (37, 'taiko-skill-fc-5', 'Demonslayer', 'An Oni felled forevermore.', 'score.perfect and 6 >= score.sr > 5', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (38, 'taiko-skill-fc-6', 'Rhythm''s Call', 'Heralding true skill.', 'score.perfect and 7 >= score.sr > 6', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (39, 'taiko-skill-fc-7', 'Time Everlasting', 'Not a single beat escapes you.', 'score.perfect and 8 >= score.sr > 7', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (40, 'taiko-skill-fc-8', 'The Drummer''s Throne', 'Percussive brilliance befitting royalty alone.', 'score.perfect and 9 >= score.sr > 8', 1); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (41, 'fruits-skill-pass-1', 'A Slice Of Life', 'Hey, this fruit catching business isn''t bad.', '(score.mods & 259 == 0) and 2 >= score.sr > 1', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (42, 'fruits-skill-pass-2', 'Dashing Ever Forward', 'Fast is how you do it.', '(score.mods & 259 == 0) and 3 >= score.sr > 2', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (43, 'fruits-skill-pass-3', 'Zesty Disposition', 'No scurvy for you, not with that much fruit.', '(score.mods & 259 == 0) and 4 >= score.sr > 3', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (44, 'fruits-skill-pass-4', 'Hyperdash ON!', 'Time and distance is no obstacle to you.', '(score.mods & 259 == 0) and 5 >= score.sr > 4', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (45, 'fruits-skill-pass-5', 'It''s Raining Fruit', 'And you can catch them all.', '(score.mods & 259 == 0) and 6 >= score.sr > 5', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (46, 'fruits-skill-pass-6', 'Fruit Ninja', 'Legendary techniques.', '(score.mods & 259 == 0) and 7 >= score.sr > 6', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (47, 'fruits-skill-pass-7', 'Dreamcatcher', 'No fruit, only dreams now.', '(score.mods & 259 == 0) and 8 >= score.sr > 7', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (48, 'fruits-skill-pass-8', 'Lord of the Catch', 'Your kingdom kneels before you.', '(score.mods & 259 == 0) and 9 >= score.sr > 8', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (49, 'fruits-skill-fc-1', 'Sweet And Sour', 'Apples and oranges, literally.', 'score.perfect and 2 >= score.sr > 1', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (50, 'fruits-skill-fc-2', 'Reaching The Core', 'The seeds of future success.', 'score.perfect and 3 >= score.sr > 2', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (51, 'fruits-skill-fc-3', 'Clean Platter', 'Clean only of failure. It is completely full, otherwise.', 'score.perfect and 4 >= score.sr > 3', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (52, 'fruits-skill-fc-4', 'Between The Rain', 'No umbrella needed.', 'score.perfect and 5 >= score.sr > 4', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (53, 'fruits-skill-fc-5', 'Addicted', 'That was an overdose?', 'score.perfect and 6 >= score.sr > 5', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (54, 'fruits-skill-fc-6', 'Quickening', 'A dash above normal limits.', 'score.perfect and 7 >= score.sr > 6', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (55, 'fruits-skill-fc-7', 'Supersonic', 'Faster than is reasonably necessary.', 'score.perfect and 8 >= score.sr > 7', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (56, 'fruits-skill-fc-8', 'Dashing Scarlet', 'Speed beyond mortal reckoning.', 'score.perfect and 9 >= score.sr > 8', 2); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (57, 'mania-skill-pass-1', 'First Steps', 'It isn''t 9-to-5, but 1-to-9. Keys, that is.', '(score.mods & 259 == 0) and 2 >= score.sr > 1', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (58, 'mania-skill-pass-2', 'No Normal Player', 'Not anymore, at least.', '(score.mods & 259 == 0) and 3 >= score.sr > 2', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (59, 'mania-skill-pass-3', 'Impulse Drive', 'Not quite hyperspeed, but getting close.', '(score.mods & 259 == 0) and 4 >= score.sr > 3', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (60, 'mania-skill-pass-4', 'Hyperspeed', 'Woah.', '(score.mods & 259 == 0) and 5 >= score.sr > 4', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (61, 'mania-skill-pass-5', 'Ever Onwards', 'Another challenge is just around the corner.', '(score.mods & 259 == 0) and 6 >= score.sr > 5', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (62, 'mania-skill-pass-6', 'Another Surpassed', 'Is there no limit to your skills?', '(score.mods & 259 == 0) and 7 >= score.sr > 6', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (63, 'mania-skill-pass-7', 'Extra Credit', 'See me after class.', '(score.mods & 259 == 0) and 8 >= score.sr > 7', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (64, 'mania-skill-pass-8', 'Maniac', 'There''s just no stopping you.', '(score.mods & 259 == 0) and 9 >= score.sr > 8', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (65, 'mania-skill-fc-1', 'Keystruck', 'The beginning of a new story', 'score.perfect and (score.mods & 259 == 0) and 2 >= score.sr > 1', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (66, 'mania-skill-fc-2', 'Keying In', 'Finding your groove.', 'score.perfect and 3 >= score.sr > 2', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (67, 'mania-skill-fc-3', 'Hyperflow', 'You can *feel* the rhythm.', 'score.perfect and 4 >= score.sr > 3', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (68, 'mania-skill-fc-4', 'Breakthrough', 'Many skills mastered, rolled into one.', 'score.perfect and 5 >= score.sr > 4', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (69, 'mania-skill-fc-5', 'Everything Extra', 'Giving your all is giving everything you have.', 'score.perfect and 6 >= score.sr > 5', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (70, 'mania-skill-fc-6', 'Level Breaker', 'Finesse beyond reason', 'score.perfect and 7 >= score.sr > 6', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (71, 'mania-skill-fc-7', 'Step Up', 'A precipice rarely seen.', 'score.perfect and 8 >= score.sr > 7', 3); -insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (72, 'mania-skill-fc-8', 'Behind The Veil', 'Supernatural!', 'score.perfect and 9 >= score.sr > 8', 3); - -# v3.1.3 -alter table clans modify name varchar(16) charset utf8 not null; -alter table clans modify tag varchar(6) charset utf8 not null; -alter table achievements modify name varchar(128) charset utf8 not null; -alter table achievements modify `desc` varchar(256) charset utf8 not null; -alter table maps modify artist varchar(128) charset utf8 not null; -alter table maps modify title varchar(128) charset utf8 not null; -alter table maps modify version varchar(128) charset utf8 not null; -alter table maps modify creator varchar(19) charset utf8 not null comment 'not 100%% certain on len'; -alter table tourney_pools drop foreign key tourney_pools_users_id_fk; -alter table tourney_pool_maps drop foreign key tourney_pool_maps_tourney_pools_id_fk; -alter table stats drop foreign key stats_users_id_fk; -alter table ratings drop foreign key ratings_maps_md5_fk; -alter table ratings drop foreign key ratings_users_id_fk; -alter table logs modify `from` int not null comment 'both from and to are playerids'; - -# v3.1.9 -alter table scores_rx modify id bigint(20) unsigned auto_increment; -update scores_rx set id = id + (6148914691236517205 - 1); -select @max_rx := MAX(id) + 1 from scores_rx; -set @s = CONCAT('alter table scores_rx auto_increment = ', @max_rx); -prepare stmt from @s; -execute stmt; -deallocate PREPARE stmt; -alter table scores_ap modify id bigint(20) unsigned auto_increment; -update scores_ap set id = id + (12297829382473034410 - 1); -select @max_ap := MAX(id) + 1 from scores_ap; -set @s = CONCAT('alter table scores_ap auto_increment = ', @max_ap); -prepare stmt from @s; -execute stmt; -deallocate PREPARE stmt; -alter table performance_reports modify scoreid bigint(20) unsigned auto_increment; - -# v3.2.0 -create table map_requests -( - id int auto_increment - primary key, - map_id int not null, - player_id int not null, - datetime datetime not null, - active tinyint(1) not null -); - -# v3.2.1 -update scores_rx set id = id - 3074457345618258603; -update scores_ap set id = id - 6148914691236517206; - -# v3.2.2 -alter table maps add max_combo int not null after total_length; -alter table users change clan_rank clan_priv tinyint(1) default 0 not null; - -# v3.2.3 -alter table users add api_key char(36) default NULL null; -create unique index users_api_key_uindex on users (api_key); - -# v3.2.4 -update achievements set file = replace(file, 'ctb', 'fruits') where mode = 2; - -# v3.2.5 -update achievements set cond = '(score.mods & 1 == 0) and 1 <= score.sr < 2' where file in ('osu-skill-pass-1', 'taiko-skill-pass-1', 'fruits-skill-pass-1', 'mania-skill-pass-1'); -update achievements set cond = '(score.mods & 1 == 0) and 2 <= score.sr < 3' where file in ('osu-skill-pass-2', 'taiko-skill-pass-2', 'fruits-skill-pass-2', 'mania-skill-pass-2'); -update achievements set cond = '(score.mods & 1 == 0) and 3 <= score.sr < 4' where file in ('osu-skill-pass-3', 'taiko-skill-pass-3', 'fruits-skill-pass-3', 'mania-skill-pass-3'); -update achievements set cond = '(score.mods & 1 == 0) and 4 <= score.sr < 5' where file in ('osu-skill-pass-4', 'taiko-skill-pass-4', 'fruits-skill-pass-4', 'mania-skill-pass-4'); -update achievements set cond = '(score.mods & 1 == 0) and 5 <= score.sr < 6' where file in ('osu-skill-pass-5', 'taiko-skill-pass-5', 'fruits-skill-pass-5', 'mania-skill-pass-5'); -update achievements set cond = '(score.mods & 1 == 0) and 6 <= score.sr < 7' where file in ('osu-skill-pass-6', 'taiko-skill-pass-6', 'fruits-skill-pass-6', 'mania-skill-pass-6'); -update achievements set cond = '(score.mods & 1 == 0) and 7 <= score.sr < 8' where file in ('osu-skill-pass-7', 'taiko-skill-pass-7', 'fruits-skill-pass-7', 'mania-skill-pass-7'); -update achievements set cond = '(score.mods & 1 == 0) and 8 <= score.sr < 9' where file in ('osu-skill-pass-8', 'taiko-skill-pass-8', 'fruits-skill-pass-8', 'mania-skill-pass-8'); -update achievements set cond = '(score.mods & 1 == 0) and 9 <= score.sr < 10' where file = 'osu-skill-pass-9'; -update achievements set cond = '(score.mods & 1 == 0) and 10 <= score.sr < 11' where file = 'osu-skill-pass-10'; - -update achievements set cond = 'score.perfect and 1 <= score.sr < 2' where file in ('osu-skill-fc-1', 'taiko-skill-fc-1', 'fruits-skill-fc-1', 'mania-skill-fc-1'); -update achievements set cond = 'score.perfect and 2 <= score.sr < 3' where file in ('osu-skill-fc-2', 'taiko-skill-fc-2', 'fruits-skill-fc-2', 'mania-skill-fc-2'); -update achievements set cond = 'score.perfect and 3 <= score.sr < 4' where file in ('osu-skill-fc-3', 'taiko-skill-fc-3', 'fruits-skill-fc-3', 'mania-skill-fc-3'); -update achievements set cond = 'score.perfect and 4 <= score.sr < 5' where file in ('osu-skill-fc-4', 'taiko-skill-fc-4', 'fruits-skill-fc-4', 'mania-skill-fc-4'); -update achievements set cond = 'score.perfect and 5 <= score.sr < 6' where file in ('osu-skill-fc-5', 'taiko-skill-fc-5', 'fruits-skill-fc-5', 'mania-skill-fc-5'); -update achievements set cond = 'score.perfect and 6 <= score.sr < 7' where file in ('osu-skill-fc-6', 'taiko-skill-fc-6', 'fruits-skill-fc-6', 'mania-skill-fc-6'); -update achievements set cond = 'score.perfect and 7 <= score.sr < 8' where file in ('osu-skill-fc-7', 'taiko-skill-fc-7', 'fruits-skill-fc-7', 'mania-skill-fc-7'); -update achievements set cond = 'score.perfect and 8 <= score.sr < 9' where file in ('osu-skill-fc-8', 'taiko-skill-fc-8', 'fruits-skill-fc-8', 'mania-skill-fc-8'); -update achievements set cond = 'score.perfect and 9 <= score.sr < 10' where file = 'osu-skill-fc-9'; -update achievements set cond = 'score.perfect and 10 <= score.sr < 11' where file = 'osu-skill-fc-10'; - -update achievements set cond = '500 <= score.max_combo < 750' where file = 'osu-combo-500'; -update achievements set cond = '750 <= score.max_combo < 1000' where file = 'osu-combo-750'; -update achievements set cond = '1000 <= score.max_combo < 2000' where file = 'osu-combo-1000'; -update achievements set cond = '2000 <= score.max_combo' where file = 'osu-combo-2000'; - -# v3.2.6 -alter table stats change maxcombo_vn_std max_combo_vn_std int unsigned default 0 not null; -alter table stats change maxcombo_vn_taiko max_combo_vn_taiko int unsigned default 0 not null; -alter table stats change maxcombo_vn_catch max_combo_vn_catch int unsigned default 0 not null; -alter table stats change maxcombo_vn_mania max_combo_vn_mania int unsigned default 0 not null; -alter table stats change maxcombo_rx_std max_combo_rx_std int unsigned default 0 not null; -alter table stats change maxcombo_rx_taiko max_combo_rx_taiko int unsigned default 0 not null; -alter table stats change maxcombo_rx_catch max_combo_rx_catch int unsigned default 0 not null; -alter table stats change maxcombo_ap_std max_combo_ap_std int unsigned default 0 not null; - -# v3.2.7 -drop table if exists user_hashes; - -# v3.3.0 -rename table friendships to relationships; -alter table relationships add type enum('friend', 'block') not null; - -# v3.3.1 -create table ingame_logins -( - id int auto_increment - primary key, - userid int not null, - ip varchar(45) not null comment 'maxlen for ipv6', - osu_ver date not null, - osu_stream varchar(11) not null, - datetime datetime not null -); - -# v3.3.7 -update achievements set cond = CONCAT(cond, ' and mode_vn == 0') where mode = 0; -update achievements set cond = CONCAT(cond, ' and mode_vn == 1') where mode = 1; -update achievements set cond = CONCAT(cond, ' and mode_vn == 2') where mode = 2; -update achievements set cond = CONCAT(cond, ' and mode_vn == 3') where mode = 3; -alter table achievements drop column mode; - -# v3.3.8 -create table mapsets -( - server enum('osu!', 'gulag') default 'osu!' not null, - id int not null, - last_osuapi_check datetime default CURRENT_TIMESTAMP not null, - primary key (server, id), - constraint nmapsets_id_uindex - unique (id) -); - -# v3.4.1 -alter table maps add filename varchar(256) charset utf8 not null after creator; - -# v3.5.2 -alter table scores_vn add online_checksum char(32) not null; -alter table scores_rx add online_checksum char(32) not null; -alter table scores_ap add online_checksum char(32) not null; - -# v4.1.1 -alter table stats add total_hits int unsigned default 0 not null after max_combo; - -# v4.1.2 -alter table stats add replay_views int unsigned default 0 not null after total_hits; - -# v4.1.3 -alter table users add preferred_mode int default 0 not null after latest_activity; -alter table users add play_style int default 0 not null after preferred_mode; -alter table users add custom_badge_name varchar(16) charset utf8 null after play_style; -alter table users add custom_badge_icon varchar(64) null after custom_badge_name; -alter table users add userpage_content varchar(2048) charset utf8 null after custom_badge_icon; - -# v4.2.0 -# please refer to tools/migrate_v420 for further v4.2.0 migrations -update stats set mode = 8 where mode = 7; - -# v4.3.1 -alter table maps change server server enum('osu!', 'private') default 'osu!' not null; -alter table mapsets change server server enum('osu!', 'private') default 'osu!' not null; - -# v4.4.2 -insert into achievements (id, file, name, `desc`, cond) values (73, 'all-intro-suddendeath', 'Finality', 'High stakes, no regrets.', 'score.mods == 32'); -insert into achievements (id, file, name, `desc`, cond) values (74, 'all-intro-hidden', 'Blindsight', 'I can see just perfectly', 'score.mods & 8'); -insert into achievements (id, file, name, `desc`, cond) values (75, 'all-intro-perfect', 'Perfectionist', 'Accept nothing but the best.', 'score.mods & 16384'); -insert into achievements (id, file, name, `desc`, cond) values (76, 'all-intro-hardrock', 'Rock Around The Clock', "You can\'t stop the rock.", 'score.mods & 16'); -insert into achievements (id, file, name, `desc`, cond) values (77, 'all-intro-doubletime', 'Time And A Half', "Having a right ol\' time. One and a half of them, almost.", 'score.mods & 64'); -insert into achievements (id, file, name, `desc`, cond) values (78, 'all-intro-flashlight', 'Are You Afraid Of The Dark?', "Harder than it looks, probably because it\'s hard to look.", 'score.mods & 1024'); -insert into achievements (id, file, name, `desc`, cond) values (79, 'all-intro-easy', 'Dial It Right Back', 'Sometimes you just want to take it easy.', 'score.mods & 2'); -insert into achievements (id, file, name, `desc`, cond) values (80, 'all-intro-nofail', 'Risk Averse', 'Safety nets are fun!', 'score.mods & 1'); -insert into achievements (id, file, name, `desc`, cond) values (81, 'all-intro-nightcore', 'Sweet Rave Party', 'Founded in the fine tradition of changing things that were just fine as they were.', 'score.mods & 512'); -insert into achievements (id, file, name, `desc`, cond) values (82, 'all-intro-halftime', 'Slowboat', 'You got there. Eventually.', 'score.mods & 256'); -insert into achievements (id, file, name, `desc`, cond) values (83, 'all-intro-spunout', 'Burned Out', 'One cannot always spin to win.', 'score.mods & 4096'); - -# v4.4.3 -alter table favourites add created_at int default 0 not null; - -# v4.7.1 -lock tables maps write; -alter table maps drop primary key; -alter table maps add primary key (id); -alter table maps modify column server enum('osu!', 'private') not null default 'osu!' after id; -unlock tables; - -# v5.0.1 -create index channels_auto_join_index - on channels (auto_join); - -create index maps_set_id_index - on maps (set_id); -create index maps_status_index - on maps (status); -create index maps_filename_index - on maps (filename); -create index maps_plays_index - on maps (plays); -create index maps_mode_index - on maps (mode); -create index maps_frozen_index - on maps (frozen); - -create index scores_map_md5_index - on scores (map_md5); -create index scores_score_index - on scores (score); -create index scores_pp_index - on scores (pp); -create index scores_mods_index - on scores (mods); -create index scores_status_index - on scores (status); -create index scores_mode_index - on scores (mode); -create index scores_play_time_index - on scores (play_time); -create index scores_userid_index - on scores (userid); -create index scores_online_checksum_index - on scores (online_checksum); - -create index stats_mode_index - on stats (mode); -create index stats_pp_index - on stats (pp); -create index stats_tscore_index - on stats (tscore); -create index stats_rscore_index - on stats (rscore); - -create index tourney_pool_maps_mods_slot_index - on tourney_pool_maps (mods, slot); - -create index user_achievements_achid_index - on user_achievements (achid); -create index user_achievements_userid_index - on user_achievements (userid); - -create index users_priv_index - on users (priv); -create index users_clan_id_index - on users (clan_id); -create index users_clan_priv_index - on users (clan_priv); -create index users_country_index - on users (country); - -# v5.2.2 -create index scores_fetch_leaderboard_generic_index - on scores (map_md5, status, mode); +# This file contains any sql updates, along with the +# version they are required from. Touching this without +# at least reading utils/updater.py is certainly a bad idea :) + +# v3.0.6 +alter table users change name_safe safe_name varchar(32) not null; +alter table users drop key users_name_safe_uindex; +alter table users add constraint users_safe_name_uindex unique (safe_name); +alter table users change pw_hash pw_bcrypt char(60) not null; +insert into channels (name, topic, read_priv, write_priv, auto_join) values + ('#supporter', 'General discussion for p2w gamers.', 48, 48, false), + ('#staff', 'General discussion for the cool kids.', 28672, 28672, true), + ('#admin', 'General discussion for the cool.', 24576, 24576, true), + ('#dev', 'General discussion for the.', 16384, 16384, true); + +# v3.0.8 +alter table users modify safe_name varchar(32) charset utf8 not null; +alter table users modify name varchar(32) charset utf8 not null; +alter table mail modify msg varchar(2048) charset utf8 not null; +alter table logs modify msg varchar(2048) charset utf8 not null; +drop table if exists comments; +create table comments +( + id int auto_increment + primary key, + target_id int not null comment 'replay, map, or set id', + target_type enum('replay', 'map', 'song') not null, + userid int not null, + time int not null, + comment varchar(80) charset utf8 not null, + colour char(6) null comment 'rgb hex string' +); + +# v3.0.9 +alter table stats modify tscore_vn_std int unsigned default 0 not null; +alter table stats modify tscore_vn_taiko int unsigned default 0 not null; +alter table stats modify tscore_vn_catch int unsigned default 0 not null; +alter table stats modify tscore_vn_mania int unsigned default 0 not null; +alter table stats modify tscore_rx_std int unsigned default 0 not null; +alter table stats modify tscore_rx_taiko int unsigned default 0 not null; +alter table stats modify tscore_rx_catch int unsigned default 0 not null; +alter table stats modify tscore_ap_std int unsigned default 0 not null; +alter table stats modify rscore_vn_std int unsigned default 0 not null; +alter table stats modify rscore_vn_taiko int unsigned default 0 not null; +alter table stats modify rscore_vn_catch int unsigned default 0 not null; +alter table stats modify rscore_vn_mania int unsigned default 0 not null; +alter table stats modify rscore_rx_std int unsigned default 0 not null; +alter table stats modify rscore_rx_taiko int unsigned default 0 not null; +alter table stats modify rscore_rx_catch int unsigned default 0 not null; +alter table stats modify rscore_ap_std int unsigned default 0 not null; +alter table stats modify pp_vn_std smallint unsigned default 0 not null; +alter table stats modify pp_vn_taiko smallint unsigned default 0 not null; +alter table stats modify pp_vn_catch smallint unsigned default 0 not null; +alter table stats modify pp_vn_mania smallint unsigned default 0 not null; +alter table stats modify pp_rx_std smallint unsigned default 0 not null; +alter table stats modify pp_rx_taiko smallint unsigned default 0 not null; +alter table stats modify pp_rx_catch smallint unsigned default 0 not null; +alter table stats modify pp_ap_std smallint unsigned default 0 not null; +alter table stats modify plays_vn_std int unsigned default 0 not null; +alter table stats modify plays_vn_taiko int unsigned default 0 not null; +alter table stats modify plays_vn_catch int unsigned default 0 not null; +alter table stats modify plays_vn_mania int unsigned default 0 not null; +alter table stats modify plays_rx_std int unsigned default 0 not null; +alter table stats modify plays_rx_taiko int unsigned default 0 not null; +alter table stats modify plays_rx_catch int unsigned default 0 not null; +alter table stats modify plays_ap_std int unsigned default 0 not null; +alter table stats modify playtime_vn_std int unsigned default 0 not null; +alter table stats modify playtime_vn_taiko int unsigned default 0 not null; +alter table stats modify playtime_vn_catch int unsigned default 0 not null; +alter table stats modify playtime_vn_mania int unsigned default 0 not null; +alter table stats modify playtime_rx_std int unsigned default 0 not null; +alter table stats modify playtime_rx_taiko int unsigned default 0 not null; +alter table stats modify playtime_rx_catch int unsigned default 0 not null; +alter table stats modify playtime_ap_std int unsigned default 0 not null; +alter table stats modify maxcombo_vn_std int unsigned default 0 not null; +alter table stats modify maxcombo_vn_taiko int unsigned default 0 not null; +alter table stats modify maxcombo_vn_catch int unsigned default 0 not null; +alter table stats modify maxcombo_vn_mania int unsigned default 0 not null; +alter table stats modify maxcombo_rx_std int unsigned default 0 not null; +alter table stats modify maxcombo_rx_taiko int unsigned default 0 not null; +alter table stats modify maxcombo_rx_catch int unsigned default 0 not null; +alter table stats modify maxcombo_ap_std int unsigned default 0 not null; + +# v3.0.10 +update channels set write_priv = 24576 where name = '#announce'; + +# v3.1.0 +alter table maps modify bpm float(12,2) default 0.00 not null; +alter table stats modify tscore_vn_std bigint unsigned default 0 not null; +alter table stats modify tscore_vn_taiko bigint unsigned default 0 not null; +alter table stats modify tscore_vn_catch bigint unsigned default 0 not null; +alter table stats modify tscore_vn_mania bigint unsigned default 0 not null; +alter table stats modify tscore_rx_std bigint unsigned default 0 not null; +alter table stats modify tscore_rx_taiko bigint unsigned default 0 not null; +alter table stats modify tscore_rx_catch bigint unsigned default 0 not null; +alter table stats modify tscore_ap_std bigint unsigned default 0 not null; +alter table stats modify rscore_vn_std bigint unsigned default 0 not null; +alter table stats modify rscore_vn_taiko bigint unsigned default 0 not null; +alter table stats modify rscore_vn_catch bigint unsigned default 0 not null; +alter table stats modify rscore_vn_mania bigint unsigned default 0 not null; +alter table stats modify rscore_rx_std bigint unsigned default 0 not null; +alter table stats modify rscore_rx_taiko bigint unsigned default 0 not null; +alter table stats modify rscore_rx_catch bigint unsigned default 0 not null; +alter table stats modify rscore_ap_std bigint unsigned default 0 not null; +alter table stats modify pp_vn_std int unsigned default 0 not null; +alter table stats modify pp_vn_taiko int unsigned default 0 not null; +alter table stats modify pp_vn_catch int unsigned default 0 not null; +alter table stats modify pp_vn_mania int unsigned default 0 not null; +alter table stats modify pp_rx_std int unsigned default 0 not null; +alter table stats modify pp_rx_taiko int unsigned default 0 not null; +alter table stats modify pp_rx_catch int unsigned default 0 not null; +alter table stats modify pp_ap_std int unsigned default 0 not null; + +# v3.1.2 +create table clans +( + id int auto_increment + primary key, + name varchar(16) not null, + tag varchar(6) not null, + owner int not null, + created_at datetime not null, + constraint clans_name_uindex + unique (name), + constraint clans_owner_uindex + unique (owner), + constraint clans_tag_uindex + unique (tag) +); +alter table users add clan_id int default 0 not null; +alter table users add clan_rank tinyint(1) default 0 not null; +create table achievements +( + id int auto_increment + primary key, + file varchar(128) not null, + name varchar(128) not null, + `desc` varchar(256) not null, + cond varchar(64) not null, + mode tinyint(1) not null, + constraint achievements_desc_uindex + unique (`desc`), + constraint achievements_file_uindex + unique (file), + constraint achievements_name_uindex + unique (name) +); +create table user_achievements +( + userid int not null, + achid int not null, + primary key (userid, achid) +); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (1, 'osu-skill-pass-1', 'Rising Star', 'Can''t go forward without the first steps.', '(score.mods & 259 == 0) and 2 >= score.sr > 1', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (2, 'osu-skill-pass-2', 'Constellation Prize', 'Definitely not a consolation prize. Now things start getting hard!', '(score.mods & 259 == 0) and 3 >= score.sr > 2', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (3, 'osu-skill-pass-3', 'Building Confidence', 'Oh, you''ve SO got this.', '(score.mods & 259 == 0) and 4 >= score.sr > 3', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (4, 'osu-skill-pass-4', 'Insanity Approaches', 'You''re not twitching, you''re just ready.', '(score.mods & 259 == 0) and 5 >= score.sr > 4', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (5, 'osu-skill-pass-5', 'These Clarion Skies', 'Everything seems so clear now.', '(score.mods & 259 == 0) and 6 >= score.sr > 5', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (6, 'osu-skill-pass-6', 'Above and Beyond', 'A cut above the rest.', '(score.mods & 259 == 0) and 7 >= score.sr > 6', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (7, 'osu-skill-pass-7', 'Supremacy', 'All marvel before your prowess.', '(score.mods & 259 == 0) and 8 >= score.sr > 7', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (8, 'osu-skill-pass-8', 'Absolution', 'My god, you''re full of stars!', '(score.mods & 259 == 0) and 9 >= score.sr > 8', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (9, 'osu-skill-pass-9', 'Event Horizon', 'No force dares to pull you under.', '(score.mods & 259 == 0) and 10 >= score.sr > 9', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (10, 'osu-skill-pass-10', 'Phantasm', 'Fevered is your passion, extraordinary is your skill.', '(score.mods & 259 == 0) and 11 >= score.sr > 10', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (11, 'osu-skill-fc-1', 'Totality', 'All the notes. Every single one.', 'score.perfect and 2 >= score.sr > 1', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (12, 'osu-skill-fc-2', 'Business As Usual', 'Two to go, please.', 'score.perfect and 3 >= score.sr > 2', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (13, 'osu-skill-fc-3', 'Building Steam', 'Hey, this isn''t so bad.', 'score.perfect and 4 >= score.sr > 3', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (14, 'osu-skill-fc-4', 'Moving Forward', 'Bet you feel good about that.', 'score.perfect and 5 >= score.sr > 4', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (15, 'osu-skill-fc-5', 'Paradigm Shift', 'Surprisingly difficult.', 'score.perfect and 6 >= score.sr > 5', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (16, 'osu-skill-fc-6', 'Anguish Quelled', 'Don''t choke.', 'score.perfect and 7 >= score.sr > 6', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (17, 'osu-skill-fc-7', 'Never Give Up', 'Excellence is its own reward.', 'score.perfect and 8 >= score.sr > 7', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (18, 'osu-skill-fc-8', 'Aberration', 'They said it couldn''t be done. They were wrong.', 'score.perfect and 9 >= score.sr > 8', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (19, 'osu-skill-fc-9', 'Chosen', 'Reign among the Prometheans, where you belong.', 'score.perfect and 10 >= score.sr > 9', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (20, 'osu-skill-fc-10', 'Unfathomable', 'You have no equal.', 'score.perfect and 11 >= score.sr > 10', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (21, 'osu-combo-500', '500 Combo', '500 big ones! You''re moving up in the world!', '750 >= score.max_combo > 500', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (22, 'osu-combo-750', '750 Combo', '750 notes back to back? Woah.', '1000 >= score.max_combo > 750', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (23, 'osu-combo-1000', '1000 Combo', 'A thousand reasons why you rock at this game.', '2000 >= score.max_combo > 1000', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (24, 'osu-combo-2000', '2000 Combo', 'Nothing can stop you now.', 'score.max_combo >= 2000', 0); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (25, 'taiko-skill-pass-1', 'My First Don', 'Marching to the beat of your own drum. Literally.', '(score.mods & 259 == 0) and 2 >= score.sr > 1', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (26, 'taiko-skill-pass-2', 'Katsu Katsu Katsu', 'Hora! Izuko!', '(score.mods & 259 == 0) and 3 >= score.sr > 2', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (27, 'taiko-skill-pass-3', 'Not Even Trying', 'Muzukashii? Not even.', '(score.mods & 259 == 0) and 4 >= score.sr > 3', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (28, 'taiko-skill-pass-4', 'Face Your Demons', 'The first trials are now behind you, but are you a match for the Oni?', '(score.mods & 259 == 0) and 5 >= score.sr > 4', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (29, 'taiko-skill-pass-5', 'The Demon Within', 'No rest for the wicked.', '(score.mods & 259 == 0) and 6 >= score.sr > 5', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (30, 'taiko-skill-pass-6', 'Drumbreaker', 'Too strong.', '(score.mods & 259 == 0) and 7 >= score.sr > 6', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (31, 'taiko-skill-pass-7', 'The Godfather', 'You are the Don of Dons.', '(score.mods & 259 == 0) and 8 >= score.sr > 7', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (32, 'taiko-skill-pass-8', 'Rhythm Incarnate', 'Feel the beat. Become the beat.', '(score.mods & 259 == 0) and 9 >= score.sr > 8', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (33, 'taiko-skill-fc-1', 'Keeping Time', 'Don, then katsu. Don, then katsu..', 'score.perfect and 2 >= score.sr > 1', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (34, 'taiko-skill-fc-2', 'To Your Own Beat', 'Straight and steady.', 'score.perfect and 3 >= score.sr > 2', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (35, 'taiko-skill-fc-3', 'Big Drums', 'Bigger scores to match.', 'score.perfect and 4 >= score.sr > 3', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (36, 'taiko-skill-fc-4', 'Adversity Overcome', 'Difficult? Not for you.', 'score.perfect and 5 >= score.sr > 4', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (37, 'taiko-skill-fc-5', 'Demonslayer', 'An Oni felled forevermore.', 'score.perfect and 6 >= score.sr > 5', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (38, 'taiko-skill-fc-6', 'Rhythm''s Call', 'Heralding true skill.', 'score.perfect and 7 >= score.sr > 6', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (39, 'taiko-skill-fc-7', 'Time Everlasting', 'Not a single beat escapes you.', 'score.perfect and 8 >= score.sr > 7', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (40, 'taiko-skill-fc-8', 'The Drummer''s Throne', 'Percussive brilliance befitting royalty alone.', 'score.perfect and 9 >= score.sr > 8', 1); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (41, 'fruits-skill-pass-1', 'A Slice Of Life', 'Hey, this fruit catching business isn''t bad.', '(score.mods & 259 == 0) and 2 >= score.sr > 1', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (42, 'fruits-skill-pass-2', 'Dashing Ever Forward', 'Fast is how you do it.', '(score.mods & 259 == 0) and 3 >= score.sr > 2', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (43, 'fruits-skill-pass-3', 'Zesty Disposition', 'No scurvy for you, not with that much fruit.', '(score.mods & 259 == 0) and 4 >= score.sr > 3', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (44, 'fruits-skill-pass-4', 'Hyperdash ON!', 'Time and distance is no obstacle to you.', '(score.mods & 259 == 0) and 5 >= score.sr > 4', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (45, 'fruits-skill-pass-5', 'It''s Raining Fruit', 'And you can catch them all.', '(score.mods & 259 == 0) and 6 >= score.sr > 5', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (46, 'fruits-skill-pass-6', 'Fruit Ninja', 'Legendary techniques.', '(score.mods & 259 == 0) and 7 >= score.sr > 6', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (47, 'fruits-skill-pass-7', 'Dreamcatcher', 'No fruit, only dreams now.', '(score.mods & 259 == 0) and 8 >= score.sr > 7', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (48, 'fruits-skill-pass-8', 'Lord of the Catch', 'Your kingdom kneels before you.', '(score.mods & 259 == 0) and 9 >= score.sr > 8', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (49, 'fruits-skill-fc-1', 'Sweet And Sour', 'Apples and oranges, literally.', 'score.perfect and 2 >= score.sr > 1', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (50, 'fruits-skill-fc-2', 'Reaching The Core', 'The seeds of future success.', 'score.perfect and 3 >= score.sr > 2', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (51, 'fruits-skill-fc-3', 'Clean Platter', 'Clean only of failure. It is completely full, otherwise.', 'score.perfect and 4 >= score.sr > 3', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (52, 'fruits-skill-fc-4', 'Between The Rain', 'No umbrella needed.', 'score.perfect and 5 >= score.sr > 4', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (53, 'fruits-skill-fc-5', 'Addicted', 'That was an overdose?', 'score.perfect and 6 >= score.sr > 5', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (54, 'fruits-skill-fc-6', 'Quickening', 'A dash above normal limits.', 'score.perfect and 7 >= score.sr > 6', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (55, 'fruits-skill-fc-7', 'Supersonic', 'Faster than is reasonably necessary.', 'score.perfect and 8 >= score.sr > 7', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (56, 'fruits-skill-fc-8', 'Dashing Scarlet', 'Speed beyond mortal reckoning.', 'score.perfect and 9 >= score.sr > 8', 2); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (57, 'mania-skill-pass-1', 'First Steps', 'It isn''t 9-to-5, but 1-to-9. Keys, that is.', '(score.mods & 259 == 0) and 2 >= score.sr > 1', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (58, 'mania-skill-pass-2', 'No Normal Player', 'Not anymore, at least.', '(score.mods & 259 == 0) and 3 >= score.sr > 2', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (59, 'mania-skill-pass-3', 'Impulse Drive', 'Not quite hyperspeed, but getting close.', '(score.mods & 259 == 0) and 4 >= score.sr > 3', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (60, 'mania-skill-pass-4', 'Hyperspeed', 'Woah.', '(score.mods & 259 == 0) and 5 >= score.sr > 4', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (61, 'mania-skill-pass-5', 'Ever Onwards', 'Another challenge is just around the corner.', '(score.mods & 259 == 0) and 6 >= score.sr > 5', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (62, 'mania-skill-pass-6', 'Another Surpassed', 'Is there no limit to your skills?', '(score.mods & 259 == 0) and 7 >= score.sr > 6', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (63, 'mania-skill-pass-7', 'Extra Credit', 'See me after class.', '(score.mods & 259 == 0) and 8 >= score.sr > 7', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (64, 'mania-skill-pass-8', 'Maniac', 'There''s just no stopping you.', '(score.mods & 259 == 0) and 9 >= score.sr > 8', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (65, 'mania-skill-fc-1', 'Keystruck', 'The beginning of a new story', 'score.perfect and (score.mods & 259 == 0) and 2 >= score.sr > 1', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (66, 'mania-skill-fc-2', 'Keying In', 'Finding your groove.', 'score.perfect and 3 >= score.sr > 2', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (67, 'mania-skill-fc-3', 'Hyperflow', 'You can *feel* the rhythm.', 'score.perfect and 4 >= score.sr > 3', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (68, 'mania-skill-fc-4', 'Breakthrough', 'Many skills mastered, rolled into one.', 'score.perfect and 5 >= score.sr > 4', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (69, 'mania-skill-fc-5', 'Everything Extra', 'Giving your all is giving everything you have.', 'score.perfect and 6 >= score.sr > 5', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (70, 'mania-skill-fc-6', 'Level Breaker', 'Finesse beyond reason', 'score.perfect and 7 >= score.sr > 6', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (71, 'mania-skill-fc-7', 'Step Up', 'A precipice rarely seen.', 'score.perfect and 8 >= score.sr > 7', 3); +insert into achievements (`id`, `file`, `name`, `desc`, `cond`, `mode`) values (72, 'mania-skill-fc-8', 'Behind The Veil', 'Supernatural!', 'score.perfect and 9 >= score.sr > 8', 3); + +# v3.1.3 +alter table clans modify name varchar(16) charset utf8 not null; +alter table clans modify tag varchar(6) charset utf8 not null; +alter table achievements modify name varchar(128) charset utf8 not null; +alter table achievements modify `desc` varchar(256) charset utf8 not null; +alter table maps modify artist varchar(128) charset utf8 not null; +alter table maps modify title varchar(128) charset utf8 not null; +alter table maps modify version varchar(128) charset utf8 not null; +alter table maps modify creator varchar(19) charset utf8 not null comment 'not 100%% certain on len'; +alter table tourney_pools drop foreign key tourney_pools_users_id_fk; +alter table tourney_pool_maps drop foreign key tourney_pool_maps_tourney_pools_id_fk; +alter table stats drop foreign key stats_users_id_fk; +alter table ratings drop foreign key ratings_maps_md5_fk; +alter table ratings drop foreign key ratings_users_id_fk; +alter table logs modify `from` int not null comment 'both from and to are playerids'; + +# v3.1.9 +alter table scores_rx modify id bigint(20) unsigned auto_increment; +update scores_rx set id = id + (6148914691236517205 - 1); +select @max_rx := MAX(id) + 1 from scores_rx; +set @s = CONCAT('alter table scores_rx auto_increment = ', @max_rx); +prepare stmt from @s; +execute stmt; +deallocate PREPARE stmt; +alter table scores_ap modify id bigint(20) unsigned auto_increment; +update scores_ap set id = id + (12297829382473034410 - 1); +select @max_ap := MAX(id) + 1 from scores_ap; +set @s = CONCAT('alter table scores_ap auto_increment = ', @max_ap); +prepare stmt from @s; +execute stmt; +deallocate PREPARE stmt; +alter table performance_reports modify scoreid bigint(20) unsigned auto_increment; + +# v3.2.0 +create table map_requests +( + id int auto_increment + primary key, + map_id int not null, + player_id int not null, + datetime datetime not null, + active tinyint(1) not null +); + +# v3.2.1 +update scores_rx set id = id - 3074457345618258603; +update scores_ap set id = id - 6148914691236517206; + +# v3.2.2 +alter table maps add max_combo int not null after total_length; +alter table users change clan_rank clan_priv tinyint(1) default 0 not null; + +# v3.2.3 +alter table users add api_key char(36) default NULL null; +create unique index users_api_key_uindex on users (api_key); + +# v3.2.4 +update achievements set file = replace(file, 'ctb', 'fruits') where mode = 2; + +# v3.2.5 +update achievements set cond = '(score.mods & 1 == 0) and 1 <= score.sr < 2' where file in ('osu-skill-pass-1', 'taiko-skill-pass-1', 'fruits-skill-pass-1', 'mania-skill-pass-1'); +update achievements set cond = '(score.mods & 1 == 0) and 2 <= score.sr < 3' where file in ('osu-skill-pass-2', 'taiko-skill-pass-2', 'fruits-skill-pass-2', 'mania-skill-pass-2'); +update achievements set cond = '(score.mods & 1 == 0) and 3 <= score.sr < 4' where file in ('osu-skill-pass-3', 'taiko-skill-pass-3', 'fruits-skill-pass-3', 'mania-skill-pass-3'); +update achievements set cond = '(score.mods & 1 == 0) and 4 <= score.sr < 5' where file in ('osu-skill-pass-4', 'taiko-skill-pass-4', 'fruits-skill-pass-4', 'mania-skill-pass-4'); +update achievements set cond = '(score.mods & 1 == 0) and 5 <= score.sr < 6' where file in ('osu-skill-pass-5', 'taiko-skill-pass-5', 'fruits-skill-pass-5', 'mania-skill-pass-5'); +update achievements set cond = '(score.mods & 1 == 0) and 6 <= score.sr < 7' where file in ('osu-skill-pass-6', 'taiko-skill-pass-6', 'fruits-skill-pass-6', 'mania-skill-pass-6'); +update achievements set cond = '(score.mods & 1 == 0) and 7 <= score.sr < 8' where file in ('osu-skill-pass-7', 'taiko-skill-pass-7', 'fruits-skill-pass-7', 'mania-skill-pass-7'); +update achievements set cond = '(score.mods & 1 == 0) and 8 <= score.sr < 9' where file in ('osu-skill-pass-8', 'taiko-skill-pass-8', 'fruits-skill-pass-8', 'mania-skill-pass-8'); +update achievements set cond = '(score.mods & 1 == 0) and 9 <= score.sr < 10' where file = 'osu-skill-pass-9'; +update achievements set cond = '(score.mods & 1 == 0) and 10 <= score.sr < 11' where file = 'osu-skill-pass-10'; + +update achievements set cond = 'score.perfect and 1 <= score.sr < 2' where file in ('osu-skill-fc-1', 'taiko-skill-fc-1', 'fruits-skill-fc-1', 'mania-skill-fc-1'); +update achievements set cond = 'score.perfect and 2 <= score.sr < 3' where file in ('osu-skill-fc-2', 'taiko-skill-fc-2', 'fruits-skill-fc-2', 'mania-skill-fc-2'); +update achievements set cond = 'score.perfect and 3 <= score.sr < 4' where file in ('osu-skill-fc-3', 'taiko-skill-fc-3', 'fruits-skill-fc-3', 'mania-skill-fc-3'); +update achievements set cond = 'score.perfect and 4 <= score.sr < 5' where file in ('osu-skill-fc-4', 'taiko-skill-fc-4', 'fruits-skill-fc-4', 'mania-skill-fc-4'); +update achievements set cond = 'score.perfect and 5 <= score.sr < 6' where file in ('osu-skill-fc-5', 'taiko-skill-fc-5', 'fruits-skill-fc-5', 'mania-skill-fc-5'); +update achievements set cond = 'score.perfect and 6 <= score.sr < 7' where file in ('osu-skill-fc-6', 'taiko-skill-fc-6', 'fruits-skill-fc-6', 'mania-skill-fc-6'); +update achievements set cond = 'score.perfect and 7 <= score.sr < 8' where file in ('osu-skill-fc-7', 'taiko-skill-fc-7', 'fruits-skill-fc-7', 'mania-skill-fc-7'); +update achievements set cond = 'score.perfect and 8 <= score.sr < 9' where file in ('osu-skill-fc-8', 'taiko-skill-fc-8', 'fruits-skill-fc-8', 'mania-skill-fc-8'); +update achievements set cond = 'score.perfect and 9 <= score.sr < 10' where file = 'osu-skill-fc-9'; +update achievements set cond = 'score.perfect and 10 <= score.sr < 11' where file = 'osu-skill-fc-10'; + +update achievements set cond = '500 <= score.max_combo < 750' where file = 'osu-combo-500'; +update achievements set cond = '750 <= score.max_combo < 1000' where file = 'osu-combo-750'; +update achievements set cond = '1000 <= score.max_combo < 2000' where file = 'osu-combo-1000'; +update achievements set cond = '2000 <= score.max_combo' where file = 'osu-combo-2000'; + +# v3.2.6 +alter table stats change maxcombo_vn_std max_combo_vn_std int unsigned default 0 not null; +alter table stats change maxcombo_vn_taiko max_combo_vn_taiko int unsigned default 0 not null; +alter table stats change maxcombo_vn_catch max_combo_vn_catch int unsigned default 0 not null; +alter table stats change maxcombo_vn_mania max_combo_vn_mania int unsigned default 0 not null; +alter table stats change maxcombo_rx_std max_combo_rx_std int unsigned default 0 not null; +alter table stats change maxcombo_rx_taiko max_combo_rx_taiko int unsigned default 0 not null; +alter table stats change maxcombo_rx_catch max_combo_rx_catch int unsigned default 0 not null; +alter table stats change maxcombo_ap_std max_combo_ap_std int unsigned default 0 not null; + +# v3.2.7 +drop table if exists user_hashes; + +# v3.3.0 +rename table friendships to relationships; +alter table relationships add type enum('friend', 'block') not null; + +# v3.3.1 +create table ingame_logins +( + id int auto_increment + primary key, + userid int not null, + ip varchar(45) not null comment 'maxlen for ipv6', + osu_ver date not null, + osu_stream varchar(11) not null, + datetime datetime not null +); + +# v3.3.7 +update achievements set cond = CONCAT(cond, ' and mode_vn == 0') where mode = 0; +update achievements set cond = CONCAT(cond, ' and mode_vn == 1') where mode = 1; +update achievements set cond = CONCAT(cond, ' and mode_vn == 2') where mode = 2; +update achievements set cond = CONCAT(cond, ' and mode_vn == 3') where mode = 3; +alter table achievements drop column mode; + +# v3.3.8 +create table mapsets +( + server enum('osu!', 'gulag') default 'osu!' not null, + id int not null, + last_osuapi_check datetime default CURRENT_TIMESTAMP not null, + primary key (server, id), + constraint nmapsets_id_uindex + unique (id) +); + +# v3.4.1 +alter table maps add filename varchar(256) charset utf8 not null after creator; + +# v3.5.2 +alter table scores_vn add online_checksum char(32) not null; +alter table scores_rx add online_checksum char(32) not null; +alter table scores_ap add online_checksum char(32) not null; + +# v4.1.1 +alter table stats add total_hits int unsigned default 0 not null after max_combo; + +# v4.1.2 +alter table stats add replay_views int unsigned default 0 not null after total_hits; + +# v4.1.3 +alter table users add preferred_mode int default 0 not null after latest_activity; +alter table users add play_style int default 0 not null after preferred_mode; +alter table users add custom_badge_name varchar(16) charset utf8 null after play_style; +alter table users add custom_badge_icon varchar(64) null after custom_badge_name; +alter table users add userpage_content varchar(2048) charset utf8 null after custom_badge_icon; + +# v4.2.0 +# please refer to tools/migrate_v420 for further v4.2.0 migrations +update stats set mode = 8 where mode = 7; + +# v4.3.1 +alter table maps change server server enum('osu!', 'private') default 'osu!' not null; +alter table mapsets change server server enum('osu!', 'private') default 'osu!' not null; + +# v4.4.2 +insert into achievements (id, file, name, `desc`, cond) values (73, 'all-intro-suddendeath', 'Finality', 'High stakes, no regrets.', 'score.mods == 32'); +insert into achievements (id, file, name, `desc`, cond) values (74, 'all-intro-hidden', 'Blindsight', 'I can see just perfectly', 'score.mods & 8'); +insert into achievements (id, file, name, `desc`, cond) values (75, 'all-intro-perfect', 'Perfectionist', 'Accept nothing but the best.', 'score.mods & 16384'); +insert into achievements (id, file, name, `desc`, cond) values (76, 'all-intro-hardrock', 'Rock Around The Clock', "You can\'t stop the rock.", 'score.mods & 16'); +insert into achievements (id, file, name, `desc`, cond) values (77, 'all-intro-doubletime', 'Time And A Half', "Having a right ol\' time. One and a half of them, almost.", 'score.mods & 64'); +insert into achievements (id, file, name, `desc`, cond) values (78, 'all-intro-flashlight', 'Are You Afraid Of The Dark?', "Harder than it looks, probably because it\'s hard to look.", 'score.mods & 1024'); +insert into achievements (id, file, name, `desc`, cond) values (79, 'all-intro-easy', 'Dial It Right Back', 'Sometimes you just want to take it easy.', 'score.mods & 2'); +insert into achievements (id, file, name, `desc`, cond) values (80, 'all-intro-nofail', 'Risk Averse', 'Safety nets are fun!', 'score.mods & 1'); +insert into achievements (id, file, name, `desc`, cond) values (81, 'all-intro-nightcore', 'Sweet Rave Party', 'Founded in the fine tradition of changing things that were just fine as they were.', 'score.mods & 512'); +insert into achievements (id, file, name, `desc`, cond) values (82, 'all-intro-halftime', 'Slowboat', 'You got there. Eventually.', 'score.mods & 256'); +insert into achievements (id, file, name, `desc`, cond) values (83, 'all-intro-spunout', 'Burned Out', 'One cannot always spin to win.', 'score.mods & 4096'); + +# v4.4.3 +alter table favourites add created_at int default 0 not null; + +# v4.7.1 +lock tables maps write; +alter table maps drop primary key; +alter table maps add primary key (id); +alter table maps modify column server enum('osu!', 'private') not null default 'osu!' after id; +unlock tables; + +# v5.0.1 +create index channels_auto_join_index + on channels (auto_join); + +create index maps_set_id_index + on maps (set_id); +create index maps_status_index + on maps (status); +create index maps_filename_index + on maps (filename); +create index maps_plays_index + on maps (plays); +create index maps_mode_index + on maps (mode); +create index maps_frozen_index + on maps (frozen); + +create index scores_map_md5_index + on scores (map_md5); +create index scores_score_index + on scores (score); +create index scores_pp_index + on scores (pp); +create index scores_mods_index + on scores (mods); +create index scores_status_index + on scores (status); +create index scores_mode_index + on scores (mode); +create index scores_play_time_index + on scores (play_time); +create index scores_userid_index + on scores (userid); +create index scores_online_checksum_index + on scores (online_checksum); + +create index stats_mode_index + on stats (mode); +create index stats_pp_index + on stats (pp); +create index stats_tscore_index + on stats (tscore); +create index stats_rscore_index + on stats (rscore); + +create index tourney_pool_maps_mods_slot_index + on tourney_pool_maps (mods, slot); + +create index user_achievements_achid_index + on user_achievements (achid); +create index user_achievements_userid_index + on user_achievements (userid); + +create index users_priv_index + on users (priv); +create index users_clan_id_index + on users (clan_id); +create index users_clan_priv_index + on users (clan_priv); +create index users_country_index + on users (country); + +# v5.2.2 +create index scores_fetch_leaderboard_generic_index + on scores (map_md5, status, mode); diff --git a/migrations/sync_legacy_data.sql b/migrations/sync_legacy_data.sql index 19c94a4..fca085e 100644 --- a/migrations/sync_legacy_data.sql +++ b/migrations/sync_legacy_data.sql @@ -1,337 +1,337 @@ --- Lazer API 数据同步脚本 --- 从现有的 bancho.py 表结构同步数据到新的 lazer 专用表 --- 执行此脚本前请确保已执行 add_missing_fields.sql - --- ============================================ --- 同步用户基本资料数据 --- ============================================ - --- 同步用户扩展资料 -INSERT INTO lazer_user_profiles ( - user_id, - is_active, - is_bot, - is_deleted, - is_online, - is_supporter, - is_restricted, - session_verified, - has_supported, - pm_friends_only, - default_group, - last_visit, - join_date, - profile_colour, - profile_hue, - avatar_url, - cover_url, - discord, - twitter, - website, - title, - title_url, - interests, - location, - occupation, - playmode, - support_level, - max_blocks, - max_friends, - post_count, - page_html, - page_raw -) -SELECT - u.id as user_id, - -- 基本状态字段 (使用默认值,因为原表没有这些字段) - 1 as is_active, - CASE WHEN u.name = 'BanchoBot' THEN 1 ELSE 0 END as is_bot, - 0 as is_deleted, - 1 as is_online, - CASE WHEN u.donor_end > UNIX_TIMESTAMP() THEN 1 ELSE 0 END as is_supporter, - CASE WHEN (u.priv & 1) = 0 THEN 1 ELSE 0 END as is_restricted, - 0 as session_verified, - CASE WHEN u.donor_end > 0 THEN 1 ELSE 0 END as has_supported, - 0 as pm_friends_only, - - -- 基本资料字段 - 'default' as default_group, - CASE WHEN u.latest_activity > 0 THEN FROM_UNIXTIME(u.latest_activity) ELSE NULL END as last_visit, - CASE WHEN u.creation_time > 0 THEN FROM_UNIXTIME(u.creation_time) ELSE NULL END as join_date, - NULL as profile_colour, - NULL as profile_hue, - - -- 社交媒体和个人资料字段 (使用默认值) - CONCAT('https://a.ppy.sh/', u.id) as avatar_url, - CONCAT('https://assets.ppy.sh/user-profile-covers/banners/', u.id, '.jpg') as cover_url, - NULL as discord, - NULL as twitter, - NULL as website, - u.custom_badge_name as title, - NULL as title_url, - NULL as interests, - CASE WHEN u.country != 'xx' THEN u.country ELSE NULL END as location, - NULL as occupation, - - -- 游戏相关字段 - CASE u.preferred_mode - WHEN 0 THEN 'osu' - WHEN 1 THEN 'taiko' - WHEN 2 THEN 'fruits' - WHEN 3 THEN 'mania' - ELSE 'osu' - END as playmode, - CASE WHEN u.donor_end > UNIX_TIMESTAMP() THEN 1 ELSE 0 END as support_level, - 100 as max_blocks, - 500 as max_friends, - 0 as post_count, - - -- 页面内容 - u.userpage_content as page_html, - u.userpage_content as page_raw - -FROM users u -ON DUPLICATE KEY UPDATE - last_visit = VALUES(last_visit), - join_date = VALUES(join_date), - is_supporter = VALUES(is_supporter), - is_restricted = VALUES(is_restricted), - has_supported = VALUES(has_supported), - title = VALUES(title), - location = VALUES(location), - playmode = VALUES(playmode), - support_level = VALUES(support_level), - page_html = VALUES(page_html), - page_raw = VALUES(page_raw); - --- 同步用户国家信息 -INSERT INTO lazer_user_countries ( - user_id, - code, - name -) -SELECT - u.id as user_id, - UPPER(u.country) as code, - CASE UPPER(u.country) - WHEN 'CN' THEN 'China' - WHEN 'US' THEN 'United States' - WHEN 'JP' THEN 'Japan' - WHEN 'KR' THEN 'South Korea' - WHEN 'CA' THEN 'Canada' - WHEN 'GB' THEN 'United Kingdom' - WHEN 'DE' THEN 'Germany' - WHEN 'FR' THEN 'France' - WHEN 'AU' THEN 'Australia' - WHEN 'RU' THEN 'Russia' - ELSE 'Unknown' - END as name -FROM users u -WHERE u.country IS NOT NULL AND u.country != 'xx' -ON DUPLICATE KEY UPDATE - code = VALUES(code), - name = VALUES(name); - --- 同步用户 Kudosu (使用默认值) -INSERT INTO lazer_user_kudosu ( - user_id, - available, - total -) -SELECT - u.id as user_id, - 0 as available, - 0 as total -FROM users u -ON DUPLICATE KEY UPDATE - available = VALUES(available), - total = VALUES(total); - --- 同步用户统计计数 (使用默认值) -INSERT INTO lazer_user_counts ( - user_id, - beatmap_playcounts_count, - comments_count, - favourite_beatmapset_count, - follower_count, - graveyard_beatmapset_count, - guest_beatmapset_count, - loved_beatmapset_count, - mapping_follower_count, - nominated_beatmapset_count, - pending_beatmapset_count, - ranked_beatmapset_count, - ranked_and_approved_beatmapset_count, - unranked_beatmapset_count, - scores_best_count, - scores_first_count, - scores_pinned_count, - scores_recent_count -) -SELECT - u.id as user_id, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -FROM users u -ON DUPLICATE KEY UPDATE - user_id = VALUES(user_id); - --- ============================================ --- 同步游戏统计数据 --- ============================================ - --- 从 stats 表同步用户统计数据到 lazer_user_statistics -INSERT INTO lazer_user_statistics ( - user_id, - mode, - count_100, - count_300, - count_50, - count_miss, - level_current, - level_progress, - global_rank, - country_rank, - pp, - ranked_score, - hit_accuracy, - total_score, - total_hits, - maximum_combo, - play_count, - play_time, - replays_watched_by_others, - is_ranked, - grade_ss, - grade_ssh, - grade_s, - grade_sh, - grade_a -) -SELECT - s.id as user_id, - CASE s.mode - WHEN 0 THEN 'osu' - WHEN 1 THEN 'taiko' - WHEN 2 THEN 'fruits' - WHEN 3 THEN 'mania' - ELSE 'osu' - END as mode, - - -- 基本命中统计 - s.n100 as count_100, - s.n300 as count_300, - s.n50 as count_50, - s.nmiss as count_miss, - - -- 等级信息 - 1 as level_current, - 0 as level_progress, - - -- 排名信息 - NULL as global_rank, - NULL as country_rank, - - -- PP 和分数 - s.pp as pp, - s.rscore as ranked_score, - CASE WHEN (s.n300 + s.n100 + s.n50 + s.nmiss) > 0 - THEN ROUND((s.n300 * 300 + s.n100 * 100 + s.n50 * 50) / ((s.n300 + s.n100 + s.n50 + s.nmiss) * 300) * 100, 2) - ELSE 0.00 - END as hit_accuracy, - s.tscore as total_score, - (s.n300 + s.n100 + s.n50) as total_hits, - s.max_combo as maximum_combo, - - -- 游戏统计 - s.plays as play_count, - s.playtime as play_time, - 0 as replays_watched_by_others, - CASE WHEN s.pp > 0 THEN 1 ELSE 0 END as is_ranked, - - -- 成绩等级计数 - 0 as grade_ss, - 0 as grade_ssh, - 0 as grade_s, - 0 as grade_sh, - 0 as grade_a - -FROM stats s -WHERE EXISTS (SELECT 1 FROM users u WHERE u.id = s.id) -ON DUPLICATE KEY UPDATE - count_100 = VALUES(count_100), - count_300 = VALUES(count_300), - count_50 = VALUES(count_50), - count_miss = VALUES(count_miss), - pp = VALUES(pp), - ranked_score = VALUES(ranked_score), - hit_accuracy = VALUES(hit_accuracy), - total_score = VALUES(total_score), - total_hits = VALUES(total_hits), - maximum_combo = VALUES(maximum_combo), - play_count = VALUES(play_count), - play_time = VALUES(play_time), - is_ranked = VALUES(is_ranked); - --- ============================================ --- 同步用户成就数据 --- ============================================ - --- 从 user_achievements 表同步数据(如果存在的话) -INSERT IGNORE INTO lazer_user_achievements ( - user_id, - achievement_id, - achieved_at -) -SELECT - ua.userid as user_id, - ua.achid as achievement_id, - NOW() as achieved_at -- 使用当前时间作为获得时间 -FROM user_achievements ua -WHERE EXISTS (SELECT 1 FROM users u WHERE u.id = ua.userid); - --- ============================================ --- 创建基础 OAuth 令牌记录(如果需要的话) --- ============================================ - --- 注意: OAuth 令牌通常在用户登录时动态创建,这里不需要预先填充 - --- ============================================ --- 同步完成提示 --- ============================================ - --- 显示同步统计信息 -SELECT - 'lazer_user_profiles' as table_name, - COUNT(*) as synced_records -FROM lazer_user_profiles -UNION ALL -SELECT - 'lazer_user_countries' as table_name, - COUNT(*) as synced_records -FROM lazer_user_countries -UNION ALL -SELECT - 'lazer_user_statistics' as table_name, - COUNT(*) as synced_records -FROM lazer_user_statistics -UNION ALL -SELECT - 'lazer_user_achievements' as table_name, - COUNT(*) as synced_records -FROM lazer_user_achievements; - --- 显示一些样本数据 -SELECT - u.id, - u.name, - lup.is_supporter, - lup.playmode, - luc.code as country_code, - lus.pp, - lus.play_count -FROM users u -LEFT JOIN lazer_user_profiles lup ON u.id = lup.user_id -LEFT JOIN lazer_user_countries luc ON u.id = luc.user_id -LEFT JOIN lazer_user_statistics lus ON u.id = lus.user_id AND lus.mode = 'osu' -ORDER BY u.id -LIMIT 10; +-- Lazer API 数据同步脚本 +-- 从现有的 bancho.py 表结构同步数据到新的 lazer 专用表 +-- 执行此脚本前请确保已执行 add_missing_fields.sql + +-- ============================================ +-- 同步用户基本资料数据 +-- ============================================ + +-- 同步用户扩展资料 +INSERT INTO lazer_user_profiles ( + user_id, + is_active, + is_bot, + is_deleted, + is_online, + is_supporter, + is_restricted, + session_verified, + has_supported, + pm_friends_only, + default_group, + last_visit, + join_date, + profile_colour, + profile_hue, + avatar_url, + cover_url, + discord, + twitter, + website, + title, + title_url, + interests, + location, + occupation, + playmode, + support_level, + max_blocks, + max_friends, + post_count, + page_html, + page_raw +) +SELECT + u.id as user_id, + -- 基本状态字段 (使用默认值,因为原表没有这些字段) + 1 as is_active, + CASE WHEN u.name = 'BanchoBot' THEN 1 ELSE 0 END as is_bot, + 0 as is_deleted, + 1 as is_online, + CASE WHEN u.donor_end > UNIX_TIMESTAMP() THEN 1 ELSE 0 END as is_supporter, + CASE WHEN (u.priv & 1) = 0 THEN 1 ELSE 0 END as is_restricted, + 0 as session_verified, + CASE WHEN u.donor_end > 0 THEN 1 ELSE 0 END as has_supported, + 0 as pm_friends_only, + + -- 基本资料字段 + 'default' as default_group, + CASE WHEN u.latest_activity > 0 THEN FROM_UNIXTIME(u.latest_activity) ELSE NULL END as last_visit, + CASE WHEN u.creation_time > 0 THEN FROM_UNIXTIME(u.creation_time) ELSE NULL END as join_date, + NULL as profile_colour, + NULL as profile_hue, + + -- 社交媒体和个人资料字段 (使用默认值) + CONCAT('https://a.ppy.sh/', u.id) as avatar_url, + CONCAT('https://assets.ppy.sh/user-profile-covers/banners/', u.id, '.jpg') as cover_url, + NULL as discord, + NULL as twitter, + NULL as website, + u.custom_badge_name as title, + NULL as title_url, + NULL as interests, + CASE WHEN u.country != 'xx' THEN u.country ELSE NULL END as location, + NULL as occupation, + + -- 游戏相关字段 + CASE u.preferred_mode + WHEN 0 THEN 'osu' + WHEN 1 THEN 'taiko' + WHEN 2 THEN 'fruits' + WHEN 3 THEN 'mania' + ELSE 'osu' + END as playmode, + CASE WHEN u.donor_end > UNIX_TIMESTAMP() THEN 1 ELSE 0 END as support_level, + 100 as max_blocks, + 500 as max_friends, + 0 as post_count, + + -- 页面内容 + u.userpage_content as page_html, + u.userpage_content as page_raw + +FROM users u +ON DUPLICATE KEY UPDATE + last_visit = VALUES(last_visit), + join_date = VALUES(join_date), + is_supporter = VALUES(is_supporter), + is_restricted = VALUES(is_restricted), + has_supported = VALUES(has_supported), + title = VALUES(title), + location = VALUES(location), + playmode = VALUES(playmode), + support_level = VALUES(support_level), + page_html = VALUES(page_html), + page_raw = VALUES(page_raw); + +-- 同步用户国家信息 +INSERT INTO lazer_user_countries ( + user_id, + code, + name +) +SELECT + u.id as user_id, + UPPER(u.country) as code, + CASE UPPER(u.country) + WHEN 'CN' THEN 'China' + WHEN 'US' THEN 'United States' + WHEN 'JP' THEN 'Japan' + WHEN 'KR' THEN 'South Korea' + WHEN 'CA' THEN 'Canada' + WHEN 'GB' THEN 'United Kingdom' + WHEN 'DE' THEN 'Germany' + WHEN 'FR' THEN 'France' + WHEN 'AU' THEN 'Australia' + WHEN 'RU' THEN 'Russia' + ELSE 'Unknown' + END as name +FROM users u +WHERE u.country IS NOT NULL AND u.country != 'xx' +ON DUPLICATE KEY UPDATE + code = VALUES(code), + name = VALUES(name); + +-- 同步用户 Kudosu (使用默认值) +INSERT INTO lazer_user_kudosu ( + user_id, + available, + total +) +SELECT + u.id as user_id, + 0 as available, + 0 as total +FROM users u +ON DUPLICATE KEY UPDATE + available = VALUES(available), + total = VALUES(total); + +-- 同步用户统计计数 (使用默认值) +INSERT INTO lazer_user_counts ( + user_id, + beatmap_playcounts_count, + comments_count, + favourite_beatmapset_count, + follower_count, + graveyard_beatmapset_count, + guest_beatmapset_count, + loved_beatmapset_count, + mapping_follower_count, + nominated_beatmapset_count, + pending_beatmapset_count, + ranked_beatmapset_count, + ranked_and_approved_beatmapset_count, + unranked_beatmapset_count, + scores_best_count, + scores_first_count, + scores_pinned_count, + scores_recent_count +) +SELECT + u.id as user_id, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +FROM users u +ON DUPLICATE KEY UPDATE + user_id = VALUES(user_id); + +-- ============================================ +-- 同步游戏统计数据 +-- ============================================ + +-- 从 stats 表同步用户统计数据到 lazer_user_statistics +INSERT INTO lazer_user_statistics ( + user_id, + mode, + count_100, + count_300, + count_50, + count_miss, + level_current, + level_progress, + global_rank, + country_rank, + pp, + ranked_score, + hit_accuracy, + total_score, + total_hits, + maximum_combo, + play_count, + play_time, + replays_watched_by_others, + is_ranked, + grade_ss, + grade_ssh, + grade_s, + grade_sh, + grade_a +) +SELECT + s.id as user_id, + CASE s.mode + WHEN 0 THEN 'osu' + WHEN 1 THEN 'taiko' + WHEN 2 THEN 'fruits' + WHEN 3 THEN 'mania' + ELSE 'osu' + END as mode, + + -- 基本命中统计 + s.n100 as count_100, + s.n300 as count_300, + s.n50 as count_50, + s.nmiss as count_miss, + + -- 等级信息 + 1 as level_current, + 0 as level_progress, + + -- 排名信息 + NULL as global_rank, + NULL as country_rank, + + -- PP 和分数 + s.pp as pp, + s.rscore as ranked_score, + CASE WHEN (s.n300 + s.n100 + s.n50 + s.nmiss) > 0 + THEN ROUND((s.n300 * 300 + s.n100 * 100 + s.n50 * 50) / ((s.n300 + s.n100 + s.n50 + s.nmiss) * 300) * 100, 2) + ELSE 0.00 + END as hit_accuracy, + s.tscore as total_score, + (s.n300 + s.n100 + s.n50) as total_hits, + s.max_combo as maximum_combo, + + -- 游戏统计 + s.plays as play_count, + s.playtime as play_time, + 0 as replays_watched_by_others, + CASE WHEN s.pp > 0 THEN 1 ELSE 0 END as is_ranked, + + -- 成绩等级计数 + 0 as grade_ss, + 0 as grade_ssh, + 0 as grade_s, + 0 as grade_sh, + 0 as grade_a + +FROM stats s +WHERE EXISTS (SELECT 1 FROM users u WHERE u.id = s.id) +ON DUPLICATE KEY UPDATE + count_100 = VALUES(count_100), + count_300 = VALUES(count_300), + count_50 = VALUES(count_50), + count_miss = VALUES(count_miss), + pp = VALUES(pp), + ranked_score = VALUES(ranked_score), + hit_accuracy = VALUES(hit_accuracy), + total_score = VALUES(total_score), + total_hits = VALUES(total_hits), + maximum_combo = VALUES(maximum_combo), + play_count = VALUES(play_count), + play_time = VALUES(play_time), + is_ranked = VALUES(is_ranked); + +-- ============================================ +-- 同步用户成就数据 +-- ============================================ + +-- 从 user_achievements 表同步数据(如果存在的话) +INSERT IGNORE INTO lazer_user_achievements ( + user_id, + achievement_id, + achieved_at +) +SELECT + ua.userid as user_id, + ua.achid as achievement_id, + NOW() as achieved_at -- 使用当前时间作为获得时间 +FROM user_achievements ua +WHERE EXISTS (SELECT 1 FROM users u WHERE u.id = ua.userid); + +-- ============================================ +-- 创建基础 OAuth 令牌记录(如果需要的话) +-- ============================================ + +-- 注意: OAuth 令牌通常在用户登录时动态创建,这里不需要预先填充 + +-- ============================================ +-- 同步完成提示 +-- ============================================ + +-- 显示同步统计信息 +SELECT + 'lazer_user_profiles' as table_name, + COUNT(*) as synced_records +FROM lazer_user_profiles +UNION ALL +SELECT + 'lazer_user_countries' as table_name, + COUNT(*) as synced_records +FROM lazer_user_countries +UNION ALL +SELECT + 'lazer_user_statistics' as table_name, + COUNT(*) as synced_records +FROM lazer_user_statistics +UNION ALL +SELECT + 'lazer_user_achievements' as table_name, + COUNT(*) as synced_records +FROM lazer_user_achievements; + +-- 显示一些样本数据 +SELECT + u.id, + u.name, + lup.is_supporter, + lup.playmode, + luc.code as country_code, + lus.pp, + lus.play_count +FROM users u +LEFT JOIN lazer_user_profiles lup ON u.id = lup.user_id +LEFT JOIN lazer_user_countries luc ON u.id = luc.user_id +LEFT JOIN lazer_user_statistics lus ON u.id = lus.user_id AND lus.mode = 'osu' +ORDER BY u.id +LIMIT 10; diff --git a/requirements.txt b/requirements.txt index 622e1f9..6dc524e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,735 +1,735 @@ -# This file was autogenerated by uv via the following command: -# uv export -aiomysql==0.2.0 \ - --hash=sha256:558b9c26d580d08b8c5fd1be23c5231ce3aeff2dadad989540fee740253deb67 \ - --hash=sha256:b7c26da0daf23a5ec5e0b133c03d20657276e4eae9b73e040b72787f6f6ade0a - # via osu-lazer-api -alembic==1.16.4 \ - --hash=sha256:b05e51e8e82efc1abd14ba2af6392897e145930c3e0a2faf2b0da2f7f7fd660d \ - --hash=sha256:efab6ada0dd0fae2c92060800e0bf5c1dc26af15a10e02fb4babff164b4725e2 - # via osu-lazer-api -annotated-types==0.7.0 \ - --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ - --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 - # via pydantic -anyio==4.9.0 \ - --hash=sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028 \ - --hash=sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c - # via - # httpx - # starlette - # watchfiles -async-timeout==5.0.1 ; python_full_version < '3.11.3' \ - --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ - --hash=sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3 - # via redis -bcrypt==4.3.0 \ - --hash=sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f \ - --hash=sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d \ - --hash=sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24 \ - --hash=sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3 \ - --hash=sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c \ - --hash=sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd \ - --hash=sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f \ - --hash=sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f \ - --hash=sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d \ - --hash=sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe \ - --hash=sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231 \ - --hash=sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef \ - --hash=sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18 \ - --hash=sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f \ - --hash=sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e \ - --hash=sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732 \ - --hash=sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304 \ - --hash=sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0 \ - --hash=sha256:55a935b8e9a1d2def0626c4269db3fcd26728cbff1e84f0341465c31c4ee56d8 \ - --hash=sha256:57967b7a28d855313a963aaea51bf6df89f833db4320da458e5b3c5ab6d4c938 \ - --hash=sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62 \ - --hash=sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180 \ - --hash=sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af \ - --hash=sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669 \ - --hash=sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761 \ - --hash=sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51 \ - --hash=sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23 \ - --hash=sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09 \ - --hash=sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505 \ - --hash=sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4 \ - --hash=sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753 \ - --hash=sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59 \ - --hash=sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b \ - --hash=sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d \ - --hash=sha256:a839320bf27d474e52ef8cb16449bb2ce0ba03ca9f44daba6d93fa1d8828e48a \ - --hash=sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b \ - --hash=sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a \ - --hash=sha256:bdc6a24e754a555d7316fa4774e64c6c3997d27ed2d1964d55920c7c227bc4ce \ - --hash=sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb \ - --hash=sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb \ - --hash=sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676 \ - --hash=sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b \ - --hash=sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe \ - --hash=sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281 \ - --hash=sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1 \ - --hash=sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef \ - --hash=sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d - # via - # osu-lazer-api - # passlib -certifi==2025.7.14 \ - --hash=sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2 \ - --hash=sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995 - # via - # httpcore - # httpx -cffi==1.17.1 ; platform_python_implementation != 'PyPy' \ - --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \ - --hash=sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36 \ - --hash=sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824 \ - --hash=sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf \ - --hash=sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3 \ - --hash=sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed \ - --hash=sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1 \ - --hash=sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8 \ - --hash=sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903 \ - --hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \ - --hash=sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683 \ - --hash=sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9 \ - --hash=sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c \ - --hash=sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4 \ - --hash=sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655 \ - --hash=sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65 \ - --hash=sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41 \ - --hash=sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6 \ - --hash=sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401 \ - --hash=sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6 \ - --hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 \ - --hash=sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4 \ - --hash=sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0 \ - --hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \ - --hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \ - --hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \ - --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \ - --hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \ - --hash=sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5 \ - --hash=sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d \ - --hash=sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e \ - --hash=sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a \ - --hash=sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4 \ - --hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \ - --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b - # via cryptography -cfgv==3.4.0 \ - --hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \ - --hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560 - # via pre-commit -click==8.2.1 \ - --hash=sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202 \ - --hash=sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b - # via uvicorn -colorama==0.4.6 ; sys_platform == 'win32' \ - --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ - --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via - # click - # uvicorn -cryptography==45.0.5 \ - --hash=sha256:0027d566d65a38497bc37e0dd7c2f8ceda73597d2ac9ba93810204f56f52ebc7 \ - --hash=sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8 \ - --hash=sha256:12e55281d993a793b0e883066f590c1ae1e802e3acb67f8b442e721e475e6463 \ - --hash=sha256:1e1da5accc0c750056c556a93c3e9cb828970206c68867712ca5805e46dc806f \ - --hash=sha256:2089cc8f70a6e454601525e5bf2779e665d7865af002a5dec8d14e561002e135 \ - --hash=sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d \ - --hash=sha256:3af26738f2db354aafe492fb3869e955b12b2ef2e16908c8b9cb928128d42c57 \ - --hash=sha256:3fcfbefc4a7f332dece7272a88e410f611e79458fab97b5efe14e54fe476f4fd \ - --hash=sha256:460f8c39ba66af7db0545a8c6f2eabcbc5a5528fc1cf6c3fa9a1e44cec33385e \ - --hash=sha256:5aa1e32983d4443e310f726ee4b071ab7569f58eedfdd65e9675484a4eb67bd1 \ - --hash=sha256:6ff8728d8d890b3dda5765276d1bc6fb099252915a2cd3aff960c4c195745dd0 \ - --hash=sha256:7259038202a47fdecee7e62e0fd0b0738b6daa335354396c6ddebdbe1206af2a \ - --hash=sha256:72e76caa004ab63accdf26023fccd1d087f6d90ec6048ff33ad0445abf7f605a \ - --hash=sha256:7760c1c2e1a7084153a0f68fab76e754083b126a47d0117c9ed15e69e2103492 \ - --hash=sha256:8c4a6ff8a30e9e3d38ac0539e9a9e02540ab3f827a3394f8852432f6b0ea152e \ - --hash=sha256:9024beb59aca9d31d36fcdc1604dd9bbeed0a55bface9f1908df19178e2f116e \ - --hash=sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97 \ - --hash=sha256:91098f02ca81579c85f66df8a588c78f331ca19089763d733e34ad359f474174 \ - --hash=sha256:926c3ea71a6043921050eaa639137e13dbe7b4ab25800932a8498364fc1abec9 \ - --hash=sha256:982518cd64c54fcada9d7e5cf28eabd3ee76bd03ab18e08a48cad7e8b6f31b18 \ - --hash=sha256:9b4cf6318915dccfe218e69bbec417fdd7c7185aa7aab139a2c0beb7468c89f0 \ - --hash=sha256:ad0caded895a00261a5b4aa9af828baede54638754b51955a0ac75576b831b27 \ - --hash=sha256:b85980d1e345fe769cfc57c57db2b59cff5464ee0c045d52c0df087e926fbe63 \ - --hash=sha256:bd4c45986472694e5121084c6ebbd112aa919a25e783b87eb95953c9573906d6 \ - --hash=sha256:be97d3a19c16a9be00edf79dca949c8fa7eff621763666a145f9f9535a5d7f42 \ - --hash=sha256:d05a38884db2ba215218745f0781775806bde4f32e07b135348355fe8e4991d9 \ - --hash=sha256:dd420e577921c8c2d31289536c386aaa30140b473835e97f83bc71ea9d2baf2d \ - --hash=sha256:e357286c1b76403dd384d938f93c46b2b058ed4dfcdce64a770f0537ed3feb6f \ - --hash=sha256:e6c00130ed423201c5bc5544c23359141660b07999ad82e34e7bb8f882bb78e0 \ - --hash=sha256:e74d30ec9c7cb2f404af331d5b4099a9b322a8a6b25c4632755c8757345baac5 \ - --hash=sha256:f3562c2f23c612f2e4a6964a61d942f891d29ee320edb62ff48ffb99f3de9ae8 - # via - # osu-lazer-api - # python-jose -distlib==0.4.0 \ - --hash=sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 \ - --hash=sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d - # via virtualenv -dnspython==2.7.0 \ - --hash=sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86 \ - --hash=sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1 - # via email-validator -ecdsa==0.19.1 \ - --hash=sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3 \ - --hash=sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61 - # via python-jose -email-validator==2.2.0 \ - --hash=sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631 \ - --hash=sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7 - # via pydantic -fastapi==0.116.1 \ - --hash=sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565 \ - --hash=sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143 - # via osu-lazer-api -filelock==3.18.0 \ - --hash=sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2 \ - --hash=sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de - # via virtualenv -greenlet==3.2.3 ; (python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64') \ - --hash=sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36 \ - --hash=sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892 \ - --hash=sha256:0921ac4ea42a5315d3446120ad48f90c3a6b9bb93dd9b3cf4e4d84a66e42de83 \ - --hash=sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688 \ - --hash=sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d \ - --hash=sha256:29e184536ba333003540790ba29829ac14bb645514fbd7e32af331e8202a62a5 \ - --hash=sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b \ - --hash=sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb \ - --hash=sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86 \ - --hash=sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c \ - --hash=sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad \ - --hash=sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95 \ - --hash=sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3 \ - --hash=sha256:592c12fb1165be74592f5de0d70f82bc5ba552ac44800d632214b76089945147 \ - --hash=sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb \ - --hash=sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849 \ - --hash=sha256:751261fc5ad7b6705f5f76726567375bb2104a059454e0226e1eef6c756748ba \ - --hash=sha256:784ae58bba89fa1fa5733d170d42486580cab9decda3484779f4759345b29822 \ - --hash=sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97 \ - --hash=sha256:83a8761c75312361aa2b5b903b79da97f13f556164a7dd2d5448655425bd4c34 \ - --hash=sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141 \ - --hash=sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3 \ - --hash=sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0 \ - --hash=sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b \ - --hash=sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365 \ - --hash=sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a \ - --hash=sha256:93c0bb79844a367782ec4f429d07589417052e621aa39a5ac1fb99c5aa308edc \ - --hash=sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163 \ - --hash=sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef \ - --hash=sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d \ - --hash=sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264 \ - --hash=sha256:c667c0bf9d406b77a15c924ef3285e1e05250948001220368e039b6aa5b5034b \ - --hash=sha256:d2971d93bb99e05f8c2c0c2f4aa9484a18d98c4c3bd3c62b65b7e6ae33dfcfaf \ - --hash=sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a \ - --hash=sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728 - # via sqlalchemy -h11==0.16.0 \ - --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ - --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 - # via - # httpcore - # uvicorn -httpcore==1.0.9 \ - --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ - --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 - # via httpx -httptools==0.6.4 \ - --hash=sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a \ - --hash=sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2 \ - --hash=sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17 \ - --hash=sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8 \ - --hash=sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3 \ - --hash=sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5 \ - --hash=sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721 \ - --hash=sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636 \ - --hash=sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0 \ - --hash=sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071 \ - --hash=sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c \ - --hash=sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1 \ - --hash=sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44 \ - --hash=sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083 \ - --hash=sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660 \ - --hash=sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988 \ - --hash=sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970 \ - --hash=sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2 \ - --hash=sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81 \ - --hash=sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069 \ - --hash=sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975 \ - --hash=sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f - # via uvicorn -httpx==0.28.1 \ - --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ - --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad - # via osu-lazer-api -identify==2.6.12 \ - --hash=sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2 \ - --hash=sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6 - # via pre-commit -idna==3.10 \ - --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ - --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 - # via - # anyio - # email-validator - # httpx -mako==1.3.10 \ - --hash=sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28 \ - --hash=sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59 - # via alembic -markupsafe==3.0.2 \ - --hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \ - --hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \ - --hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \ - --hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \ - --hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \ - --hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \ - --hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \ - --hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \ - --hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \ - --hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \ - --hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \ - --hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \ - --hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \ - --hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \ - --hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \ - --hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \ - --hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \ - --hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \ - --hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \ - --hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \ - --hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \ - --hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \ - --hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \ - --hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \ - --hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \ - --hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \ - --hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \ - --hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \ - --hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \ - --hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \ - --hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \ - --hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \ - --hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \ - --hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \ - --hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \ - --hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \ - --hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \ - --hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \ - --hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \ - --hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \ - --hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 - # via mako -msgpack==1.1.1 \ - --hash=sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8 \ - --hash=sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157 \ - --hash=sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d \ - --hash=sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0 \ - --hash=sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5 \ - --hash=sha256:4a28e8072ae9779f20427af07f53bbb8b4aa81151054e882aee333b158da8752 \ - --hash=sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac \ - --hash=sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef \ - --hash=sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323 \ - --hash=sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4 \ - --hash=sha256:5fd1b58e1431008a57247d6e7cc4faa41c3607e8e7d4aaf81f7c29ea013cb458 \ - --hash=sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69 \ - --hash=sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce \ - --hash=sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558 \ - --hash=sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd \ - --hash=sha256:7da8831f9a0fdb526621ba09a281fadc58ea12701bc709e7b8cbc362feabc295 \ - --hash=sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c \ - --hash=sha256:88daaf7d146e48ec71212ce21109b66e06a98e5e44dca47d853cbfe171d6c8d2 \ - --hash=sha256:8a8b10fdb84a43e50d38057b06901ec9da52baac6983d3f709d8507f3889d43f \ - --hash=sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9 \ - --hash=sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a \ - --hash=sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0 \ - --hash=sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a \ - --hash=sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238 \ - --hash=sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7 \ - --hash=sha256:ba0c325c3f485dc54ec298d8b024e134acf07c10d494ffa24373bea729acf704 \ - --hash=sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a \ - --hash=sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c \ - --hash=sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b \ - --hash=sha256:d8b55ea20dc59b181d3f47103f113e6f28a5e1c89fd5b67b9140edb442ab67f2 \ - --hash=sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b - # via - # msgpack-types - # osu-lazer-api -msgpack-types==0.5.0 \ - --hash=sha256:8b633ed75e495a555fa0615843de559a74b1d176828d59bb393d266e51f6bda7 \ - --hash=sha256:aebd1b8da23f8f9966d66ebb1a43bd261b95751c6a267bd21a124d2ccac84201 -nodeenv==1.9.1 \ - --hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \ - --hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9 - # via pre-commit -passlib==1.7.4 \ - --hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \ - --hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04 - # via osu-lazer-api -platformdirs==4.3.8 \ - --hash=sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc \ - --hash=sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4 - # via virtualenv -pre-commit==4.2.0 \ - --hash=sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146 \ - --hash=sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd -pyasn1==0.6.1 \ - --hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \ - --hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034 - # via - # python-jose - # rsa -pycparser==2.22 ; platform_python_implementation != 'PyPy' \ - --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ - --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc - # via cffi -pydantic==2.11.7 \ - --hash=sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db \ - --hash=sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b - # via - # fastapi - # osu-lazer-api - # sqlmodel -pydantic-core==2.33.2 \ - --hash=sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56 \ - --hash=sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef \ - --hash=sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a \ - --hash=sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f \ - --hash=sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab \ - --hash=sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916 \ - --hash=sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf \ - --hash=sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a \ - --hash=sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7 \ - --hash=sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612 \ - --hash=sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1 \ - --hash=sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7 \ - --hash=sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a \ - --hash=sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7 \ - --hash=sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025 \ - --hash=sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849 \ - --hash=sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b \ - --hash=sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e \ - --hash=sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea \ - --hash=sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac \ - --hash=sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51 \ - --hash=sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e \ - --hash=sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162 \ - --hash=sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65 \ - --hash=sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de \ - --hash=sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc \ - --hash=sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb \ - --hash=sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef \ - --hash=sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1 \ - --hash=sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5 \ - --hash=sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88 \ - --hash=sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290 \ - --hash=sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d \ - --hash=sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc \ - --hash=sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc \ - --hash=sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30 \ - --hash=sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e \ - --hash=sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9 \ - --hash=sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9 \ - --hash=sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f \ - --hash=sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5 \ - --hash=sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab \ - --hash=sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593 \ - --hash=sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1 \ - --hash=sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f \ - --hash=sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8 \ - --hash=sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf \ - --hash=sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246 \ - --hash=sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9 \ - --hash=sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011 \ - --hash=sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6 \ - --hash=sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8 \ - --hash=sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2 \ - --hash=sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6 \ - --hash=sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d - # via pydantic -pymysql==1.1.1 \ - --hash=sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c \ - --hash=sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0 - # via aiomysql -python-dotenv==1.1.1 \ - --hash=sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc \ - --hash=sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab - # via - # osu-lazer-api - # uvicorn -python-jose==3.5.0 \ - --hash=sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771 \ - --hash=sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b - # via osu-lazer-api -python-multipart==0.0.20 \ - --hash=sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104 \ - --hash=sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13 - # via osu-lazer-api -pyyaml==6.0.2 \ - --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ - --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ - --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ - --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ - --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ - --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ - --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ - --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ - --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ - --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ - --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ - --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ - --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ - --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ - --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ - --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ - --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ - --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ - --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ - --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ - --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ - --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ - --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ - --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ - --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ - --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ - --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ - --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 - # via - # pre-commit - # uvicorn -redis==6.2.0 \ - --hash=sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e \ - --hash=sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977 - # via osu-lazer-api -rsa==4.9.1 \ - --hash=sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762 \ - --hash=sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75 - # via python-jose -ruff==0.12.4 \ - --hash=sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e \ - --hash=sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b \ - --hash=sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873 \ - --hash=sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57 \ - --hash=sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184 \ - --hash=sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1 \ - --hash=sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93 \ - --hash=sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442 \ - --hash=sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb \ - --hash=sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c \ - --hash=sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e \ - --hash=sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a \ - --hash=sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb \ - --hash=sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3 \ - --hash=sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a \ - --hash=sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045 \ - --hash=sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586 \ - --hash=sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a -six==1.17.0 \ - --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ - --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 - # via ecdsa -sniffio==1.3.1 \ - --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ - --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc - # via anyio -sqlalchemy==2.0.41 \ - --hash=sha256:03968a349db483936c249f4d9cd14ff2c296adfa1290b660ba6516f973139582 \ - --hash=sha256:293cd444d82b18da48c9f71cd7005844dbbd06ca19be1ccf6779154439eec0b8 \ - --hash=sha256:32f9dc8c44acdee06c8fc6440db9eae8b4af8b01e4b1aee7bdd7241c22edff4f \ - --hash=sha256:3d3549fc3e40667ec7199033a4e40a2f669898a00a7b18a931d3efb4c7900504 \ - --hash=sha256:41836fe661cc98abfae476e14ba1906220f92c4e528771a8a3ae6a151242d2ae \ - --hash=sha256:4eeb195cdedaf17aab6b247894ff2734dcead6c08f748e617bfe05bd5a218443 \ - --hash=sha256:4f67766965996e63bb46cfbf2ce5355fc32d9dd3b8ad7e536a920ff9ee422e23 \ - --hash=sha256:57df5dc6fdb5ed1a88a1ed2195fd31927e705cad62dedd86b46972752a80f576 \ - --hash=sha256:598d9ebc1e796431bbd068e41e4de4dc34312b7aa3292571bb3674a0cb415dd1 \ - --hash=sha256:5b14e97886199c1f52c14629c11d90c11fbb09e9334fa7bb5f6d068d9ced0ce0 \ - --hash=sha256:6145afea51ff0af7f2564a05fa95eb46f542919e6523729663a5d285ecb3cf5e \ - --hash=sha256:6375cd674fe82d7aa9816d1cb96ec592bac1726c11e0cafbf40eeee9a4516b5f \ - --hash=sha256:81f413674d85cfd0dfcd6512e10e0f33c19c21860342a4890c3a2b59479929f9 \ - --hash=sha256:82ca366a844eb551daff9d2e6e7a9e5e76d2612c8564f58db6c19a726869c1df \ - --hash=sha256:90c11ceb9a1f482c752a71f203a81858625d8df5746d787a4786bca4ffdf71c6 \ - --hash=sha256:911cc493ebd60de5f285bcae0491a60b4f2a9f0f5c270edd1c4dbaef7a38fc04 \ - --hash=sha256:9f8c9fdd15a55d9465e590a402f42082705d66b05afc3ffd2d2eb3c6ba919560 \ - --hash=sha256:a104c5694dfd2d864a6f91b0956eb5d5883234119cb40010115fd45a16da5e70 \ - --hash=sha256:a62448526dd9ed3e3beedc93df9bb6b55a436ed1474db31a2af13b313a70a7e1 \ - --hash=sha256:a8808d5cf866c781150d36a3c8eb3adccfa41a8105d031bf27e92c251e3969d6 \ - --hash=sha256:b46fa6eae1cd1c20e6e6f44e19984d438b6b2d8616d21d783d150df714f44078 \ - --hash=sha256:bfc9064f6658a3d1cadeaa0ba07570b83ce6801a1314985bf98ec9b95d74e15f \ - --hash=sha256:c153265408d18de4cc5ded1941dcd8315894572cddd3c58df5d5b5705b3fa28d \ - --hash=sha256:d4ae769b9c1c7757e4ccce94b0641bc203bbdf43ba7a2413ab2523d8d047d8dc \ - --hash=sha256:dc56c9788617b8964ad02e8fcfeed4001c1f8ba91a9e1f31483c0dffb207002a \ - --hash=sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9 - # via - # alembic - # osu-lazer-api - # sqlmodel -sqlmodel==0.0.24 \ - --hash=sha256:6778852f09370908985b667d6a3ab92910d0d5ec88adcaf23dbc242715ff7193 \ - --hash=sha256:cc5c7613c1a5533c9c7867e1aab2fd489a76c9e8a061984da11b4e613c182423 - # via osu-lazer-api -starlette==0.47.2 \ - --hash=sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8 \ - --hash=sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b - # via fastapi -typing-extensions==4.14.1 \ - --hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \ - --hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76 - # via - # alembic - # anyio - # fastapi - # msgpack-types - # pydantic - # pydantic-core - # sqlalchemy - # starlette - # typing-inspection -typing-inspection==0.4.1 \ - --hash=sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51 \ - --hash=sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28 - # via pydantic -uvicorn==0.35.0 \ - --hash=sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a \ - --hash=sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01 - # via osu-lazer-api -uvloop==0.21.0 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' \ - --hash=sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0 \ - --hash=sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f \ - --hash=sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c \ - --hash=sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3 \ - --hash=sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d \ - --hash=sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb \ - --hash=sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6 \ - --hash=sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af \ - --hash=sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc \ - --hash=sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb \ - --hash=sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553 \ - --hash=sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e \ - --hash=sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6 \ - --hash=sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d \ - --hash=sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc \ - --hash=sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281 \ - --hash=sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8 \ - --hash=sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816 \ - --hash=sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2 - # via uvicorn -virtualenv==20.32.0 \ - --hash=sha256:2c310aecb62e5aa1b06103ed7c2977b81e042695de2697d01017ff0f1034af56 \ - --hash=sha256:886bf75cadfdc964674e6e33eb74d787dff31ca314ceace03ca5810620f4ecf0 - # via pre-commit -watchfiles==1.1.0 \ - --hash=sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a \ - --hash=sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6 \ - --hash=sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3 \ - --hash=sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7 \ - --hash=sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a \ - --hash=sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259 \ - --hash=sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297 \ - --hash=sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1 \ - --hash=sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a \ - --hash=sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b \ - --hash=sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb \ - --hash=sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b \ - --hash=sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339 \ - --hash=sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9 \ - --hash=sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb \ - --hash=sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4 \ - --hash=sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c \ - --hash=sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8 \ - --hash=sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12 \ - --hash=sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30 \ - --hash=sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0 \ - --hash=sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c \ - --hash=sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5 \ - --hash=sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb \ - --hash=sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2 \ - --hash=sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e \ - --hash=sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575 \ - --hash=sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f \ - --hash=sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f \ - --hash=sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d \ - --hash=sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92 \ - --hash=sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b \ - --hash=sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b \ - --hash=sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd \ - --hash=sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4 \ - --hash=sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792 \ - --hash=sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0 \ - --hash=sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297 \ - --hash=sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef \ - --hash=sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179 \ - --hash=sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee \ - --hash=sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011 \ - --hash=sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e \ - --hash=sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf \ - --hash=sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db \ - --hash=sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20 \ - --hash=sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4 \ - --hash=sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575 \ - --hash=sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa \ - --hash=sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c \ - --hash=sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f \ - --hash=sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018 \ - --hash=sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2 \ - --hash=sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d \ - --hash=sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd \ - --hash=sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47 \ - --hash=sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb \ - --hash=sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147 \ - --hash=sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8 \ - --hash=sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670 \ - --hash=sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c \ - --hash=sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5 \ - --hash=sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e \ - --hash=sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e \ - --hash=sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8 \ - --hash=sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895 \ - --hash=sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7 \ - --hash=sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432 \ - --hash=sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc \ - --hash=sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633 \ - --hash=sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f \ - --hash=sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77 \ - --hash=sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12 \ - --hash=sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f - # via uvicorn -websockets==15.0.1 \ - --hash=sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2 \ - --hash=sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5 \ - --hash=sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8 \ - --hash=sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85 \ - --hash=sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375 \ - --hash=sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065 \ - --hash=sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597 \ - --hash=sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f \ - --hash=sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3 \ - --hash=sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf \ - --hash=sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4 \ - --hash=sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665 \ - --hash=sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22 \ - --hash=sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675 \ - --hash=sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4 \ - --hash=sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65 \ - --hash=sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792 \ - --hash=sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57 \ - --hash=sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3 \ - --hash=sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151 \ - --hash=sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d \ - --hash=sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431 \ - --hash=sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee \ - --hash=sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413 \ - --hash=sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8 \ - --hash=sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa \ - --hash=sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9 \ - --hash=sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905 \ - --hash=sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe \ - --hash=sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562 \ - --hash=sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561 \ - --hash=sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215 \ - --hash=sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931 \ - --hash=sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f \ - --hash=sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7 - # via uvicorn +# This file was autogenerated by uv via the following command: +# uv export +aiomysql==0.2.0 \ + --hash=sha256:558b9c26d580d08b8c5fd1be23c5231ce3aeff2dadad989540fee740253deb67 \ + --hash=sha256:b7c26da0daf23a5ec5e0b133c03d20657276e4eae9b73e040b72787f6f6ade0a + # via osu-lazer-api +alembic==1.16.4 \ + --hash=sha256:b05e51e8e82efc1abd14ba2af6392897e145930c3e0a2faf2b0da2f7f7fd660d \ + --hash=sha256:efab6ada0dd0fae2c92060800e0bf5c1dc26af15a10e02fb4babff164b4725e2 + # via osu-lazer-api +annotated-types==0.7.0 \ + --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ + --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 + # via pydantic +anyio==4.9.0 \ + --hash=sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028 \ + --hash=sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c + # via + # httpx + # starlette + # watchfiles +async-timeout==5.0.1 ; python_full_version < '3.11.3' \ + --hash=sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c \ + --hash=sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3 + # via redis +bcrypt==4.3.0 \ + --hash=sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f \ + --hash=sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d \ + --hash=sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24 \ + --hash=sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3 \ + --hash=sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c \ + --hash=sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd \ + --hash=sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f \ + --hash=sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f \ + --hash=sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d \ + --hash=sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe \ + --hash=sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231 \ + --hash=sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef \ + --hash=sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18 \ + --hash=sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f \ + --hash=sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e \ + --hash=sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732 \ + --hash=sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304 \ + --hash=sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0 \ + --hash=sha256:55a935b8e9a1d2def0626c4269db3fcd26728cbff1e84f0341465c31c4ee56d8 \ + --hash=sha256:57967b7a28d855313a963aaea51bf6df89f833db4320da458e5b3c5ab6d4c938 \ + --hash=sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62 \ + --hash=sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180 \ + --hash=sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af \ + --hash=sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669 \ + --hash=sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761 \ + --hash=sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51 \ + --hash=sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23 \ + --hash=sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09 \ + --hash=sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505 \ + --hash=sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4 \ + --hash=sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753 \ + --hash=sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59 \ + --hash=sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b \ + --hash=sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d \ + --hash=sha256:a839320bf27d474e52ef8cb16449bb2ce0ba03ca9f44daba6d93fa1d8828e48a \ + --hash=sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b \ + --hash=sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a \ + --hash=sha256:bdc6a24e754a555d7316fa4774e64c6c3997d27ed2d1964d55920c7c227bc4ce \ + --hash=sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb \ + --hash=sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb \ + --hash=sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676 \ + --hash=sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b \ + --hash=sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe \ + --hash=sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281 \ + --hash=sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1 \ + --hash=sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef \ + --hash=sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d + # via + # osu-lazer-api + # passlib +certifi==2025.7.14 \ + --hash=sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2 \ + --hash=sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995 + # via + # httpcore + # httpx +cffi==1.17.1 ; platform_python_implementation != 'PyPy' \ + --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \ + --hash=sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36 \ + --hash=sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824 \ + --hash=sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf \ + --hash=sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3 \ + --hash=sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed \ + --hash=sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1 \ + --hash=sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8 \ + --hash=sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903 \ + --hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \ + --hash=sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683 \ + --hash=sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9 \ + --hash=sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c \ + --hash=sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4 \ + --hash=sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655 \ + --hash=sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65 \ + --hash=sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41 \ + --hash=sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6 \ + --hash=sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401 \ + --hash=sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6 \ + --hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 \ + --hash=sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4 \ + --hash=sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0 \ + --hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \ + --hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \ + --hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \ + --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \ + --hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \ + --hash=sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5 \ + --hash=sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d \ + --hash=sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e \ + --hash=sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a \ + --hash=sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4 \ + --hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \ + --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b + # via cryptography +cfgv==3.4.0 \ + --hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \ + --hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560 + # via pre-commit +click==8.2.1 \ + --hash=sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202 \ + --hash=sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b + # via uvicorn +colorama==0.4.6 ; sys_platform == 'win32' \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via + # click + # uvicorn +cryptography==45.0.5 \ + --hash=sha256:0027d566d65a38497bc37e0dd7c2f8ceda73597d2ac9ba93810204f56f52ebc7 \ + --hash=sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8 \ + --hash=sha256:12e55281d993a793b0e883066f590c1ae1e802e3acb67f8b442e721e475e6463 \ + --hash=sha256:1e1da5accc0c750056c556a93c3e9cb828970206c68867712ca5805e46dc806f \ + --hash=sha256:2089cc8f70a6e454601525e5bf2779e665d7865af002a5dec8d14e561002e135 \ + --hash=sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d \ + --hash=sha256:3af26738f2db354aafe492fb3869e955b12b2ef2e16908c8b9cb928128d42c57 \ + --hash=sha256:3fcfbefc4a7f332dece7272a88e410f611e79458fab97b5efe14e54fe476f4fd \ + --hash=sha256:460f8c39ba66af7db0545a8c6f2eabcbc5a5528fc1cf6c3fa9a1e44cec33385e \ + --hash=sha256:5aa1e32983d4443e310f726ee4b071ab7569f58eedfdd65e9675484a4eb67bd1 \ + --hash=sha256:6ff8728d8d890b3dda5765276d1bc6fb099252915a2cd3aff960c4c195745dd0 \ + --hash=sha256:7259038202a47fdecee7e62e0fd0b0738b6daa335354396c6ddebdbe1206af2a \ + --hash=sha256:72e76caa004ab63accdf26023fccd1d087f6d90ec6048ff33ad0445abf7f605a \ + --hash=sha256:7760c1c2e1a7084153a0f68fab76e754083b126a47d0117c9ed15e69e2103492 \ + --hash=sha256:8c4a6ff8a30e9e3d38ac0539e9a9e02540ab3f827a3394f8852432f6b0ea152e \ + --hash=sha256:9024beb59aca9d31d36fcdc1604dd9bbeed0a55bface9f1908df19178e2f116e \ + --hash=sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97 \ + --hash=sha256:91098f02ca81579c85f66df8a588c78f331ca19089763d733e34ad359f474174 \ + --hash=sha256:926c3ea71a6043921050eaa639137e13dbe7b4ab25800932a8498364fc1abec9 \ + --hash=sha256:982518cd64c54fcada9d7e5cf28eabd3ee76bd03ab18e08a48cad7e8b6f31b18 \ + --hash=sha256:9b4cf6318915dccfe218e69bbec417fdd7c7185aa7aab139a2c0beb7468c89f0 \ + --hash=sha256:ad0caded895a00261a5b4aa9af828baede54638754b51955a0ac75576b831b27 \ + --hash=sha256:b85980d1e345fe769cfc57c57db2b59cff5464ee0c045d52c0df087e926fbe63 \ + --hash=sha256:bd4c45986472694e5121084c6ebbd112aa919a25e783b87eb95953c9573906d6 \ + --hash=sha256:be97d3a19c16a9be00edf79dca949c8fa7eff621763666a145f9f9535a5d7f42 \ + --hash=sha256:d05a38884db2ba215218745f0781775806bde4f32e07b135348355fe8e4991d9 \ + --hash=sha256:dd420e577921c8c2d31289536c386aaa30140b473835e97f83bc71ea9d2baf2d \ + --hash=sha256:e357286c1b76403dd384d938f93c46b2b058ed4dfcdce64a770f0537ed3feb6f \ + --hash=sha256:e6c00130ed423201c5bc5544c23359141660b07999ad82e34e7bb8f882bb78e0 \ + --hash=sha256:e74d30ec9c7cb2f404af331d5b4099a9b322a8a6b25c4632755c8757345baac5 \ + --hash=sha256:f3562c2f23c612f2e4a6964a61d942f891d29ee320edb62ff48ffb99f3de9ae8 + # via + # osu-lazer-api + # python-jose +distlib==0.4.0 \ + --hash=sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 \ + --hash=sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d + # via virtualenv +dnspython==2.7.0 \ + --hash=sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86 \ + --hash=sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1 + # via email-validator +ecdsa==0.19.1 \ + --hash=sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3 \ + --hash=sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61 + # via python-jose +email-validator==2.2.0 \ + --hash=sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631 \ + --hash=sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7 + # via pydantic +fastapi==0.116.1 \ + --hash=sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565 \ + --hash=sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143 + # via osu-lazer-api +filelock==3.18.0 \ + --hash=sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2 \ + --hash=sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de + # via virtualenv +greenlet==3.2.3 ; (python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64') \ + --hash=sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36 \ + --hash=sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892 \ + --hash=sha256:0921ac4ea42a5315d3446120ad48f90c3a6b9bb93dd9b3cf4e4d84a66e42de83 \ + --hash=sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688 \ + --hash=sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d \ + --hash=sha256:29e184536ba333003540790ba29829ac14bb645514fbd7e32af331e8202a62a5 \ + --hash=sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b \ + --hash=sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb \ + --hash=sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86 \ + --hash=sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c \ + --hash=sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad \ + --hash=sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95 \ + --hash=sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3 \ + --hash=sha256:592c12fb1165be74592f5de0d70f82bc5ba552ac44800d632214b76089945147 \ + --hash=sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb \ + --hash=sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849 \ + --hash=sha256:751261fc5ad7b6705f5f76726567375bb2104a059454e0226e1eef6c756748ba \ + --hash=sha256:784ae58bba89fa1fa5733d170d42486580cab9decda3484779f4759345b29822 \ + --hash=sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97 \ + --hash=sha256:83a8761c75312361aa2b5b903b79da97f13f556164a7dd2d5448655425bd4c34 \ + --hash=sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141 \ + --hash=sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3 \ + --hash=sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0 \ + --hash=sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b \ + --hash=sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365 \ + --hash=sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a \ + --hash=sha256:93c0bb79844a367782ec4f429d07589417052e621aa39a5ac1fb99c5aa308edc \ + --hash=sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163 \ + --hash=sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef \ + --hash=sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d \ + --hash=sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264 \ + --hash=sha256:c667c0bf9d406b77a15c924ef3285e1e05250948001220368e039b6aa5b5034b \ + --hash=sha256:d2971d93bb99e05f8c2c0c2f4aa9484a18d98c4c3bd3c62b65b7e6ae33dfcfaf \ + --hash=sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a \ + --hash=sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728 + # via sqlalchemy +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via + # httpcore + # uvicorn +httpcore==1.0.9 \ + --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ + --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 + # via httpx +httptools==0.6.4 \ + --hash=sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a \ + --hash=sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2 \ + --hash=sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17 \ + --hash=sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8 \ + --hash=sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3 \ + --hash=sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5 \ + --hash=sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721 \ + --hash=sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636 \ + --hash=sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0 \ + --hash=sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071 \ + --hash=sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c \ + --hash=sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1 \ + --hash=sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44 \ + --hash=sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083 \ + --hash=sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660 \ + --hash=sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988 \ + --hash=sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970 \ + --hash=sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2 \ + --hash=sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81 \ + --hash=sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069 \ + --hash=sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975 \ + --hash=sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f + # via uvicorn +httpx==0.28.1 \ + --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ + --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + # via osu-lazer-api +identify==2.6.12 \ + --hash=sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2 \ + --hash=sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6 + # via pre-commit +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 + # via + # anyio + # email-validator + # httpx +mako==1.3.10 \ + --hash=sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28 \ + --hash=sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59 + # via alembic +markupsafe==3.0.2 \ + --hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \ + --hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \ + --hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \ + --hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \ + --hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \ + --hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \ + --hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \ + --hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \ + --hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \ + --hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \ + --hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \ + --hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \ + --hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \ + --hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \ + --hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \ + --hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \ + --hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \ + --hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \ + --hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \ + --hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \ + --hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \ + --hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \ + --hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \ + --hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \ + --hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \ + --hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \ + --hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \ + --hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \ + --hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \ + --hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \ + --hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \ + --hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \ + --hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \ + --hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \ + --hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \ + --hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \ + --hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \ + --hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \ + --hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \ + --hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \ + --hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 + # via mako +msgpack==1.1.1 \ + --hash=sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8 \ + --hash=sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157 \ + --hash=sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d \ + --hash=sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0 \ + --hash=sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5 \ + --hash=sha256:4a28e8072ae9779f20427af07f53bbb8b4aa81151054e882aee333b158da8752 \ + --hash=sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac \ + --hash=sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef \ + --hash=sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323 \ + --hash=sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4 \ + --hash=sha256:5fd1b58e1431008a57247d6e7cc4faa41c3607e8e7d4aaf81f7c29ea013cb458 \ + --hash=sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69 \ + --hash=sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce \ + --hash=sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558 \ + --hash=sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd \ + --hash=sha256:7da8831f9a0fdb526621ba09a281fadc58ea12701bc709e7b8cbc362feabc295 \ + --hash=sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c \ + --hash=sha256:88daaf7d146e48ec71212ce21109b66e06a98e5e44dca47d853cbfe171d6c8d2 \ + --hash=sha256:8a8b10fdb84a43e50d38057b06901ec9da52baac6983d3f709d8507f3889d43f \ + --hash=sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9 \ + --hash=sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a \ + --hash=sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0 \ + --hash=sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a \ + --hash=sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238 \ + --hash=sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7 \ + --hash=sha256:ba0c325c3f485dc54ec298d8b024e134acf07c10d494ffa24373bea729acf704 \ + --hash=sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a \ + --hash=sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c \ + --hash=sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b \ + --hash=sha256:d8b55ea20dc59b181d3f47103f113e6f28a5e1c89fd5b67b9140edb442ab67f2 \ + --hash=sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b + # via + # msgpack-types + # osu-lazer-api +msgpack-types==0.5.0 \ + --hash=sha256:8b633ed75e495a555fa0615843de559a74b1d176828d59bb393d266e51f6bda7 \ + --hash=sha256:aebd1b8da23f8f9966d66ebb1a43bd261b95751c6a267bd21a124d2ccac84201 +nodeenv==1.9.1 \ + --hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \ + --hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9 + # via pre-commit +passlib==1.7.4 \ + --hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \ + --hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04 + # via osu-lazer-api +platformdirs==4.3.8 \ + --hash=sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc \ + --hash=sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4 + # via virtualenv +pre-commit==4.2.0 \ + --hash=sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146 \ + --hash=sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd +pyasn1==0.6.1 \ + --hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \ + --hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034 + # via + # python-jose + # rsa +pycparser==2.22 ; platform_python_implementation != 'PyPy' \ + --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ + --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc + # via cffi +pydantic==2.11.7 \ + --hash=sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db \ + --hash=sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b + # via + # fastapi + # osu-lazer-api + # sqlmodel +pydantic-core==2.33.2 \ + --hash=sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56 \ + --hash=sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef \ + --hash=sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a \ + --hash=sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f \ + --hash=sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab \ + --hash=sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916 \ + --hash=sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf \ + --hash=sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a \ + --hash=sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7 \ + --hash=sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612 \ + --hash=sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1 \ + --hash=sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7 \ + --hash=sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a \ + --hash=sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7 \ + --hash=sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025 \ + --hash=sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849 \ + --hash=sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b \ + --hash=sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e \ + --hash=sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea \ + --hash=sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac \ + --hash=sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51 \ + --hash=sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e \ + --hash=sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162 \ + --hash=sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65 \ + --hash=sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de \ + --hash=sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc \ + --hash=sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb \ + --hash=sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef \ + --hash=sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1 \ + --hash=sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5 \ + --hash=sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88 \ + --hash=sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290 \ + --hash=sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d \ + --hash=sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc \ + --hash=sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc \ + --hash=sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30 \ + --hash=sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e \ + --hash=sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9 \ + --hash=sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9 \ + --hash=sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f \ + --hash=sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5 \ + --hash=sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab \ + --hash=sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593 \ + --hash=sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1 \ + --hash=sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f \ + --hash=sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8 \ + --hash=sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf \ + --hash=sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246 \ + --hash=sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9 \ + --hash=sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011 \ + --hash=sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6 \ + --hash=sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8 \ + --hash=sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2 \ + --hash=sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6 \ + --hash=sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d + # via pydantic +pymysql==1.1.1 \ + --hash=sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c \ + --hash=sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0 + # via aiomysql +python-dotenv==1.1.1 \ + --hash=sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc \ + --hash=sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab + # via + # osu-lazer-api + # uvicorn +python-jose==3.5.0 \ + --hash=sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771 \ + --hash=sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b + # via osu-lazer-api +python-multipart==0.0.20 \ + --hash=sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104 \ + --hash=sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13 + # via osu-lazer-api +pyyaml==6.0.2 \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via + # pre-commit + # uvicorn +redis==6.2.0 \ + --hash=sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e \ + --hash=sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977 + # via osu-lazer-api +rsa==4.9.1 \ + --hash=sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762 \ + --hash=sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75 + # via python-jose +ruff==0.12.4 \ + --hash=sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e \ + --hash=sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b \ + --hash=sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873 \ + --hash=sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57 \ + --hash=sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184 \ + --hash=sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1 \ + --hash=sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93 \ + --hash=sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442 \ + --hash=sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb \ + --hash=sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c \ + --hash=sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e \ + --hash=sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a \ + --hash=sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb \ + --hash=sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3 \ + --hash=sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a \ + --hash=sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045 \ + --hash=sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586 \ + --hash=sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via ecdsa +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via anyio +sqlalchemy==2.0.41 \ + --hash=sha256:03968a349db483936c249f4d9cd14ff2c296adfa1290b660ba6516f973139582 \ + --hash=sha256:293cd444d82b18da48c9f71cd7005844dbbd06ca19be1ccf6779154439eec0b8 \ + --hash=sha256:32f9dc8c44acdee06c8fc6440db9eae8b4af8b01e4b1aee7bdd7241c22edff4f \ + --hash=sha256:3d3549fc3e40667ec7199033a4e40a2f669898a00a7b18a931d3efb4c7900504 \ + --hash=sha256:41836fe661cc98abfae476e14ba1906220f92c4e528771a8a3ae6a151242d2ae \ + --hash=sha256:4eeb195cdedaf17aab6b247894ff2734dcead6c08f748e617bfe05bd5a218443 \ + --hash=sha256:4f67766965996e63bb46cfbf2ce5355fc32d9dd3b8ad7e536a920ff9ee422e23 \ + --hash=sha256:57df5dc6fdb5ed1a88a1ed2195fd31927e705cad62dedd86b46972752a80f576 \ + --hash=sha256:598d9ebc1e796431bbd068e41e4de4dc34312b7aa3292571bb3674a0cb415dd1 \ + --hash=sha256:5b14e97886199c1f52c14629c11d90c11fbb09e9334fa7bb5f6d068d9ced0ce0 \ + --hash=sha256:6145afea51ff0af7f2564a05fa95eb46f542919e6523729663a5d285ecb3cf5e \ + --hash=sha256:6375cd674fe82d7aa9816d1cb96ec592bac1726c11e0cafbf40eeee9a4516b5f \ + --hash=sha256:81f413674d85cfd0dfcd6512e10e0f33c19c21860342a4890c3a2b59479929f9 \ + --hash=sha256:82ca366a844eb551daff9d2e6e7a9e5e76d2612c8564f58db6c19a726869c1df \ + --hash=sha256:90c11ceb9a1f482c752a71f203a81858625d8df5746d787a4786bca4ffdf71c6 \ + --hash=sha256:911cc493ebd60de5f285bcae0491a60b4f2a9f0f5c270edd1c4dbaef7a38fc04 \ + --hash=sha256:9f8c9fdd15a55d9465e590a402f42082705d66b05afc3ffd2d2eb3c6ba919560 \ + --hash=sha256:a104c5694dfd2d864a6f91b0956eb5d5883234119cb40010115fd45a16da5e70 \ + --hash=sha256:a62448526dd9ed3e3beedc93df9bb6b55a436ed1474db31a2af13b313a70a7e1 \ + --hash=sha256:a8808d5cf866c781150d36a3c8eb3adccfa41a8105d031bf27e92c251e3969d6 \ + --hash=sha256:b46fa6eae1cd1c20e6e6f44e19984d438b6b2d8616d21d783d150df714f44078 \ + --hash=sha256:bfc9064f6658a3d1cadeaa0ba07570b83ce6801a1314985bf98ec9b95d74e15f \ + --hash=sha256:c153265408d18de4cc5ded1941dcd8315894572cddd3c58df5d5b5705b3fa28d \ + --hash=sha256:d4ae769b9c1c7757e4ccce94b0641bc203bbdf43ba7a2413ab2523d8d047d8dc \ + --hash=sha256:dc56c9788617b8964ad02e8fcfeed4001c1f8ba91a9e1f31483c0dffb207002a \ + --hash=sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9 + # via + # alembic + # osu-lazer-api + # sqlmodel +sqlmodel==0.0.24 \ + --hash=sha256:6778852f09370908985b667d6a3ab92910d0d5ec88adcaf23dbc242715ff7193 \ + --hash=sha256:cc5c7613c1a5533c9c7867e1aab2fd489a76c9e8a061984da11b4e613c182423 + # via osu-lazer-api +starlette==0.47.2 \ + --hash=sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8 \ + --hash=sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b + # via fastapi +typing-extensions==4.14.1 \ + --hash=sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36 \ + --hash=sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76 + # via + # alembic + # anyio + # fastapi + # msgpack-types + # pydantic + # pydantic-core + # sqlalchemy + # starlette + # typing-inspection +typing-inspection==0.4.1 \ + --hash=sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51 \ + --hash=sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28 + # via pydantic +uvicorn==0.35.0 \ + --hash=sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a \ + --hash=sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01 + # via osu-lazer-api +uvloop==0.21.0 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' \ + --hash=sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0 \ + --hash=sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f \ + --hash=sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c \ + --hash=sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3 \ + --hash=sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d \ + --hash=sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb \ + --hash=sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6 \ + --hash=sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af \ + --hash=sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc \ + --hash=sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb \ + --hash=sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553 \ + --hash=sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e \ + --hash=sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6 \ + --hash=sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d \ + --hash=sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc \ + --hash=sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281 \ + --hash=sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8 \ + --hash=sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816 \ + --hash=sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2 + # via uvicorn +virtualenv==20.32.0 \ + --hash=sha256:2c310aecb62e5aa1b06103ed7c2977b81e042695de2697d01017ff0f1034af56 \ + --hash=sha256:886bf75cadfdc964674e6e33eb74d787dff31ca314ceace03ca5810620f4ecf0 + # via pre-commit +watchfiles==1.1.0 \ + --hash=sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a \ + --hash=sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6 \ + --hash=sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3 \ + --hash=sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7 \ + --hash=sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a \ + --hash=sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259 \ + --hash=sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297 \ + --hash=sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1 \ + --hash=sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a \ + --hash=sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b \ + --hash=sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb \ + --hash=sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b \ + --hash=sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339 \ + --hash=sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9 \ + --hash=sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb \ + --hash=sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4 \ + --hash=sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c \ + --hash=sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8 \ + --hash=sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12 \ + --hash=sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30 \ + --hash=sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0 \ + --hash=sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c \ + --hash=sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5 \ + --hash=sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb \ + --hash=sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2 \ + --hash=sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e \ + --hash=sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575 \ + --hash=sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f \ + --hash=sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f \ + --hash=sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d \ + --hash=sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92 \ + --hash=sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b \ + --hash=sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b \ + --hash=sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd \ + --hash=sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4 \ + --hash=sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792 \ + --hash=sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0 \ + --hash=sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297 \ + --hash=sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef \ + --hash=sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179 \ + --hash=sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee \ + --hash=sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011 \ + --hash=sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e \ + --hash=sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf \ + --hash=sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db \ + --hash=sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20 \ + --hash=sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4 \ + --hash=sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575 \ + --hash=sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa \ + --hash=sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c \ + --hash=sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f \ + --hash=sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018 \ + --hash=sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2 \ + --hash=sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d \ + --hash=sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd \ + --hash=sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47 \ + --hash=sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb \ + --hash=sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147 \ + --hash=sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8 \ + --hash=sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670 \ + --hash=sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c \ + --hash=sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5 \ + --hash=sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e \ + --hash=sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e \ + --hash=sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8 \ + --hash=sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895 \ + --hash=sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7 \ + --hash=sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432 \ + --hash=sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc \ + --hash=sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633 \ + --hash=sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f \ + --hash=sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77 \ + --hash=sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12 \ + --hash=sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f + # via uvicorn +websockets==15.0.1 \ + --hash=sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2 \ + --hash=sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5 \ + --hash=sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8 \ + --hash=sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85 \ + --hash=sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375 \ + --hash=sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065 \ + --hash=sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597 \ + --hash=sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f \ + --hash=sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3 \ + --hash=sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf \ + --hash=sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4 \ + --hash=sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665 \ + --hash=sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22 \ + --hash=sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675 \ + --hash=sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4 \ + --hash=sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65 \ + --hash=sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792 \ + --hash=sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57 \ + --hash=sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3 \ + --hash=sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151 \ + --hash=sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d \ + --hash=sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431 \ + --hash=sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee \ + --hash=sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413 \ + --hash=sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8 \ + --hash=sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa \ + --hash=sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9 \ + --hash=sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905 \ + --hash=sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe \ + --hash=sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562 \ + --hash=sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561 \ + --hash=sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215 \ + --hash=sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931 \ + --hash=sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f \ + --hash=sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7 + # via uvicorn