├── src ├── lib │ ├── utils │ │ ├── index.ts │ │ ├── time.ts │ │ └── utils.ts │ ├── translations │ │ ├── index.ts │ │ ├── zh-CN.json │ │ └── en.json │ ├── nezha │ │ └── api.ts │ └── telegram │ │ ├── api.ts │ │ └── handlers.ts ├── types │ ├── telegram.ts │ └── nezha.ts └── index.ts ├── .prettierrc ├── .editorconfig ├── vitest.config.mts ├── package.json ├── LICENSE ├── wrangler.toml ├── .github └── workflows │ └── deploy.yml ├── tsconfig.json ├── README.md ├── .gitignore └── bun.lock /src/lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils'; 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "semi": true, 5 | "useTabs": true 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.yml] 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /vitest.config.mts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'; 2 | 3 | export default defineWorkersConfig({ 4 | test: { 5 | poolOptions: { 6 | workers: { 7 | wrangler: { configPath: './wrangler.toml' }, 8 | }, 9 | }, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /src/lib/translations/index.ts: -------------------------------------------------------------------------------- 1 | import i18next from 'i18next'; 2 | import en from '../translations/en.json'; 3 | import zh from '../translations/zh-CN.json'; 4 | 5 | i18next.init({ 6 | lng: 'en', 7 | resources: { 8 | en: { 9 | translation: en, 10 | }, 11 | 'zh-CN': { 12 | translation: zh, 13 | }, 14 | }, 15 | }); 16 | 17 | export default i18next; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plain-nezha-bot", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "start": "wrangler dev", 9 | "test": "vitest", 10 | "cf-typegen": "wrangler types" 11 | }, 12 | "devDependencies": { 13 | "@cloudflare/vitest-pool-workers": "^0.8.34", 14 | "typescript": "^5.8.3", 15 | "vitest": "3.0.9", 16 | "wrangler": "^4.18.0" 17 | }, 18 | "dependencies": { 19 | "@vlad-yakovlev/telegram-md": "^2.0.0", 20 | "i18next": "^24.2.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/utils/time.ts: -------------------------------------------------------------------------------- 1 | export class TimeUtils { 2 | private timeZone: string; 3 | private locale: string; 4 | 5 | constructor({ timeZone, locale }: { timeZone: string; locale: string }) { 6 | this.timeZone = timeZone; 7 | this.locale = locale; 8 | } 9 | 10 | updateTimezoneLocale(tz: string, locale: string) { 11 | this.timeZone = tz; 12 | this.locale = locale; 13 | } 14 | 15 | dateStr(value?: string | number | Date): string { 16 | return (value ? new Date(value) : new Date()).toLocaleString(this.locale, { timeZone: this.timeZone }); 17 | } 18 | } 19 | 20 | export default new TimeUtils({ timeZone: 'UTC', locale: 'en-US' }); 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 uubulb 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /wrangler.toml: -------------------------------------------------------------------------------- 1 | #:schema node_modules/wrangler/config-schema.json 2 | name = "plain-nezha-bot" 3 | main = "src/index.ts" 4 | compatibility_date = "2024-12-05" 5 | compatibility_flags = ["nodejs_compat"] 6 | 7 | # Workers Logs 8 | # Docs: https://developers.cloudflare.com/workers/observability/logs/workers-logs/ 9 | # Configuration: https://developers.cloudflare.com/workers/observability/logs/workers-logs/#enable-workers-logs 10 | [observability] 11 | enabled = true 12 | 13 | # Bind a D1 database. D1 is Cloudflare’s native serverless SQL database. 14 | # Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases 15 | # [[d1_databases]] 16 | # binding = "MY_DB" 17 | # database_name = "my-database" 18 | # database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 19 | 20 | # Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. 21 | # Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#kv-namespaces 22 | [[kv_namespaces]] 23 | binding = "NZ_BOT_STORE" 24 | id = "kv_namespace_id" 25 | 26 | [vars] 27 | NZ_BASEURL="https://www.example.com" 28 | LANG="en" 29 | ENDPOINT_PATH = "/endpoint" 30 | TZ="UTC" 31 | 32 | [triggers] 33 | crons = ["*/30 * * * *"] 34 | -------------------------------------------------------------------------------- /src/lib/utils/utils.ts: -------------------------------------------------------------------------------- 1 | export function buildUrl(path: string, data?: any): string { 2 | if (!data) return path; 3 | const url = new URL(path); 4 | for (const key in data) { 5 | url.searchParams.append(key, data[key]); 6 | } 7 | return url.toString(); 8 | } 9 | 10 | export const nezhaUtils = { 11 | isOffline: (lastActive: string) => { 12 | const date = new Date(lastActive); 13 | const now = new Date(); 14 | 15 | const state = (now.getTime() - date.getTime()) / 1000 > 30 ? true : false; 16 | return state; 17 | }, 18 | formatBytes: (bytes: number) => { 19 | if (bytes === 0 || isNaN(bytes)) return '0B'; 20 | const k = 1024; 21 | const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 22 | const i = Math.floor(Math.log(bytes) / Math.log(k)); 23 | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + sizes[i]; 24 | }, 25 | formatUsage: (used: number, total: number) => { 26 | const result = (used / total) * 100; 27 | return isNaN(result) ? '0' : result.toFixed(2); 28 | }, 29 | convertSecondsToDays: (seconds: number) => { 30 | const secondsInADay = 24 * 60 * 60; 31 | return Math.ceil(seconds / secondsInADay); 32 | }, 33 | }; 34 | 35 | export function getFlagEmoji(countryCode?: string) { 36 | if (!countryCode) return '❔️'; 37 | 38 | return countryCode.toUpperCase().replace(/./g, (char) => String.fromCodePoint(127397 + char.charCodeAt(0))); 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Set up Bun 17 | uses: oven-sh/setup-bun@v1 18 | with: 19 | bun-version: 'latest' 20 | 21 | - name: Install dependencies 22 | run: bun install 23 | 24 | - name: Deploy 25 | uses: cloudflare/wrangler-action@v3 26 | with: 27 | apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} 28 | preCommands: | 29 | sed -i 's/kv_namespace_id/${{ secrets.KV_ID }}/g' wrangler.toml 30 | command: deploy --var NZ_BASEURL:${{ secrets.NZ_BASEURL }} LANG:${{ secrets.LANG }} TZ:${{ secrets.TZ }} ENDPOINT_PATH:${{ secrets.ENDPOINT_PATH }} 31 | secrets: | 32 | TELEGRAM_BOT_TOKEN 33 | TELEGRAM_SECRET 34 | TELEGRAM_UID 35 | PASSWORD 36 | NZ_USERNAME 37 | NZ_PASSWORD 38 | quiet: true 39 | env: 40 | TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} 41 | TELEGRAM_SECRET: ${{ secrets.TELEGRAM_SECRET }} 42 | TELEGRAM_UID: ${{ secrets.TELEGRAM_UID }} 43 | PASSWORD: ${{ secrets.PASSWORD }} 44 | NZ_USERNAME: ${{ secrets.NZ_USERNAME }} 45 | NZ_PASSWORD: ${{ secrets.NZ_PASSWORD }} 46 | -------------------------------------------------------------------------------- /src/lib/translations/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "Print help messages": "打印帮助信息", 3 | "Print server information (with id)": "打印服务器信息(ID)", 4 | "Print server information (with server name)": "打印服务器信息(服务器名称)", 5 | "Print server overview": "打印服务器概览", 6 | "Print monitor info": "打印监控信息", 7 | "Print cycle transfer info": "打印周期流量信息", 8 | "Available commands": "可用命令", 9 | "The server name is not valid.": "服务器名称无效。", 10 | "The service name is not valid.": "服务名称无效。", 11 | "No cycle transfer data available.": "没有周期流量数据可用。", 12 | "No service matches.": "没有匹配的服务。", 13 | "Cycle Start": "周期开始", 14 | "Cycle End": "周期结束", 15 | "Last Updated At": "更新于", 16 | "locale": "zh-CN", 17 | "Server": "服务器", 18 | "Usage": "使用情况", 19 | "Next Update Time": "下次更新时间", 20 | "No service data available.": "没有服务数据可用。", 21 | "Current Status": "当前状态", 22 | "Availability": "可用性", 23 | "Average Delay": "平均延迟", 24 | "Refresh": "刷新", 25 | "No group matches.": "没有匹配的组。", 26 | "All": "全部", 27 | "Statistics": "统计", 28 | "Total Servers": "总服务器数", 29 | "Online Servers": "在线服务器数", 30 | "Memory": "内存", 31 | "Swap": "交换", 32 | "Disk": "磁盘", 33 | "Traffic": "流量", 34 | "NIC": "网速", 35 | "Traffic Symmetry": "流量对等性", 36 | "The server id is not valid.": "服务器 ID 无效。", 37 | "The server id is not valid. Please check your prompt again.": "服务器 ID 无效,请再检查一遍输入。", 38 | "Online": "在线", 39 | "Offline": "离线", 40 | "ID": "ID", 41 | "IPv4": "IPv4", 42 | "IPv6": "IPv6", 43 | "Platform": "平台", 44 | "CPU Model(s)": "CPU 型号", 45 | "GPU Model(s)": "GPU 型号", 46 | "Uptime": "运行时间", 47 | "Load": "负载", 48 | "CPU Usage": "CPU 使用率", 49 | "Days": "天" 50 | } 51 | -------------------------------------------------------------------------------- /src/types/telegram.ts: -------------------------------------------------------------------------------- 1 | export interface Update { 2 | update_id: number; 3 | message?: Message; 4 | edited_message?: Message; 5 | callback_query?: CallbackQuery; 6 | } 7 | 8 | export interface InaccessibleMessage { 9 | message_id: number; 10 | date: number; 11 | chat: Chat; 12 | } 13 | 14 | export interface Message { 15 | message_id: number; 16 | date: number; 17 | chat: Chat; 18 | from?: User; 19 | reply_to_message?: Message; 20 | text?: string; 21 | } 22 | 23 | export type MaybeInaccessibleMessage = InaccessibleMessage | Message; 24 | 25 | export interface CallbackQuery { 26 | id: string; 27 | from: User; 28 | message: MaybeInaccessibleMessage; 29 | data?: string; 30 | } 31 | 32 | export interface User { 33 | id: number; 34 | is_bot: boolean; 35 | first_name: string; 36 | last_name?: string; 37 | username?: string; 38 | } 39 | 40 | export interface Chat { 41 | id: number; 42 | type: 'private' | 'group' | 'supergroup' | 'channel'; 43 | } 44 | 45 | export interface InlineKeyboardMarkup { 46 | inline_keyboard: InlineKeyboardButton[][]; 47 | } 48 | 49 | export interface InlineKeyboardButton { 50 | text: string; 51 | callback_data?: string; 52 | } 53 | 54 | export interface BotCommand { 55 | command: string; 56 | description: string; 57 | } 58 | 59 | export interface BotCommandScopeAllPrivateChats { 60 | type: 'all_private_chats'; 61 | } 62 | 63 | // Library-specific fields 64 | export interface CommonResponse { 65 | text: string; 66 | withInline?: boolean; 67 | inlineKeyboard?: InlineKeyboardButton[][]; 68 | } 69 | 70 | export type CommandHandler = (message: Message) => Promise; 71 | export type CallbackHandler = (query: CallbackQuery) => Promise; 72 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 6 | "target": "es2021", 7 | /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 8 | "lib": ["es2021"], 9 | /* Specify what JSX code is generated. */ 10 | "jsx": "react-jsx", 11 | 12 | /* Specify what module code is generated. */ 13 | "module": "es2022", 14 | /* Specify how TypeScript looks up a file from a given module specifier. */ 15 | "moduleResolution": "Bundler", 16 | /* Specify type package names to be included without being referenced in a source file. */ 17 | "types": [], 18 | /* Enable importing .json files */ 19 | "resolveJsonModule": true, 20 | 21 | /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 22 | "allowJs": true, 23 | /* Enable error reporting in type-checked JavaScript files. */ 24 | "checkJs": false, 25 | 26 | /* Disable emitting files from a compilation. */ 27 | "noEmit": true, 28 | 29 | /* Ensure that each file can be safely transpiled without relying on other imports. */ 30 | "isolatedModules": true, 31 | /* Allow 'import x from y' when a module doesn't have a default export. */ 32 | "allowSyntheticDefaultImports": true, 33 | /* Ensure that casing is correct in imports. */ 34 | "forceConsistentCasingInFileNames": true, 35 | 36 | /* Enable all strict type-checking options. */ 37 | "strict": true, 38 | 39 | /* Skip type checking all .d.ts files. */ 40 | "skipLibCheck": true 41 | }, 42 | "exclude": ["test"], 43 | "include": ["worker-configuration.d.ts", "src/**/*.ts"] 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Plain Nezha Bot 2 | 3 | 单用户单面板多语言私聊哪吒 Telegram 机器人,可以在 Cloudflare Workers 上部署。 4 | 5 | Inspired by https://github.com/nezhahq/Nezha-Telegram-Bot-V1 6 | 7 | ## 部署 8 | 9 | ### 使用 Github Actions 10 | 11 | 1. Fork 本项目 12 | 2. 在 Cloudflare Dashboard 使用模板 `Edit Cloudflare Workers` 并指定要使用的帐号创建一个 API Token,记录下来。 13 | 3. 创建一个 Workers KV 命名空间,保存下 ID 和 名称。 14 | 4. 在 Fork 的仓库设置创建需要的 Repository secrets: 15 | 16 | - `CLOUDFLARE_API_TOKEN`:刚刚创建的 API Token。 17 | - `KV_ID`:KV 命名空间 ID。 18 | - `NZ_BASEURL`:面板地址,例如 `https://ops.naibahq.com`。 19 | - `LANG`:语言,可选 `en` `zh-CN`,默认为 `en`。 20 | - `TZ`:时区,默认为 `UTC`。 21 | - `ENDPOINT_PATH`:接收 Telegram Webhook 的路由路径,例如 `/endpoint`。 22 | - `TELEGRAM_BOT_TOKEN`:从 BotFather 那里获取的机器人 Token。 23 | - `TELEGRAM_SECRET`:Webhook 认证密钥。 24 | - `TELEGRAM_UID`:用户 UID,机器人将不会与除此 UID 外的任何人互动。 25 | - `PASSWORD`:基本认证密码,用于进行 注册 / 取消注册 / 刷新 Token 操作。 26 | - `NZ_USERNAME`:面板用户名,用于初次认证及后续刷新。 27 | - `NZ_PASSWORD`:面板密码,用于初次认证及后续刷新。 28 | 29 | 5. Actions 栏里手动触发 Workflow,即可自动部署。为了不泄漏信息禁用了部署日志输出,部署完成后可以去 Cloudflare Dashboard 查看 Workers 的信息。 30 | 6. 后续更新可以直接拉取上游分支并推送,会自动触发部署。 31 | 32 | ### 手动 33 | 34 | 1. Clone 本项目,运行 `bun install` 安装依赖。 35 | 2. 用你喜欢的方式创建一个 Workers KV 命名空间,保存下 ID。 36 | 3. 修改 `wrangler.toml`,将 `kv_namespaces` 字段改为你创建的 KV 信息。 37 | 4. 修改 `wrangler.toml` 中的 `vars`: 38 | 39 | - `NZ_BASEURL`:面板地址,例如 `https://ops.naibahq.com`。 40 | - `LANG`:语言,可选 `en` `zh-CN`,默认为 `en`。 41 | - `TZ`:时区,默认为 `UTC`。 42 | - `ENDPOINT_PATH`:接收 Telegram Webhook 的路由路径,例如 `/endpoint`。 43 | 44 | 5. 创建以下 Secrets: 45 | 46 | - `TELEGRAM_BOT_TOKEN`:从 BotFather 那里获取的机器人 Token。 47 | - `TELEGRAM_SECRET`:Webhook 认证密钥。 48 | - `TELEGRAM_UID`:用户 UID,机器人将不会与除此 UID 外的任何人互动。 49 | - `PASSWORD`:基本认证密码,用于进行 注册 / 取消注册 / 刷新 Token 操作。 50 | - `NZ_USERNAME`:面板用户名,用于初次认证及后续刷新。 51 | - `NZ_PASSWORD`:面板密码,用于初次认证及后续刷新。 52 | 53 | 6. 输入 `bunx wrangler deploy` 部署项目。 54 | 55 | ## 使用 56 | 57 | 访问 `/register` 路由注册 Webhook 即可开始使用。 58 | 59 | 如暂时不需使用,可以访问 `/unregister` 删除 Webhook。 60 | 61 | 默认每 30 分钟触发一次 Token 刷新操作,可以在 `wrangler.toml` 中手动修改,或是访问 `/refresh` 手动刷新。 62 | -------------------------------------------------------------------------------- /src/lib/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Print help messages": "Print help messages", 3 | "Print server information (with id)": "Print server information (with id)", 4 | "Print server information (with server name)": "Print server information (with server name)", 5 | "Print server overview": "Print server overview", 6 | "Print monitor info": "Print monitor info", 7 | "Print cycle transfer info": "Print cycle transfer info", 8 | "Available commands": "Available commands", 9 | "The server name is not valid.": "The server name is not valid.", 10 | "The service name is not valid.": "The service name is not valid.", 11 | "No cycle transfer data available.": "No cycle transfer data available.", 12 | "No service matches.": "No service matches.", 13 | "Cycle Start": "Cycle Start", 14 | "Cycle End": "Cycle End", 15 | "Last Updated At": "Last Updated At", 16 | "locale": "en-US", 17 | "Server": "Server", 18 | "Usage": "Usage", 19 | "Next Update Time": "Next Update Time", 20 | "No service data available.": "No service data available.", 21 | "Current Status": "Current Status", 22 | "Availability": "Availability", 23 | "Average Delay": "Average Delay", 24 | "Refresh": "Refresh", 25 | "No group matches.": "No group matches.", 26 | "All": "All", 27 | "Statistics": "Statistics", 28 | "Total Servers": "Total Servers", 29 | "Online Servers": "Online Servers", 30 | "Memory": "Memory", 31 | "Swap": "Swap", 32 | "Disk": "Disk", 33 | "Traffic": "Traffic", 34 | "NIC": "NIC", 35 | "Traffic Symmetry": "Traffic Symmetry", 36 | "The server id is not valid.": "The server id is not valid.", 37 | "The server id is not valid. Please check your prompt again.": "The server id is not valid. Please check your prompt again.", 38 | "Online": "Online", 39 | "Offline": "Offline", 40 | "ID": "ID", 41 | "IPv4": "IPv4", 42 | "IPv6": "IPv6", 43 | "Platform": "Platform", 44 | "CPU Model(s)": "CPU Model(s)", 45 | "GPU Model(s)": "GPU Model(s)", 46 | "Uptime": "Uptime", 47 | "Load": "Load", 48 | "CPU Usage": "CPU Usage", 49 | "Days": "Days" 50 | } 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | .wrangler/ 173 | .wrangler.toml -------------------------------------------------------------------------------- /src/types/nezha.ts: -------------------------------------------------------------------------------- 1 | export interface CommonResponse { 2 | success: boolean; 3 | error: string; 4 | data: T; 5 | } 6 | 7 | export interface LoginResponse { 8 | expire: string; 9 | token: string; 10 | } 11 | 12 | export interface ModelServer { 13 | created_at: string; 14 | /** DDNS配置 */ 15 | ddns_profiles?: number[]; 16 | /** 展示排序,越大越靠前 */ 17 | display_index: number; 18 | /** 启用DDNS */ 19 | enable_ddns: boolean; 20 | geoip: ModelGeoIP; 21 | /** 对游客隐藏 */ 22 | hide_for_guest: boolean; 23 | host: ModelHost; 24 | id: number; 25 | last_active: string; 26 | name: string; 27 | /** 管理员可见备注 */ 28 | note: string; 29 | /** 公开备注 */ 30 | public_note: string; 31 | state: ModelHostState; 32 | updated_at: string; 33 | uuid: string; 34 | } 35 | 36 | export interface ModelGeoIP { 37 | country_code: string; 38 | ip: ModelIP; 39 | } 40 | 41 | export interface ModelHost { 42 | arch: string; 43 | boot_time: number; 44 | cpu: string[]; 45 | disk_total: number; 46 | gpu: string[]; 47 | mem_total: number; 48 | platform: string; 49 | platform_version: string; 50 | swap_total: number; 51 | version: string; 52 | virtualization: string; 53 | } 54 | 55 | export interface ModelHostState { 56 | cpu: number; 57 | disk_used: number; 58 | gpu: number[]; 59 | load_1: number; 60 | load_15: number; 61 | load_5: number; 62 | mem_used: number; 63 | net_in_speed: number; 64 | net_in_transfer: number; 65 | net_out_speed: number; 66 | net_out_transfer: number; 67 | process_count: number; 68 | swap_used: number; 69 | tcp_conn_count: number; 70 | temperatures: ModelSensorTemperature[]; 71 | udp_conn_count: number; 72 | uptime: number; 73 | } 74 | 75 | export interface ModelSensorTemperature { 76 | name?: string; 77 | temperature?: number; 78 | } 79 | 80 | export interface ModelIP { 81 | ipv4_addr: string; 82 | ipv6_addr: string; 83 | } 84 | 85 | export interface ModelServerGroupResponseItem { 86 | group: ModelServerGroup; 87 | servers: number[]; 88 | } 89 | 90 | export interface ModelServerGroup { 91 | created_at: string; 92 | id: number; 93 | name: string; 94 | updated_at: string; 95 | } 96 | 97 | // Library-specific fields 98 | export interface OverviewStats { 99 | servers: number; 100 | online_servers: number; 101 | mem_used: number; 102 | mem_total: number; 103 | swap_used: number; 104 | swap_total: number; 105 | disk_used: number; 106 | disk_total: number; 107 | net_in_speed: number; 108 | net_in_transfer: number; 109 | net_out_speed: number; 110 | net_out_transfer: number; 111 | } 112 | 113 | export interface ModelServiceResponse { 114 | cycle_transfer_stats: Record; 115 | services: Record; 116 | } 117 | 118 | export interface ModelCycleTransferStats { 119 | from: string; 120 | max: number; 121 | min: number; 122 | name: string; 123 | next_update: Record; 124 | server_name: Record; 125 | to: string; 126 | transfer: Record; 127 | } 128 | 129 | export interface ModelServiceResponseItem { 130 | current_down: number; 131 | current_up: number; 132 | delay: number[]; 133 | down: number[]; 134 | service_name: string; 135 | total_down: number; 136 | total_up: number; 137 | up: number[]; 138 | } 139 | -------------------------------------------------------------------------------- /src/lib/nezha/api.ts: -------------------------------------------------------------------------------- 1 | import * as nezha from '../../types/nezha'; 2 | import { buildUrl } from '../utils'; 3 | 4 | export interface NezhaAPIClientProps { 5 | base_url: string; 6 | username: string; 7 | password: string; 8 | cache: KVNamespace; 9 | } 10 | 11 | interface _NezhaAPIClientProps extends NezhaAPIClientProps { 12 | token: string; 13 | } 14 | 15 | enum FetcherMethod { 16 | GET = 'GET', 17 | POST = 'POST', 18 | PUT = 'PUT', 19 | PATCH = 'PATCH', 20 | DELETE = 'DELETE', 21 | } 22 | 23 | export class NezhaAPIClient { 24 | private readonly base_url: string; 25 | private readonly username: string; 26 | private readonly password: string; 27 | private token: string; 28 | private readonly cache: KVNamespace; 29 | 30 | private constructor({ base_url, username, password, token, cache }: _NezhaAPIClientProps) { 31 | this.base_url = base_url; 32 | this.username = username; 33 | this.password = password; 34 | this.token = token; 35 | this.cache = cache; 36 | } 37 | 38 | static async init({ base_url, username, password, cache }: NezhaAPIClientProps) { 39 | const client = new NezhaAPIClient({ base_url, username, password, token: '', cache }); 40 | 41 | const val = await cache.get('NZ_TOKEN'); 42 | if (val !== null) { 43 | client.token = val; 44 | return client; 45 | } 46 | 47 | try { 48 | await client.getToken(); 49 | } catch (error) { 50 | throw new Error('Failed to initialize NezhaAPIClient: ' + (error as Error).message); 51 | } 52 | 53 | return client; 54 | } 55 | 56 | async refreshToken() { 57 | try { 58 | const req = await this.fetcher(FetcherMethod.GET, '/api/v1/refresh-token'); 59 | this.token = req.token; 60 | await this.cache.put('NZ_TOKEN', req.token); 61 | } catch (error) { 62 | console.log('Refresh failed, perhaps the token is expired. Acquiring new token'); 63 | console.log('The error message is:', (error as Error).message); 64 | await this.getToken(); 65 | } 66 | } 67 | 68 | async getServerStats(id?: number) { 69 | return this.fetcher(FetcherMethod.GET, '/api/v1/server', { id }); 70 | } 71 | 72 | async getServerGroups() { 73 | return this.fetcher(FetcherMethod.GET, '/api/v1/server-group', null) || []; 74 | } 75 | 76 | async getService() { 77 | return this.fetcher(FetcherMethod.GET, '/api/v1/service', null) || {}; 78 | } 79 | 80 | private async getToken() { 81 | const req = await this.fetcher(FetcherMethod.POST, '/api/v1/login', { 82 | username: this.username, 83 | password: this.password, 84 | }); 85 | this.token = req.token; 86 | await this.cache.put('NZ_TOKEN', req.token); 87 | } 88 | 89 | private async fetcher(method: FetcherMethod, path: string, data?: any) { 90 | const endpoint = `${this.base_url}${path}`; 91 | 92 | let response; 93 | if (method === FetcherMethod.GET) { 94 | response = await fetch(buildUrl(endpoint, data), { 95 | method: 'GET', 96 | headers: { 97 | Authorization: `Bearer ${this.token}`, 98 | }, 99 | }); 100 | } else { 101 | response = await fetch(endpoint, { 102 | method: method, 103 | headers: { 104 | 'Content-Type': 'application/json', 105 | Authorization: `Bearer ${this.token}`, 106 | }, 107 | body: data ? JSON.stringify(data) : null, 108 | }); 109 | } 110 | if (!response.ok) { 111 | throw new Error(response.statusText); 112 | } 113 | 114 | const responseData: nezha.CommonResponse = await response.json(); 115 | if (!responseData.success) { 116 | throw new Error(responseData.error); 117 | } 118 | 119 | return responseData.data; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/lib/telegram/api.ts: -------------------------------------------------------------------------------- 1 | import * as telegram from '../../types/telegram'; 2 | 3 | export class Telegram { 4 | private readonly base_url: string; 5 | private readonly token: string; 6 | private readonly handlers: Record; 7 | private callbackHandler?: telegram.CallbackHandler; 8 | 9 | constructor({ base_url, token }: { base_url?: string; token: string }) { 10 | this.base_url = base_url ? base_url : 'https://api.telegram.org'; 11 | this.token = token; 12 | this.handlers = {}; 13 | } 14 | 15 | async setWebhook(url: string, secret?: string) { 16 | return this.newRequest('setWebhook', { url: url, secret_token: secret }); 17 | } 18 | 19 | async deleteWebhook() { 20 | return this.newRequest('deleteWebhook'); 21 | } 22 | 23 | async onUpdate(update: telegram.Update) { 24 | if (update.message) { 25 | return this.onMessage(update.message); 26 | } else if (update.callback_query) { 27 | return this.onCallbackQuery(update.callback_query); 28 | } 29 | } 30 | 31 | registerCommand({ command, fn }: { command: string; fn: telegram.CommandHandler }) { 32 | this.handlers[command] = fn; 33 | } 34 | 35 | registerCallback(fn: telegram.CallbackHandler) { 36 | this.callbackHandler = fn; 37 | } 38 | 39 | setMyCommands(commands: telegram.BotCommand[]) { 40 | return this.newRequest('setMyCommands', { 41 | commands: commands, 42 | scope: { 43 | type: 'all_private_chats', 44 | }, 45 | }); 46 | } 47 | 48 | private async onMessage(message: telegram.Message) { 49 | let response: telegram.CommonResponse | undefined; 50 | for (const command in this.handlers) { 51 | if (message.text?.startsWith(`/${command}`)) { 52 | response = await this.handlers[command](message); 53 | break; 54 | } 55 | } 56 | 57 | if (response) { 58 | const param = this.formMessageMarkdown(message, response); 59 | return this.sendMessage(param); 60 | } 61 | } 62 | 63 | private async onCallbackQuery(callbackQuery: telegram.CallbackQuery) { 64 | if (!('text' in callbackQuery.message)) return; 65 | 66 | let response: telegram.CommonResponse | undefined; 67 | if (this.callbackHandler) { 68 | response = await this.callbackHandler(callbackQuery); 69 | } 70 | 71 | await this.newRequest('answerCallbackQuery', { callback_query_id: callbackQuery.id }); 72 | 73 | if (response) { 74 | const param = this.formMessageMarkdown(callbackQuery.message, response, true); 75 | return this.editMessageText(param); 76 | } 77 | } 78 | 79 | private formMessageMarkdown(message: telegram.Message, response: telegram.CommonResponse, edit?: boolean) { 80 | let param: { 81 | chat_id: number; 82 | message_id?: number; 83 | text: string; 84 | parse_mode: string; 85 | reply_markup?: { inline_keyboard: telegram.InlineKeyboardButton[][] | undefined }; 86 | } = { 87 | chat_id: message.chat.id, 88 | message_id: edit ? message.message_id : undefined, 89 | text: response.text, 90 | parse_mode: 'MarkdownV2', 91 | }; 92 | 93 | if (response.withInline) { 94 | param.reply_markup = { 95 | inline_keyboard: response.inlineKeyboard, 96 | }; 97 | } 98 | 99 | return param; 100 | } 101 | 102 | private async sendMessage(param?: { [key: string]: any }) { 103 | return this.newRequest('sendMessage', param); 104 | } 105 | 106 | private async editMessageText(param?: { [key: string]: any }) { 107 | return this.newRequest('editMessageText', param); 108 | } 109 | 110 | private async newRequest(method: string, param?: { [key: string]: any }) { 111 | const endpoint = `${this.base_url}/bot${this.token}/${method}`; 112 | 113 | return fetch(endpoint, { 114 | method: 'POST', 115 | headers: { 116 | 'Content-Type': 'application/json', 117 | }, 118 | body: JSON.stringify(param || {}), 119 | }); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { TelegramHandlers } from './lib/telegram/handlers'; 2 | import { Telegram } from './lib/telegram/api'; 3 | import { Update } from './types/telegram'; 4 | import { NezhaAPIClient } from './lib/nezha/api'; 5 | import time from './lib/utils/time'; 6 | import i18next from './lib/translations'; 7 | 8 | export default { 9 | async fetch(request, env): Promise { 10 | const telegram = new Telegram({ 11 | token: env.TELEGRAM_BOT_TOKEN, 12 | }); 13 | 14 | const nezha = await NezhaAPIClient.init({ 15 | base_url: env.NZ_BASEURL, 16 | username: env.NZ_USERNAME, 17 | password: env.NZ_PASSWORD, 18 | cache: env.NZ_BOT_STORE, 19 | }); 20 | 21 | i18next.changeLanguage(env.LANG); 22 | time.updateTimezoneLocale(env.TZ, i18next.t('locale')); 23 | 24 | const handlers = new TelegramHandlers({ uid: parseInt(env.TELEGRAM_UID), nzClient: nezha }); 25 | registerAllCommands(telegram, handlers); 26 | 27 | const url = new URL(request.url); 28 | switch (url.pathname) { 29 | case '/register': { 30 | try { 31 | await basicAuth(request, env); 32 | } catch (error) { 33 | return new Response((error as Error).message, { 34 | status: 401, 35 | headers: { 36 | 'WWW-Authenticate': 'Basic realm="Restricted Area"', 37 | }, 38 | }); 39 | } 40 | 41 | await telegram.setMyCommands(handlers.getCommands()); 42 | 43 | const webHookUrl = `https://${url.hostname}${env.ENDPOINT_PATH}`; 44 | return telegram.setWebhook(webHookUrl, env.TELEGRAM_SECRET); 45 | } 46 | case '/unregister': { 47 | try { 48 | await basicAuth(request, env); 49 | } catch (error) { 50 | return new Response((error as Error).message, { 51 | status: 401, 52 | headers: { 53 | 'WWW-Authenticate': 'Basic realm="Restricted Area"', 54 | }, 55 | }); 56 | } 57 | 58 | return telegram.deleteWebhook(); 59 | } 60 | case '/refresh': { 61 | try { 62 | await basicAuth(request, env); 63 | } catch (error) { 64 | return new Response((error as Error).message, { 65 | status: 401, 66 | headers: { 67 | 'WWW-Authenticate': 'Basic realm="Restricted Area"', 68 | }, 69 | }); 70 | } 71 | 72 | await refreshToken(nezha); 73 | return new Response('refresh completed'); 74 | } 75 | case env.ENDPOINT_PATH: { 76 | if (env.TELEGRAM_SECRET && request.headers.get('X-Telegram-Bot-Api-Secret-Token') !== env.TELEGRAM_SECRET) { 77 | return new Response('Unauthorized', { status: 401 }); 78 | } 79 | 80 | const update: Update = await request.json(); 81 | const resp = await telegram.onUpdate(update); 82 | 83 | if (resp) { 84 | return resp; 85 | } else { 86 | return new Response('No response'); 87 | } 88 | } 89 | default: { 90 | return new Response('Wrong route', { status: 404 }); 91 | } 92 | } 93 | }, 94 | async scheduled(_, env) { 95 | const nezha = await NezhaAPIClient.init({ 96 | base_url: env.NZ_BASEURL, 97 | username: env.NZ_USERNAME, 98 | password: env.NZ_PASSWORD, 99 | cache: env.NZ_BOT_STORE, 100 | }); 101 | 102 | await refreshToken(nezha); 103 | }, 104 | } satisfies ExportedHandler; 105 | 106 | async function basicAuth(req: Request, env: Env) { 107 | const authHeader = req.headers.get('Authorization'); 108 | const expectedAuth = env.PASSWORD; 109 | 110 | if (!authHeader || !authHeader.startsWith('Basic ')) { 111 | throw new Error('Unauthorized'); 112 | } 113 | 114 | const encodedCredentials = authHeader.split(' ')[1]; 115 | const decodedCredentials = atob(encodedCredentials); 116 | const [_, password] = decodedCredentials.split(':'); 117 | 118 | const encoder = new TextEncoder(); 119 | if (!crypto.subtle.timingSafeEqual(encoder.encode(password), encoder.encode(expectedAuth))) { 120 | throw new Error('Unauthorized'); 121 | } 122 | } 123 | 124 | function registerAllCommands(client: Telegram, handlers: TelegramHandlers) { 125 | client.registerCommand({ 126 | command: 'start', 127 | fn: handlers.startHandler, 128 | }); 129 | client.registerCommand({ 130 | command: 'help', 131 | fn: handlers.startHandler, 132 | }); 133 | client.registerCommand({ 134 | command: 'sid', 135 | fn: handlers.sidHandler, 136 | }); 137 | client.registerCommand({ 138 | command: 'server', 139 | fn: handlers.serverHandler, 140 | }); 141 | client.registerCommand({ 142 | command: 'overview', 143 | fn: handlers.overviewHandler, 144 | }); 145 | client.registerCommand({ 146 | command: 'monitor', 147 | fn: handlers.monitorHandler, 148 | }); 149 | client.registerCommand({ 150 | command: 'transfer', 151 | fn: handlers.transferHandler, 152 | }); 153 | 154 | client.registerCallback(handlers.callBackHandler); 155 | } 156 | 157 | async function refreshToken(nezha: NezhaAPIClient) { 158 | console.log('refreshing token...'); 159 | 160 | try { 161 | await nezha.refreshToken(); 162 | } catch (e) { 163 | console.error('refresh failed: ', (e as Error).message); 164 | } 165 | console.log('refresh succeeded.'); 166 | } 167 | -------------------------------------------------------------------------------- /src/lib/telegram/handlers.ts: -------------------------------------------------------------------------------- 1 | import { md } from '@vlad-yakovlev/telegram-md'; 2 | import * as telegram from '../../types/telegram'; 3 | import * as nezha from '../../types/nezha'; 4 | import { NezhaAPIClient } from '../nezha/api'; 5 | import { nezhaUtils, getFlagEmoji } from '../utils'; 6 | import i18next from '../translations'; 7 | import time from '../utils/time'; 8 | 9 | export class TelegramHandlers { 10 | private readonly uid: number; 11 | private readonly nzClient: NezhaAPIClient; 12 | 13 | constructor({ uid, nzClient }: { uid: number; nzClient: NezhaAPIClient }) { 14 | this.uid = uid; 15 | this.nzClient = nzClient; 16 | } 17 | 18 | getCommands() { 19 | const commands: telegram.BotCommand[] = [ 20 | { 21 | command: '/start', 22 | description: i18next.t('Print help messages'), 23 | }, 24 | { 25 | command: '/help', 26 | description: i18next.t('Print help messages'), 27 | }, 28 | { 29 | command: '/sid', 30 | description: i18next.t('Print server information (with id)'), 31 | }, 32 | { 33 | command: '/server', 34 | description: i18next.t('Print server information (with server name)'), 35 | }, 36 | { 37 | command: '/overview', 38 | description: i18next.t('Print server overview'), 39 | }, 40 | { 41 | command: '/monitor', 42 | description: i18next.t('Print monitor info'), 43 | }, 44 | { 45 | command: '/transfer', 46 | description: i18next.t('Print cycle transfer info'), 47 | }, 48 | ]; 49 | 50 | return commands; 51 | } 52 | 53 | startHandler = async (message: telegram.Message) => { 54 | if (!this.authHandler(message)) return; 55 | 56 | const complexMessage = md` 57 | ${md.bold(i18next.t('Available commands'))}: 58 | ${md.inlineCode('/sid server_id')} - ${i18next.t('Print server information (with id)')} 59 | ${md.inlineCode('/server server_name')} - ${i18next.t('Print server information (with server name)')} 60 | ${md.inlineCode('/overview [group_name]')} - ${i18next.t('Print server overview')} 61 | ${md.inlineCode('/monitor service_name')} - ${i18next.t('Print monitor info')} 62 | ${md.inlineCode('/transfer service_name')} - ${i18next.t('Print cycle transfer info')} 63 | ${md.inlineCode('/help')} - ${i18next.t('Print help messages')} 64 | `; 65 | 66 | const response: telegram.CommonResponse = { 67 | text: md.build(complexMessage), 68 | }; 69 | 70 | return response; 71 | }; 72 | 73 | sidHandler = async (message: telegram.Message) => { 74 | if (!this.authHandler(message)) return; 75 | 76 | const sidStr = message.text?.slice(4).trim(); // "/sid" 77 | 78 | const sid = parseInt(sidStr || ''); 79 | if (isNaN(sid)) return { text: md.build(md`${i18next.t('The server id is not valid.')}`) }; 80 | 81 | return this.generateServerResponse(sid); 82 | }; 83 | 84 | serverHandler = async (message: telegram.Message) => { 85 | if (!this.authHandler(message)) return; 86 | 87 | const serverName = message.text?.slice(7).trim(); // "/server" 88 | if (!serverName) { 89 | return { text: md.build(md`${i18next.t('The server name is not valid.')}`) }; 90 | } 91 | 92 | return this.generateServerResponse(serverName); 93 | }; 94 | 95 | overviewHandler = async (message: telegram.Message) => { 96 | if (!this.authHandler(message)) return; 97 | 98 | const groupName = message.text?.slice(9).trim(); // "/overview" 99 | return this.generateOverviewResponse(groupName); 100 | }; 101 | 102 | monitorHandler = async (message: telegram.Message) => { 103 | if (!this.authHandler(message)) return; 104 | 105 | const serviceName = message.text?.slice(8).trim(); // "/monitor" 106 | if (!serviceName) return { text: md.build(md`${i18next.t('The service name is not valid.')}`) }; 107 | 108 | return this.generateMonitorResponse(serviceName); 109 | }; 110 | 111 | transferHandler = async (message: telegram.Message) => { 112 | if (!this.authHandler(message)) return; 113 | 114 | const serviceName = message.text?.slice(9).trim(); // "/transfer" 115 | if (!serviceName) return { text: md.build(md`${i18next.t('The service name is not valid.')}`) }; 116 | 117 | return this.generateTransferResponse(serviceName); 118 | }; 119 | 120 | callBackHandler = async (query: telegram.CallbackQuery) => { 121 | if (query.message.chat.type !== 'private') return; 122 | if (query.from.id !== this.uid) return; 123 | 124 | const data = query.data; 125 | if (data?.startsWith('refresh_server')) { 126 | const sidStr = data.split('_')[2]; 127 | const sid = parseInt(sidStr); 128 | return this.generateServerResponse(sid); 129 | } else if (data?.startsWith('overview')) { 130 | const g = data.split('_'); 131 | let groupName: string | undefined; 132 | if (g.length > 1) { 133 | groupName = g[1]; 134 | } 135 | return this.generateOverviewResponse(groupName); 136 | } else if (data?.startsWith('monitor')) { 137 | const serviceName = data.split('_')[1]; 138 | return this.generateMonitorResponse(serviceName); 139 | } else if (data?.startsWith('transfer')) { 140 | const serviceName = data.split('_')[1]; 141 | return this.generateTransferResponse(serviceName); 142 | } 143 | }; 144 | 145 | private async generateTransferResponse(serviceName: string) { 146 | const serviceResp = await this.nzClient.getService(); 147 | if (!serviceResp.cycle_transfer_stats) return { text: md.build(md`${i18next.t('No cycle transfer data available.')}`) }; 148 | 149 | let matchedServiceName: string | undefined; 150 | const matchedService = Object.values(serviceResp.cycle_transfer_stats).find((s) => 151 | s.name.toLowerCase().includes(serviceName.toLowerCase()), 152 | ); 153 | 154 | if (matchedService) { 155 | matchedServiceName = matchedService.name; 156 | } else { 157 | return { text: md.build(md`${i18next.t('No service matches.')}`) }; 158 | } 159 | 160 | const response: telegram.CommonResponse = { 161 | text: md.build(md` 162 | 🛣 ${md.bold(matchedServiceName)} 163 | ========================== 164 | ${i18next.t('Cycle Start')}: ${time.dateStr(matchedService.from)} 165 | ${i18next.t('Cycle End')}: ${time.dateStr(matchedService.to)} 166 | ${this.formatTransferMessage(matchedService).join('\n')} 167 | ${i18next.t('Last Updated At')}: ${time.dateStr()} 168 | `), 169 | withInline: true, 170 | inlineKeyboard: [ 171 | [ 172 | { 173 | text: 'Refresh', 174 | callback_data: `transfer_${matchedServiceName}`, 175 | }, 176 | ], 177 | ], 178 | }; 179 | 180 | return response; 181 | } 182 | 183 | private formatTransferMessage(service: nezha.ModelCycleTransferStats) { 184 | return Object.entries(service.server_name).map( 185 | ([sid, name]) => ` 186 | ${i18next.t('Server')}: ${name} 187 | ${i18next.t('Usage')}: ${nezhaUtils.formatUsage(service.transfer[sid], service.max)}% ${nezhaUtils.formatBytes( 188 | service.transfer[sid], 189 | )}/${nezhaUtils.formatBytes(service.max)} 190 | ${i18next.t('Next Update Time')}: ${time.dateStr(service.next_update[sid])} 191 | `, 192 | ); 193 | } 194 | 195 | private async generateMonitorResponse(serviceName: string) { 196 | const serviceResp = await this.nzClient.getService(); 197 | if (!serviceResp.services) return { text: md.build(md`${i18next.t('No service data available.')}`) }; 198 | 199 | let matchedServiceName: string | undefined; 200 | 201 | const matchedService = Object.values(serviceResp.services).find((s) => 202 | s.service_name.toLowerCase().includes(serviceName.toLowerCase()), 203 | ); 204 | 205 | if (matchedService) { 206 | matchedServiceName = matchedService.service_name; 207 | } else { 208 | return { text: md.build(md`${i18next.t('No service matches.')}`) }; 209 | } 210 | 211 | const avgDelay = matchedService.delay.length > 0 ? matchedService.delay.reduce((a, b) => a + b, 0) / matchedService.delay.length : 0; 212 | 213 | const response: telegram.CommonResponse = { 214 | text: md.build(md` 215 | 🚨 ${md.bold(matchedServiceName)} 216 | ========================== 217 | ${i18next.t('Current Status')}: ${matchedService.up[29] > matchedService.down[29] ? '✅' : '❌'} 218 | ${i18next.t('Availability')}: ${nezhaUtils.formatUsage(matchedService.total_up - matchedService.total_down, matchedService.total_up)}% 219 | ${i18next.t('Average Delay')}: ${avgDelay.toFixed(2)}ms 220 | 221 | ${i18next.t('Last Updated At')}: ${time.dateStr()} 222 | `), 223 | withInline: true, 224 | inlineKeyboard: [ 225 | [ 226 | { 227 | text: i18next.t('Refresh'), 228 | callback_data: `monitor_${matchedServiceName}`, 229 | }, 230 | ], 231 | ], 232 | }; 233 | 234 | return response; 235 | } 236 | 237 | private async generateOverviewResponse(groupName?: string) { 238 | let servers = await this.nzClient.getServerStats(); 239 | const groups = await this.nzClient.getServerGroups(); 240 | 241 | let matchedGroupName: string | undefined; 242 | if (groupName) { 243 | const group = groups.find((group) => group.group.name.toLowerCase().includes(groupName.toLowerCase())); 244 | if (group) { 245 | servers = servers.filter((s) => group.servers.includes(s.id)); 246 | matchedGroupName = group.group.name; 247 | } else { 248 | return { text: md.build(md`${i18next.t('No group matches.')}`) }; 249 | } 250 | } 251 | 252 | const inlineKeyboard: telegram.InlineKeyboardButton[][] = [ 253 | [ 254 | { 255 | text: i18next.t('All'), 256 | callback_data: 'overview', 257 | }, 258 | ], 259 | ...groups.reduce((result: telegram.InlineKeyboardButton[][], group, index) => { 260 | if (index % 4 === 0) result.push([]); 261 | result[result.length - 1].push({ 262 | text: group.group.name, 263 | callback_data: `overview_${group.group.name}`, 264 | }); 265 | return result; 266 | }, [] as telegram.InlineKeyboardButton[][]), 267 | ]; 268 | 269 | const stats = this.calculateGroupStats(servers); 270 | const response: telegram.CommonResponse = { 271 | text: md.build(md` 272 | 📊 ${i18next.t('Statistics')}${matchedGroupName ? ' for ' : ''}${md.bold(matchedGroupName)} 273 | ========================== 274 | ${i18next.t('Total Servers')}: ${stats.servers} 275 | ${i18next.t('Online Servers')}: ${stats.online_servers} 276 | ${i18next.t('Memory')}: ${nezhaUtils.formatUsage(stats.mem_used, stats.mem_total)}% ${nezhaUtils.formatBytes( 277 | stats.mem_used, 278 | )}/${nezhaUtils.formatBytes(stats.mem_total)} 279 | ${i18next.t('Swap')}: ${nezhaUtils.formatUsage(stats.swap_used, stats.swap_total)}% ${nezhaUtils.formatBytes( 280 | stats.swap_used, 281 | )}/${nezhaUtils.formatBytes(stats.swap_total)} 282 | ${i18next.t('Disk')}: ${nezhaUtils.formatUsage(stats.disk_used, stats.disk_total)}% ${nezhaUtils.formatBytes( 283 | stats.disk_used, 284 | )}/${nezhaUtils.formatBytes(stats.disk_total)} 285 | ${i18next.t('Traffic')}: ↓${nezhaUtils.formatBytes(stats.net_in_transfer)} ↑${nezhaUtils.formatBytes(stats.net_out_transfer)} 286 | ${i18next.t('NIC')}: ↓${nezhaUtils.formatBytes(stats.net_in_speed)}/s ↑${nezhaUtils.formatBytes(stats.net_out_speed)}/s 287 | ${i18next.t('Traffic Symmetry')}: ${nezhaUtils.formatUsage(stats.net_out_transfer, stats.net_in_transfer)}% 288 | 289 | ${i18next.t('Last Updated At')}: ${md.bold(time.dateStr())} 290 | `), 291 | withInline: true, 292 | inlineKeyboard: inlineKeyboard, 293 | }; 294 | 295 | return response; 296 | } 297 | 298 | private calculateGroupStats(servers: nezha.ModelServer[]): nezha.OverviewStats { 299 | return servers.reduce( 300 | (acc, s) => { 301 | acc.servers++; 302 | if (!nezhaUtils.isOffline(s.last_active)) { 303 | acc.online_servers++; 304 | } 305 | acc.mem_used += s.state.mem_used; 306 | acc.mem_total += s.host.mem_total; 307 | acc.swap_used += s.state.swap_used; 308 | acc.swap_total += s.host.swap_total; 309 | acc.disk_used += s.state.disk_used; 310 | acc.disk_total += s.host.disk_total; 311 | acc.net_in_speed += s.state.net_in_speed; 312 | acc.net_out_speed += s.state.net_out_speed; 313 | acc.net_in_transfer += s.state.net_in_transfer; 314 | acc.net_out_transfer += s.state.net_out_transfer; 315 | return acc; 316 | }, 317 | { 318 | servers: 0, 319 | online_servers: 0, 320 | mem_used: 0, 321 | mem_total: 0, 322 | swap_used: 0, 323 | swap_total: 0, 324 | disk_used: 0, 325 | disk_total: 0, 326 | net_in_speed: 0, 327 | net_out_speed: 0, 328 | net_in_transfer: 0, 329 | net_out_transfer: 0, 330 | }, 331 | ); 332 | } 333 | 334 | private async generateServerResponse(sid: number | string) { 335 | const sidIsNumber = typeof sid === 'number'; 336 | const servers = await this.nzClient.getServerStats(sidIsNumber ? sid : undefined); 337 | let server: nezha.ModelServer | undefined; 338 | 339 | if (sidIsNumber) { 340 | for (const s of servers) { 341 | if (s.id === sid) { 342 | server = s; 343 | break; 344 | } 345 | } 346 | } else { 347 | for (const s of servers) { 348 | if (s.name.toLowerCase().includes(sid.toLowerCase())) { 349 | server = s; 350 | break; 351 | } 352 | } 353 | } 354 | 355 | const response: telegram.CommonResponse = server 356 | ? { 357 | text: this.formatServerMessage(server), 358 | withInline: true, 359 | inlineKeyboard: [ 360 | [ 361 | { 362 | text: i18next.t('Refresh'), 363 | callback_data: `refresh_server_${server.id}`, 364 | }, 365 | ], 366 | ], 367 | } 368 | : { text: md.build(md`${i18next.t('The server id is not valid. Please check your prompt again.')}`) }; 369 | 370 | return response; 371 | } 372 | 373 | private formatServerMessage(server: nezha.ModelServer) { 374 | const onlineStr = i18next.t('Online'); 375 | const offlineStr = i18next.t('Offline'); 376 | return md.build(md` 377 | ${getFlagEmoji(server.geoip.country_code)} ${md.bold(server.name)} ${ 378 | nezhaUtils.isOffline(server.last_active) ? `❌️ ${offlineStr}` : `✅ ${onlineStr}` 379 | } 380 | ========================== 381 | ${i18next.t('ID')}: ${md.bold(server.id)} 382 | ${i18next.t('IPv4')}: ${server.geoip.ip.ipv4_addr || `❌️`} 383 | ${i18next.t('IPv6')}: ${server.geoip.ip.ipv6_addr || `❌️`} 384 | ${i18next.t('Platform')}: ${server.host.platform || 'Unknown'} 385 | ${i18next.t('CPU Model(s)')}: ${server.host.cpu.join(',')}${ 386 | server.host.gpu ? `\n${i18next.t('GPU Model(s)')}: ${server.host.gpu.join(',')}` : '' 387 | } 388 | ${i18next.t('Uptime')}: ${nezhaUtils.convertSecondsToDays(server.state.uptime)} ${i18next.t('Days')} 389 | ${i18next.t('Load')}: ${server.state.load_1 || 0} ${server.state.load_5 || 0} ${server.state.load_15 || 0} 390 | ${i18next.t('CPU Usage')}: ${server.state.cpu ? server.state.cpu.toFixed(2) : 0}% 391 | ${i18next.t('Memory')}: ${nezhaUtils.formatUsage(server.state.mem_used, server.host.mem_total)}% ${nezhaUtils.formatBytes( 392 | server.state.mem_used, 393 | )}/${nezhaUtils.formatBytes(server.host.mem_total)} 394 | ${i18next.t('Swap')}: ${nezhaUtils.formatUsage(server.state.swap_used, server.host.swap_total)}% ${nezhaUtils.formatBytes( 395 | server.state.swap_used, 396 | )}/${nezhaUtils.formatBytes(server.host.swap_total)} 397 | ${i18next.t('Disk')}: ${nezhaUtils.formatUsage(server.state.disk_used, server.host.disk_total)}% ${nezhaUtils.formatBytes( 398 | server.state.disk_used, 399 | )}/${nezhaUtils.formatBytes(server.host.disk_total)} 400 | ${i18next.t('Traffic')}: ↓${nezhaUtils.formatBytes(server.state.net_in_transfer)} ↑${nezhaUtils.formatBytes(server.state.net_out_transfer)} 401 | ${i18next.t('NIC')}: ↓${nezhaUtils.formatBytes(server.state.net_in_speed)}/s ↑${nezhaUtils.formatBytes(server.state.net_out_speed)}/s 402 | 403 | ${i18next.t('Last Updated At')}: ${md.bold(time.dateStr())} 404 | `); 405 | } 406 | 407 | private authHandler = (message: telegram.Message) => { 408 | if (message.chat.type !== 'private') return false; 409 | 410 | if (message.from?.id !== this.uid) return false; 411 | 412 | return true; 413 | }; 414 | } 415 | -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "plain-nezha-bot", 6 | "dependencies": { 7 | "@vlad-yakovlev/telegram-md": "^2.0.0", 8 | "i18next": "^24.2.3", 9 | }, 10 | "devDependencies": { 11 | "@cloudflare/vitest-pool-workers": "^0.8.4", 12 | "typescript": "^5.8.3", 13 | "vitest": "3.0.9", 14 | "wrangler": "^4.18.0", 15 | }, 16 | }, 17 | }, 18 | "packages": { 19 | "@babel/runtime": ["@babel/runtime@7.27.4", "", {}, "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA=="], 20 | 21 | "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], 22 | 23 | "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-MtUgNl+QkQyhQvv5bbWP+BpBC1N0me4CHHuP2H4ktmOMKdB/6kkz/lo+zqiA4mEazb4y+1cwyNjVrQ2DWeE4mg=="], 24 | 25 | "@cloudflare/vitest-pool-workers": ["@cloudflare/vitest-pool-workers@0.8.34", "", { "dependencies": { "birpc": "0.2.14", "cjs-module-lexer": "^1.2.3", "devalue": "^4.3.0", "miniflare": "4.20250525.0", "semver": "^7.7.1", "wrangler": "4.18.0", "zod": "^3.22.3" }, "peerDependencies": { "@vitest/runner": "2.0.x - 3.1.x", "@vitest/snapshot": "2.0.x - 3.1.x", "vitest": "2.0.x - 3.1.x" } }, "sha512-MB+J+UAeU+a0wfrSt1DvElgBFsXYBxAf6O1H67iT/4L/zOwDhKcx2ewMikUDf8O217ag2q1yEhhwug6XQZ76nA=="], 26 | 27 | "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250525.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-L5l+7sSJJT2+riR5rS3Q3PKNNySPjWfRIeaNGMVRi1dPO6QPi4lwuxfRUFNoeUdilZJUVPfSZvTtj9RedsKznQ=="], 28 | 29 | "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250525.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y3IbIdrF/vJWh/WBvshwcSyUh175VAiLRW7963S1dXChrZ1N5wuKGQm9xY69cIGVtitpMJWWW3jLq7J/Xxwm0Q=="], 30 | 31 | "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250525.0", "", { "os": "linux", "cpu": "x64" }, "sha512-KSyQPAby+c6cpENoO0ayCQlY6QIh28l/+QID7VC1SLXfiNHy+hPNsH1vVBTST6CilHVAQSsy9tCZ9O9XECB8yg=="], 32 | 33 | "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250525.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Nt0FUxS2kQhJUea4hMCNPaetkrAFDhPnNX/ntwcqVlGgnGt75iaAhupWJbU0GB+gIWlKeuClUUnDZqKbicoKyg=="], 34 | 35 | "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250525.0", "", { "os": "win32", "cpu": "x64" }, "sha512-mwTj+9f3uIa4NEXR1cOa82PjLa6dbrb3J+KCVJFYIaq7e63VxEzOchCXS4tublT2pmOhmFqkgBMXrxozxNkR2Q=="], 36 | 37 | "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], 38 | 39 | "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], 40 | 41 | "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], 42 | 43 | "@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="], 44 | 45 | "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="], 46 | 47 | "@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="], 48 | 49 | "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="], 50 | 51 | "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="], 52 | 53 | "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="], 54 | 55 | "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="], 56 | 57 | "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="], 58 | 59 | "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="], 60 | 61 | "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="], 62 | 63 | "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="], 64 | 65 | "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="], 66 | 67 | "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="], 68 | 69 | "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="], 70 | 71 | "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="], 72 | 73 | "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="], 74 | 75 | "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="], 76 | 77 | "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="], 78 | 79 | "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="], 80 | 81 | "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="], 82 | 83 | "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="], 84 | 85 | "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="], 86 | 87 | "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="], 88 | 89 | "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], 90 | 91 | "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], 92 | 93 | "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], 94 | 95 | "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], 96 | 97 | "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], 98 | 99 | "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], 100 | 101 | "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], 102 | 103 | "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], 104 | 105 | "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], 106 | 107 | "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], 108 | 109 | "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], 110 | 111 | "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], 112 | 113 | "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], 114 | 115 | "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], 116 | 117 | "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], 118 | 119 | "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], 120 | 121 | "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], 122 | 123 | "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], 124 | 125 | "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], 126 | 127 | "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], 128 | 129 | "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], 130 | 131 | "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 132 | 133 | "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 134 | 135 | "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], 136 | 137 | "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.41.1", "", { "os": "android", "cpu": "arm" }, "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw=="], 138 | 139 | "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.41.1", "", { "os": "android", "cpu": "arm64" }, "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA=="], 140 | 141 | "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.41.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w=="], 142 | 143 | "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.41.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg=="], 144 | 145 | "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.41.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg=="], 146 | 147 | "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.41.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA=="], 148 | 149 | "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg=="], 150 | 151 | "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA=="], 152 | 153 | "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA=="], 154 | 155 | "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg=="], 156 | 157 | "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw=="], 158 | 159 | "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.41.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A=="], 160 | 161 | "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw=="], 162 | 163 | "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw=="], 164 | 165 | "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.41.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g=="], 166 | 167 | "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A=="], 168 | 169 | "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ=="], 170 | 171 | "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.41.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ=="], 172 | 173 | "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.41.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg=="], 174 | 175 | "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="], 176 | 177 | "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], 178 | 179 | "@vitest/expect": ["@vitest/expect@3.0.9", "", { "dependencies": { "@vitest/spy": "3.0.9", "@vitest/utils": "3.0.9", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig=="], 180 | 181 | "@vitest/mocker": ["@vitest/mocker@3.0.9", "", { "dependencies": { "@vitest/spy": "3.0.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA=="], 182 | 183 | "@vitest/pretty-format": ["@vitest/pretty-format@3.2.0", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-gUUhaUmPBHFkrqnOokmfMGRBMHhgpICud9nrz/xpNV3/4OXCn35oG+Pl8rYYsKaTNd/FAIrqRHnwpDpmYxCYZw=="], 184 | 185 | "@vitest/runner": ["@vitest/runner@3.0.9", "", { "dependencies": { "@vitest/utils": "3.0.9", "pathe": "^2.0.3" } }, "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw=="], 186 | 187 | "@vitest/snapshot": ["@vitest/snapshot@3.0.9", "", { "dependencies": { "@vitest/pretty-format": "3.0.9", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A=="], 188 | 189 | "@vitest/spy": ["@vitest/spy@3.0.9", "", { "dependencies": { "tinyspy": "^3.0.2" } }, "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ=="], 190 | 191 | "@vitest/utils": ["@vitest/utils@3.0.9", "", { "dependencies": { "@vitest/pretty-format": "3.0.9", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" } }, "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng=="], 192 | 193 | "@vlad-yakovlev/telegram-md": ["@vlad-yakovlev/telegram-md@2.0.0", "", {}, "sha512-qdJYD2N1I7CN0uRboTE2PDvHL9/w519RjIA0bnsOllr8BYIZOzBnjmPqTvg1laZk+dqmcchrHSmEhLGAZBSSfA=="], 194 | 195 | "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], 196 | 197 | "acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="], 198 | 199 | "as-table": ["as-table@1.0.55", "", { "dependencies": { "printable-characters": "^1.0.42" } }, "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ=="], 200 | 201 | "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], 202 | 203 | "birpc": ["birpc@0.2.14", "", {}, "sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA=="], 204 | 205 | "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], 206 | 207 | "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], 208 | 209 | "chai": ["chai@5.2.0", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw=="], 210 | 211 | "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], 212 | 213 | "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], 214 | 215 | "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], 216 | 217 | "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 218 | 219 | "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 220 | 221 | "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], 222 | 223 | "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], 224 | 225 | "data-uri-to-buffer": ["data-uri-to-buffer@2.0.2", "", {}, "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA=="], 226 | 227 | "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], 228 | 229 | "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], 230 | 231 | "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], 232 | 233 | "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], 234 | 235 | "devalue": ["devalue@4.3.3", "", {}, "sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg=="], 236 | 237 | "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], 238 | 239 | "esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], 240 | 241 | "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], 242 | 243 | "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], 244 | 245 | "expect-type": ["expect-type@1.2.1", "", {}, "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw=="], 246 | 247 | "exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], 248 | 249 | "fdir": ["fdir@6.4.5", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw=="], 250 | 251 | "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 252 | 253 | "get-source": ["get-source@2.0.12", "", { "dependencies": { "data-uri-to-buffer": "^2.0.0", "source-map": "^0.6.1" } }, "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w=="], 254 | 255 | "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], 256 | 257 | "i18next": ["i18next@24.2.3", "", { "dependencies": { "@babel/runtime": "^7.26.10" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A=="], 258 | 259 | "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], 260 | 261 | "loupe": ["loupe@3.1.3", "", {}, "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug=="], 262 | 263 | "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 264 | 265 | "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], 266 | 267 | "miniflare": ["miniflare@4.20250525.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250525.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-F5XRDn9WqxUaHphUT8qwy5WXC/3UwbBRJTdjjP5uwHX82vypxIlHNyHziZnplPLhQa1kbSdIY7wfuP1XJyyYZw=="], 268 | 269 | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 270 | 271 | "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], 272 | 273 | "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 274 | 275 | "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], 276 | 277 | "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], 278 | 279 | "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 280 | 281 | "pathval": ["pathval@2.0.0", "", {}, "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA=="], 282 | 283 | "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 284 | 285 | "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], 286 | 287 | "postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="], 288 | 289 | "printable-characters": ["printable-characters@1.0.42", "", {}, "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ=="], 290 | 291 | "rollup": ["rollup@4.41.1", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.41.1", "@rollup/rollup-android-arm64": "4.41.1", "@rollup/rollup-darwin-arm64": "4.41.1", "@rollup/rollup-darwin-x64": "4.41.1", "@rollup/rollup-freebsd-arm64": "4.41.1", "@rollup/rollup-freebsd-x64": "4.41.1", "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", "@rollup/rollup-linux-arm-musleabihf": "4.41.1", "@rollup/rollup-linux-arm64-gnu": "4.41.1", "@rollup/rollup-linux-arm64-musl": "4.41.1", "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-musl": "4.41.1", "@rollup/rollup-linux-s390x-gnu": "4.41.1", "@rollup/rollup-linux-x64-gnu": "4.41.1", "@rollup/rollup-linux-x64-musl": "4.41.1", "@rollup/rollup-win32-arm64-msvc": "4.41.1", "@rollup/rollup-win32-ia32-msvc": "4.41.1", "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw=="], 292 | 293 | "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], 294 | 295 | "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], 296 | 297 | "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], 298 | 299 | "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], 300 | 301 | "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 302 | 303 | "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 304 | 305 | "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], 306 | 307 | "stacktracey": ["stacktracey@2.1.8", "", { "dependencies": { "as-table": "^1.0.36", "get-source": "^2.0.12" } }, "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw=="], 308 | 309 | "std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="], 310 | 311 | "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="], 312 | 313 | "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], 314 | 315 | "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], 316 | 317 | "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], 318 | 319 | "tinypool": ["tinypool@1.1.0", "", {}, "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ=="], 320 | 321 | "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], 322 | 323 | "tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="], 324 | 325 | "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 326 | 327 | "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], 328 | 329 | "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], 330 | 331 | "undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], 332 | 333 | "unenv": ["unenv@2.0.0-rc.17", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg=="], 334 | 335 | "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], 336 | 337 | "vite-node": ["vite-node@3.0.9", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg=="], 338 | 339 | "vitest": ["vitest@3.0.9", "", { "dependencies": { "@vitest/expect": "3.0.9", "@vitest/mocker": "3.0.9", "@vitest/pretty-format": "^3.0.9", "@vitest/runner": "3.0.9", "@vitest/snapshot": "3.0.9", "@vitest/spy": "3.0.9", "@vitest/utils": "3.0.9", "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.1.0", "magic-string": "^0.30.17", "pathe": "^2.0.3", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", "vite-node": "3.0.9", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.0.9", "@vitest/ui": "3.0.9", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ=="], 340 | 341 | "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], 342 | 343 | "workerd": ["workerd@1.20250525.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250525.0", "@cloudflare/workerd-darwin-arm64": "1.20250525.0", "@cloudflare/workerd-linux-64": "1.20250525.0", "@cloudflare/workerd-linux-arm64": "1.20250525.0", "@cloudflare/workerd-windows-64": "1.20250525.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-SXJgLREy/Aqw2J71Oah0Pbu+SShbqbTExjVQyRBTM1r7MG7fS5NUlknhnt6sikjA/t4cO09Bi8OJqHdTkrcnYQ=="], 344 | 345 | "wrangler": ["wrangler@4.18.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.2", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250525.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250525.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250525.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-/ng0KI9io97SNsBU1rheADBLLTE5Djybgsi4gXuvH1RBKJGpyj1xWvZ2fuWu8vAonit3EiZkwtERTm6kESHP3A=="], 346 | 347 | "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], 348 | 349 | "youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="], 350 | 351 | "zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="], 352 | 353 | "@vitest/snapshot/@vitest/pretty-format": ["@vitest/pretty-format@3.0.9", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA=="], 354 | 355 | "@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.0.9", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA=="], 356 | 357 | "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], 358 | 359 | "vite/esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="], 360 | 361 | "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="], 362 | 363 | "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="], 364 | 365 | "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="], 366 | 367 | "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="], 368 | 369 | "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="], 370 | 371 | "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="], 372 | 373 | "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="], 374 | 375 | "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="], 376 | 377 | "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="], 378 | 379 | "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="], 380 | 381 | "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="], 382 | 383 | "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="], 384 | 385 | "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="], 386 | 387 | "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="], 388 | 389 | "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="], 390 | 391 | "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="], 392 | 393 | "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="], 394 | 395 | "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="], 396 | 397 | "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="], 398 | 399 | "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="], 400 | 401 | "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="], 402 | 403 | "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="], 404 | 405 | "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="], 406 | 407 | "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="], 408 | 409 | "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="], 410 | } 411 | } 412 | --------------------------------------------------------------------------------