From 82221955331dcaae637281eaba26f6bead8c8b1c Mon Sep 17 00:00:00 2001 From: Clarke X <1014930533@qq.com> Date: Fri, 29 May 2026 10:19:39 +0800 Subject: [PATCH 1/2] feat: update dependencies and enhance testing suite - Updated Python version requirement to >=3.12 in pyproject.toml. - Replaced websockets with aiohttp in dependencies for improved performance. - Upgraded rich and pypinyin to their latest versions. - Added pytest as a development dependency for better testing capabilities. - Created comprehensive tests for aiohttp server, configuration normalization, message parsing, protocol handling, and server commands. - Removed legacy websockets dependency from the project. - Improved test coverage by adding tests for message formatting and server behavior. --- .github/changelog-config.json | 128 +- .github/copilot-instructions.md | 687 +++--- .github/workflows/nuitka-build.yml | 137 +- .github/workflows/pyinstaller-build.yml | 108 +- .gitignore | 81 +- .pre-commit-config.yaml | 38 +- .pylintrc | 14 +- README.md | 333 +-- echo-client.spec | 78 +- echo_client/__init__.py | 30 +- echo_client/cli.py | 42 +- echo_client/commands.py | 566 ++--- echo_client/config.py | 231 +- echo_client/connection.py | 125 ++ echo_client/message.py | 1054 ++++----- echo_client/protocol.py | 162 ++ echo_client/server.py | 743 +++---- main.py | 16 +- message_sample.txt | 106 +- poetry.lock | 2691 +++++++++++------------ pyproject.toml | 38 +- pyrequirements.txt | 2 +- requirements.txt | 4 +- tests/test_aiohttp_server.py | 130 ++ tests/test_config.py | 44 + tests/test_message.py | 62 + tests/test_protocol.py | 40 + tests/test_server.py | 39 + 28 files changed, 4047 insertions(+), 3682 deletions(-) create mode 100644 echo_client/connection.py create mode 100644 echo_client/protocol.py create mode 100644 tests/test_aiohttp_server.py create mode 100644 tests/test_config.py create mode 100644 tests/test_message.py create mode 100644 tests/test_protocol.py create mode 100644 tests/test_server.py diff --git a/.github/changelog-config.json b/.github/changelog-config.json index df76214..1f53492 100644 --- a/.github/changelog-config.json +++ b/.github/changelog-config.json @@ -1,64 +1,64 @@ -{ - "categories": [ - { - "title": "## 🚀 Features", - "labels": ["feature", "enhancement"] - }, - { - "title": "## 🐛 Fixes", - "labels": ["fix", "bugfix", "bug"] - }, - { - "title": "## 🧪 Tests", - "labels": ["test"] - }, - { - "title": "## 📝 Documentation", - "labels": ["documentation", "docs"] - }, - { - "title": "## 🔧 Maintenance", - "labels": ["chore", "dependencies"] - }, - { - "title": "## 🎨 Style", - "labels": ["style"] - }, - { - "title": "## ⚡ Performance", - "labels": ["performance"] - }, - { - "title": "## 🔄 Refactor", - "labels": ["refactor"] - } - ], - "ignore_labels": [ - "ignore", - "skip-changelog" - ], - "sort": "ASC", - "template": "#{{CHANGELOG}}\n\n
\nUncategorized\n\n#{{UNCATEGORIZED}}\n
", - "pr_template": "- #{{TITLE}} by @#{{AUTHOR}} in ##{{NUMBER}}", - "empty_template": "- No changes", - "label_extractor": [ - { - "pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(.+\\))?!?:", - "target": "$1", - "on_property": "title" - } - ], - "transformers": [ - { - "pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(.+\\))?!?:", - "target": "" - } - ], - "max_tags_to_fetch": 200, - "max_pull_requests": 200, - "max_back_track_time_days": 365, - "exclude_merge_branches": [], - "tag_resolver": { - "method": "semver" - } -} +{ + "categories": [ + { + "title": "## 🚀 Features", + "labels": ["feature", "enhancement"] + }, + { + "title": "## 🐛 Fixes", + "labels": ["fix", "bugfix", "bug"] + }, + { + "title": "## 🧪 Tests", + "labels": ["test"] + }, + { + "title": "## 📝 Documentation", + "labels": ["documentation", "docs"] + }, + { + "title": "## 🔧 Maintenance", + "labels": ["chore", "dependencies"] + }, + { + "title": "## 🎨 Style", + "labels": ["style"] + }, + { + "title": "## ⚡ Performance", + "labels": ["performance"] + }, + { + "title": "## 🔄 Refactor", + "labels": ["refactor"] + } + ], + "ignore_labels": [ + "ignore", + "skip-changelog" + ], + "sort": "ASC", + "template": "#{{CHANGELOG}}\n\n
\nUncategorized\n\n#{{UNCATEGORIZED}}\n
", + "pr_template": "- #{{TITLE}} by @#{{AUTHOR}} in ##{{NUMBER}}", + "empty_template": "- No changes", + "label_extractor": [ + { + "pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(.+\\))?!?:", + "target": "$1", + "on_property": "title" + } + ], + "transformers": [ + { + "pattern": "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(.+\\))?!?:", + "target": "" + } + ], + "max_tags_to_fetch": 200, + "max_pull_requests": 200, + "max_back_track_time_days": 365, + "exclude_merge_branches": [], + "tag_resolver": { + "method": "semver" + } +} diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 84daae6..a55195b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,344 +1,343 @@ -# Echo Client - Copilot Instructions - -## Project Overview - -Echo Client is a command-line console tool designed for the Echo-live/OBS workflow, specifically for silent VTubers and content creators who need to send subtitles in batches. It provides: - -- A local WebSocket server that listens for Echo-live broadcast connections -- An interactive CLI with Rich-based colorful terminal output -- Support for rich text formatting with Markdown and fast formatting shortcuts -- Typewriting effects with automatic pause insertion -- Configurable message decorations (quotes, parentheses, username brackets) -- Programmable message suffix support (e.g., auto-append "喵") -- Interrupt protection (Ctrl+C guard) -- Batch script execution via `/source` command -- Cross-platform packaging support with PyInstaller - -## Key Technologies - -- **Language**: Python 3.9+ -- **WebSocket Server**: `websockets` library (asyncio-based) -- **CLI Framework**: `rich` for terminal UI -- **Chinese Processing**: `jieba` for word segmentation, `pypinyin` for pinyin conversion -- **Markdown Parsing**: `markdown-it-py` -- **Configuration**: PyYAML for YAML-based config files -- **Package Management**: Poetry -- **Code Quality**: pylint, black, isort, pre-commit hooks - -## Project Structure - -``` -echo-client/ -├── echo_client/ # Main package -│ ├── __init__.py # Package initialization -│ ├── cli.py # CLI entry point (main function) -│ ├── server.py # EchoServer class - WebSocket server and console orchestration -│ ├── commands.py # Command parsing and catalog system -│ ├── config.py # Configuration loading/saving (config.yaml) -│ └── message.py # Message parsing, rendering, typewriting, and formatting -├── main.py # Compatibility entry point -├── pyproject.toml # Poetry dependencies and project metadata -├── poetry.lock # Locked dependencies -├── .pylintrc # Pylint configuration -├── .pre-commit-config.yaml # Pre-commit hooks configuration -├── echo-client.spec # PyInstaller spec for building executables -└── message_sample.txt # Example script demonstrating all features -``` - -## Echo-live WebSocket Protocol - -Echo Client implements a WebSocket server that Echo-live connects to as a client. This section provides comprehensive documentation about the protocol. - -### Overview - -The WebSocket protocol enables bidirectional communication between Echo Client (server) and Echo-live (client). Echo Client sends message data and control commands, while Echo-live sends status updates and events back. - -### Complete Protocol Documentation References - -#### Message Format Documentation -- **Base Message Format**: https://echo-live-doc.pages.dev/message/base/ -- **Start Paragraph**: https://echo-live-doc.pages.dev/message/start-par/ - Controls paragraph-level display -- **Style Formatting**: https://echo-live-doc.pages.dev/message/style/ - Text styling (bold, italic, color, size, etc.) -- **Pause Events**: https://echo-live-doc.pages.dev/message/pause/ - Insert timed pauses in message display -- **General Events**: https://echo-live-doc.pages.dev/message/event/ - Event system for typewriting and animations -- **Paragraph Management**: https://echo-live-doc.pages.dev/message/paragraph/ - Multi-paragraph message handling - -#### Broadcast API Documentation -- **Broadcast Overview**: https://echo-live-doc.pages.dev/dev/broadcast/ - Main broadcast protocol page -- **API Reference**: https://echo-live-doc.pages.dev/dev/broadcast/api/ - Complete API listing - -#### Broadcast API Actions (Server → Client) - -**Connection & Lifecycle:** -- **hello**: https://echo-live-doc.pages.dev/dev/broadcast/api/hello/ - Initial handshake from Echo-live client -- **ping**: https://echo-live-doc.pages.dev/dev/broadcast/api/ping/ - Heartbeat mechanism for connection monitoring -- **websocket_heartbeat**: https://echo-live-doc.pages.dev/dev/broadcast/api/websocket_heartbeat/ - Alternative heartbeat format -- **close**: https://echo-live-doc.pages.dev/dev/broadcast/api/close/ - Graceful connection closure -- **websocket_close**: https://echo-live-doc.pages.dev/dev/broadcast/api/websocket_close/ - WebSocket-level close event -- **shutdown**: https://echo-live-doc.pages.dev/dev/broadcast/api/shutdown/ - Server shutdown notification - -**Display State Management:** -- **page_visible**: https://echo-live-doc.pages.dev/dev/broadcast/api/page_visible/ - Page becomes visible (tab focused) -- **page_hidden**: https://echo-live-doc.pages.dev/dev/broadcast/api/page_hidden/ - Page becomes hidden (tab unfocused) -- **live_display_update**: https://echo-live-doc.pages.dev/dev/broadcast/api/live_display_update/ - Display state changed -- **set_live_display**: https://echo-live-doc.pages.dev/dev/broadcast/api/set_live_display/ - Set display visibility - -**Message Control:** -- **echo_next**: https://echo-live-doc.pages.dev/dev/broadcast/api/echo_next/ - Skip to next message -- **echo_printing**: https://echo-live-doc.pages.dev/dev/broadcast/api/echo_printing/ - Typewriting progress update -- **echo_state_update**: https://echo-live-doc.pages.dev/dev/broadcast/api/echo_state_update/ - Message state changed -- **history_clear**: https://echo-live-doc.pages.dev/dev/broadcast/api/history_clear/ - Clear message history - -**Error Handling:** -- **error**: https://echo-live-doc.pages.dev/dev/broadcast/api/error/ - General error response -- **error_unknown**: https://echo-live-doc.pages.dev/dev/broadcast/api/error_unknown/ - Unknown action error - -### Message Structure - -Messages sent to Echo-live follow this general structure: -```json -{ - "action": "send", - "data": { - "username": "Someone", - "messages": [...], // Array of message segments with formatting - "events": [...] // Optional events like typewriting or pause - } -} -``` - -### Core Message Components - -#### 1. Message Segments (`messages` array) -Each message consists of segments with text and optional styling: -```json -{ - "text": "Hello", - "style": { - "color": "#66ccff", - "bold": true, - "italic": false, - "size": "middle" - } -} -``` - -Supported style properties: -- **color**: Hex color code or named color -- **bold**: Boolean for bold text -- **italic**: Boolean for italic text -- **underline**: Boolean for underlined text -- **strike**: Boolean for strikethrough -- **size**: String - "extra-small", "small", "middle", "large", "extra-large" -- **className**: String - CSS class name (optionally prefixed with "echo-text-") - -#### 2. Events Array -Events control display timing and animations: - -**Pause Event:** -```json -{ - "name": "pause", - "duration": 500 // milliseconds -} -``` - -**Typewriting Event:** -```json -{ - "name": "echo", - "text": "你好", - "data": "ni3 hao3", // pinyin or zhuyin representation - "speed": 10 // ms per character -} -``` - -#### 3. Paragraph Control -Use `start-par` property to control paragraph display: -```json -{ - "action": "send", - "data": { - "username": "Someone", - "messages": [...], - "start-par": false // Continue in same paragraph vs. new paragraph - } -} -``` - -### Client-to-Server Messages - -Echo-live clients send these actions to the server: - -1. **hello** - Initial connection with client info -2. **ping** - Heartbeat to maintain connection -3. **page_visible/page_hidden** - Tab visibility changes -4. **echo_state_update** - Message display state changes -5. **echo_printing** - Typewriting progress updates -6. **live_display_update** - Display visibility changes -7. **close** - Graceful disconnect notification - -### Server Response Pattern - -For most client messages, the server should: -1. Update internal state (client tracking, heartbeat counts, visibility) -2. Optionally respond with acknowledgment (not required for heartbeats) -3. Log events to console for debugging - -### Implementation in Echo Client - -The `EchoServer` class handles the protocol: -- `_handle_client()` - Main WebSocket handler -- Tracks client state in multiple dicts (IDs, names, types, visibility, heartbeat counts) -- Sends messages via `_broadcast_to_websocket()` -- Processes incoming events and updates console display - -### Protocol Best Practices - -1. **Always send valid JSON** - Echo-live will reject malformed messages -2. **Include username** - Required field for all "send" actions -3. **Validate message structure** - Use the parsing functions in `message.py` -4. **Handle disconnects gracefully** - Clean up client state on disconnect -5. **Monitor heartbeats** - Track ping messages to detect dead connections -6. **Respect display visibility** - Don't send messages when page is hidden (optional) -7. **Use typewriting carefully** - Requires proper text segmentation for Chinese - -## Development Workflow - -### Setup -```bash -poetry install # Install all dependencies -poetry run echo-client # Run the application -``` - -### Linting and Code Quality -```bash -poetry run pylint echo_client/ # Run pylint -poetry run black echo_client/ # Format code (via pre-commit) -poetry run isort echo_client/ # Sort imports (via pre-commit) -pre-commit run --all-files # Run all pre-commit hooks -``` - -### Building Executables -```bash -pip install pyinstaller -pyinstaller echo-client.spec # Creates dist/echo-client.exe -``` - -## Code Style and Conventions - -### Python Style -- Follow PEP 8 with max line length of 150 characters (see `.pylintrc`) -- Use type hints (`from __future__ import annotations`) -- Docstrings for modules (present) but not strictly enforced for all functions -- Pylint score target: 9.5+ out of 10 - -### Naming Conventions -- Classes: PascalCase (e.g., `EchoServer`, `CommandCatalog`) -- Functions/Methods: snake_case (e.g., `load_config`, `parse_message`) -- Constants: UPPER_SNAKE_CASE (e.g., `PING_PAYLOAD`, `DEFAULT_PRINT_SPEED`) -- Private methods: prefix with `_` (e.g., `_cmd_help`) - -### Async/Await Patterns -- Use `asyncio` for all WebSocket and I/O operations -- Server runs in `asyncio.run(server.run())` -- WebSocket handlers are async coroutines -- Console I/O uses `asyncio.to_thread()` for blocking operations - -## Configuration System - -The application uses YAML-based configuration stored in `config.yaml`: - -### Key Configuration Fields -- **Server Settings**: `host` (default: 127.0.0.1), `port` (default: 3000) -- **Display Settings**: `username`, `username_brackets` -- **Typewriting**: `typewriting`, `typewriting_scheme` (pinyin/zhuyin), `print_speed` -- **Auto Features**: `autopause`, `quote_style`, `auto_parentheses`, `auto_suffix` -- **Custom Quotes**: `quote_custom_left`, `quote_custom_right` (仅在 `quote_style=custom` 时生效) -- **Control**: `inhibit_ctrl_c`, `command_prefix` (default: `/`) - -Configuration is hot-reloaded on each message send, allowing runtime modifications. - -## Message Formatting System - -Echo Client supports two overlay formatting systems: - -### 1. Markdown Syntax -- `**text**` or `__text__` → Bold -- `*text*` or `_text_` → Italic -- `` `code` `` → Code style - -### 2. Fast Formatting (@ prefix) -- Style: `@b` (bold), `@i` (italic), `@u` (underline), `@s` (strikethrough) -- Color: `@[#66ccff]` or `@[color-name]` -- Size: `@+` (larger), `@-` (smaller) - can stack -- Reset: `@r` (restore default) -- Emoji: `@{emoji-id}` -- CSS Class: `@` (adds `echo-text-` prefix) or `@<:classname>` (raw) -- Literal: `\@` → `@` - -Both systems can be combined in the same message. - -## Command System - -Commands use a pluggable catalog system defined in `commands.py`: - -### Command Structure -- Commands start with `command_prefix` (default: `/`) -- Support aliases (e.g., `/h`, `/?` for `/help`) -- Can have arguments and usage hints -- Toggle commands show current state -- Input starting with `//` sends literal `/` text - -### Adding New Commands -1. Add command method to `EchoServer` (prefix with `_cmd_`) -2. Define `CommandSpec` in `build_command_specs()` -3. Register aliases and argument hints -4. Update status display if it's a toggle command - -## Testing - -Currently, the project does not have automated unit tests. Testing is done manually by: -1. Running the application with `poetry run echo-client` -2. Executing commands interactively -3. Using `/source message_sample.txt` for comprehensive feature testing -4. Connecting with Echo-live in OBS to test WebSocket protocol - -## Common Pitfalls and Gotchas - -1. **Async Context**: All WebSocket operations must be async. Use `asyncio.to_thread()` for blocking console I/O. -2. **Config Reloading**: Config is reloaded on each message send, not on server start. Changes take effect immediately. -3. **Message Escaping**: Double `//` at start escapes to send literal `/` text. The `@` character uses `\@` for escaping. -4. **Client Tracking**: Server maintains multiple dicts for client state (IDs, names, types, visibility, heartbeat counts). -5. **Signal Handling**: When `inhibit_ctrl_c` is enabled, SIGINT is ignored. Use `/quit` or disable with `/nocc`. -6. **Typewriting**: Requires jieba word segmentation for Chinese text, then converts to pinyin/zhuyin per character. - -## Dependencies and Security - -- Keep Poetry dependencies updated via `poetry update` -- Major dependencies: - - `websockets ^12.0` - WebSocket server - - `rich ^13.7.0` - Terminal UI - - `pypinyin ^0.50.0` - Pinyin conversion - - `pyyaml ^6.0.1` - Config parsing - - `jieba ^0.42.1` - Chinese segmentation - - `markdown-it-py ^3.0.0` - Markdown parsing - - `aiohttp ^3.9.0` - Async HTTP (if needed) - -## Internationalization - -The project is primarily designed for Chinese-speaking users: -- UI messages and console output are in Chinese -- Documentation (README.md) is in Chinese -- Supports both Simplified Chinese input and processing -- Typewriting supports pinyin (拼音) and zhuyin (注音/Bopomofo) schemes - -## Future Development Considerations - -When extending the project: -- Maintain backward compatibility with config.yaml format -- Ensure new commands follow the established pattern -- Keep the WebSocket protocol aligned with Echo-live specifications -- Test with actual Echo-live instances in OBS -- Update `message_sample.txt` with examples of new features -- Consider performance impact of message parsing (it's per-message) +# Echo Client - Copilot Instructions + +## Project Overview + +Echo Client is a command-line console tool designed for the Echo-live/OBS workflow, specifically for silent VTubers and content creators who need to send subtitles in batches. It provides: + +- A local WebSocket server that listens for Echo-live broadcast connections +- An interactive CLI with Rich-based colorful terminal output +- Support for rich text formatting with Markdown and fast formatting shortcuts +- Typewriting effects with automatic pause insertion +- Configurable message decorations (quotes, parentheses, username brackets) +- Programmable message suffix support (e.g., auto-append "喵") +- Interrupt protection (Ctrl+C guard) +- Batch script execution via `/source` command +- Cross-platform packaging support with PyInstaller + +## Key Technologies + +- **Language**: Python 3.12+ +- **WebSocket/HTTP Server**: `aiohttp` web application with WebSocket routes +- **CLI Framework**: `rich` for terminal UI +- **Chinese Processing**: `jieba` for word segmentation, `pypinyin` for pinyin conversion +- **Markdown Parsing**: `markdown-it-py` +- **Configuration**: PyYAML for YAML-based config files +- **Package Management**: Poetry +- **Code Quality**: pylint, black, isort, pre-commit hooks + +## Project Structure + +``` +echo-client/ +├── echo_client/ # Main package +│ ├── __init__.py # Package initialization +│ ├── cli.py # CLI entry point (main function) +│ ├── server.py # EchoServer class - WebSocket server and console orchestration +│ ├── commands.py # Command parsing and catalog system +│ ├── config.py # Configuration loading/saving (config.yaml) +│ └── message.py # Message parsing, rendering, typewriting, and formatting +├── main.py # Compatibility entry point +├── pyproject.toml # Poetry dependencies and project metadata +├── poetry.lock # Locked dependencies +├── .pylintrc # Pylint configuration +├── .pre-commit-config.yaml # Pre-commit hooks configuration +├── echo-client.spec # PyInstaller spec for building executables +└── message_sample.txt # Example script demonstrating all features +``` + +## Echo-live WebSocket Protocol + +Echo Client implements a WebSocket server that Echo-live connects to as a client. This section provides comprehensive documentation about the protocol. + +### Overview + +The WebSocket protocol enables bidirectional communication between Echo Client (server) and Echo-live (client). Echo Client sends message data and control commands, while Echo-live sends status updates and events back. + +### Complete Protocol Documentation References + +#### Message Format Documentation +- **Base Message Format**: https://echo-live-doc.pages.dev/message/base/ +- **Start Paragraph**: https://echo-live-doc.pages.dev/message/start-par/ - Controls paragraph-level display +- **Style Formatting**: https://echo-live-doc.pages.dev/message/style/ - Text styling (bold, italic, color, size, etc.) +- **Pause Events**: https://echo-live-doc.pages.dev/message/pause/ - Insert timed pauses in message display +- **General Events**: https://echo-live-doc.pages.dev/message/event/ - Event system for typewriting and animations +- **Paragraph Management**: https://echo-live-doc.pages.dev/message/paragraph/ - Multi-paragraph message handling + +#### Broadcast API Documentation +- **Broadcast Overview**: https://echo-live-doc.pages.dev/dev/broadcast/ - Main broadcast protocol page +- **API Reference**: https://echo-live-doc.pages.dev/dev/broadcast/api/ - Complete API listing + +#### Broadcast API Actions (Server → Client) + +**Connection & Lifecycle:** +- **hello**: https://echo-live-doc.pages.dev/dev/broadcast/api/hello/ - Initial handshake from Echo-live client +- **ping**: https://echo-live-doc.pages.dev/dev/broadcast/api/ping/ - Heartbeat mechanism for connection monitoring +- **websocket_heartbeat**: https://echo-live-doc.pages.dev/dev/broadcast/api/websocket_heartbeat/ - Alternative heartbeat format +- **close**: https://echo-live-doc.pages.dev/dev/broadcast/api/close/ - Graceful connection closure +- **websocket_close**: https://echo-live-doc.pages.dev/dev/broadcast/api/websocket_close/ - WebSocket-level close event +- **shutdown**: https://echo-live-doc.pages.dev/dev/broadcast/api/shutdown/ - Server shutdown notification + +**Display State Management:** +- **page_visible**: https://echo-live-doc.pages.dev/dev/broadcast/api/page_visible/ - Page becomes visible (tab focused) +- **page_hidden**: https://echo-live-doc.pages.dev/dev/broadcast/api/page_hidden/ - Page becomes hidden (tab unfocused) +- **live_display_update**: https://echo-live-doc.pages.dev/dev/broadcast/api/live_display_update/ - Display state changed +- **set_live_display**: https://echo-live-doc.pages.dev/dev/broadcast/api/set_live_display/ - Set display visibility + +**Message Control:** +- **echo_next**: https://echo-live-doc.pages.dev/dev/broadcast/api/echo_next/ - Skip to next message +- **echo_printing**: https://echo-live-doc.pages.dev/dev/broadcast/api/echo_printing/ - Typewriting progress update +- **echo_state_update**: https://echo-live-doc.pages.dev/dev/broadcast/api/echo_state_update/ - Message state changed +- **history_clear**: https://echo-live-doc.pages.dev/dev/broadcast/api/history_clear/ - Clear message history + +**Error Handling:** +- **error**: https://echo-live-doc.pages.dev/dev/broadcast/api/error/ - General error response +- **error_unknown**: https://echo-live-doc.pages.dev/dev/broadcast/api/error_unknown/ - Unknown action error + +### Message Structure + +Messages sent to Echo-live follow this general structure: +```json +{ + "action": "send", + "data": { + "username": "Someone", + "messages": [...], // Array of message segments with formatting + "events": [...] // Optional events like typewriting or pause + } +} +``` + +### Core Message Components + +#### 1. Message Segments (`messages` array) +Each message consists of segments with text and optional styling: +```json +{ + "text": "Hello", + "style": { + "color": "#66ccff", + "bold": true, + "italic": false, + "size": "middle" + } +} +``` + +Supported style properties: +- **color**: Hex color code or named color +- **bold**: Boolean for bold text +- **italic**: Boolean for italic text +- **underline**: Boolean for underlined text +- **strike**: Boolean for strikethrough +- **size**: String - "extra-small", "small", "middle", "large", "extra-large" +- **className**: String - CSS class name (optionally prefixed with "echo-text-") + +#### 2. Events Array +Events control display timing and animations: + +**Pause Event:** +```json +{ + "name": "pause", + "duration": 500 // milliseconds +} +``` + +**Typewriting Event:** +```json +{ + "name": "echo", + "text": "你好", + "data": "ni3 hao3", // pinyin or zhuyin representation + "speed": 10 // ms per character +} +``` + +#### 3. Paragraph Control +Use `start-par` property to control paragraph display: +```json +{ + "action": "send", + "data": { + "username": "Someone", + "messages": [...], + "start-par": false // Continue in same paragraph vs. new paragraph + } +} +``` + +### Client-to-Server Messages + +Echo-live clients send these actions to the server: + +1. **hello** - Initial connection with client info +2. **ping** - Heartbeat to maintain connection +3. **page_visible/page_hidden** - Tab visibility changes +4. **echo_state_update** - Message display state changes +5. **echo_printing** - Typewriting progress updates +6. **live_display_update** - Display visibility changes +7. **close** - Graceful disconnect notification + +### Server Response Pattern + +For most client messages, the server should: +1. Update internal state (client tracking, heartbeat counts, visibility) +2. Optionally respond with acknowledgment (not required for heartbeats) +3. Log events to console for debugging + +### Implementation in Echo Client + +The `EchoServer` class handles the protocol: +- `_handle_client()` - Main WebSocket handler +- Tracks client state in multiple dicts (IDs, names, types, visibility, heartbeat counts) +- Sends messages via `_broadcast_to_websocket()` +- Processes incoming events and updates console display + +### Protocol Best Practices + +1. **Always send valid JSON** - Echo-live will reject malformed messages +2. **Include username** - Required field for all "send" actions +3. **Validate message structure** - Use the parsing functions in `message.py` +4. **Handle disconnects gracefully** - Clean up client state on disconnect +5. **Monitor heartbeats** - Track ping messages to detect dead connections +6. **Respect display visibility** - Don't send messages when page is hidden (optional) +7. **Use typewriting carefully** - Requires proper text segmentation for Chinese + +## Development Workflow + +### Setup +```bash +poetry install # Install all dependencies +poetry run echo-client # Run the application +``` + +### Linting and Code Quality +```bash +poetry run pylint echo_client/ # Run pylint +poetry run black echo_client/ # Format code (via pre-commit) +poetry run isort echo_client/ # Sort imports (via pre-commit) +pre-commit run --all-files # Run all pre-commit hooks +``` + +### Building Executables +```bash +pip install pyinstaller +pyinstaller echo-client.spec # Creates dist/echo-client.exe +``` + +## Code Style and Conventions + +### Python Style +- Follow PEP 8 with max line length of 150 characters (see `.pylintrc`) +- Use type hints (`from __future__ import annotations`) +- Docstrings for modules (present) but not strictly enforced for all functions +- Pylint score target: 9.5+ out of 10 + +### Naming Conventions +- Classes: PascalCase (e.g., `EchoServer`, `CommandCatalog`) +- Functions/Methods: snake_case (e.g., `load_config`, `parse_message`) +- Constants: UPPER_SNAKE_CASE (e.g., `PING_PAYLOAD`, `DEFAULT_PRINT_SPEED`) +- Private methods: prefix with `_` (e.g., `_cmd_help`) + +### Async/Await Patterns +- Use `asyncio` for all WebSocket and I/O operations +- Server runs in `asyncio.run(server.run())` +- WebSocket handlers are async coroutines +- Console I/O uses `asyncio.to_thread()` for blocking operations + +## Configuration System + +The application uses YAML-based configuration stored in `config.yaml`: + +### Key Configuration Fields +- **Server Settings**: `host` (default: 127.0.0.1), `port` (default: 3000) +- **Display Settings**: `username`, `username_brackets` +- **Typewriting**: `typewriting`, `typewriting_scheme` (pinyin/zhuyin), `print_speed` +- **Auto Features**: `autopause`, `quote_style`, `auto_parentheses`, `auto_suffix` +- **Custom Quotes**: `quote_custom_left`, `quote_custom_right` (仅在 `quote_style=custom` 时生效) +- **Control**: `inhibit_ctrl_c`, `command_prefix` (default: `/`) + +Configuration is hot-reloaded on each message send, allowing runtime modifications. + +## Message Formatting System + +Echo Client supports two overlay formatting systems: + +### 1. Markdown Syntax +- `**text**` or `__text__` → Bold +- `*text*` or `_text_` → Italic +- `` `code` `` → Code style + +### 2. Fast Formatting (@ prefix) +- Style: `@b` (bold), `@i` (italic), `@u` (underline), `@s` (strikethrough) +- Color: `@[#66ccff]` or `@[color-name]` +- Size: `@+` (larger), `@-` (smaller) - can stack +- Reset: `@r` (restore default) +- Emoji: `@{emoji-id}` +- CSS Class: `@` (adds `echo-text-` prefix) or `@<:classname>` (raw) +- Literal: `\@` → `@` + +Both systems can be combined in the same message. + +## Command System + +Commands use a pluggable catalog system defined in `commands.py`: + +### Command Structure +- Commands start with `command_prefix` (default: `/`) +- Support aliases (e.g., `/h`, `/?` for `/help`) +- Can have arguments and usage hints +- Toggle commands show current state +- Input starting with `//` sends literal `/` text + +### Adding New Commands +1. Add command method to `EchoServer` (prefix with `_cmd_`) +2. Define `CommandSpec` in `build_command_specs()` +3. Register aliases and argument hints +4. Update status display if it's a toggle command + +## Testing + +Currently, the project does not have automated unit tests. Testing is done manually by: +1. Running the application with `poetry run echo-client` +2. Executing commands interactively +3. Using `/source message_sample.txt` for comprehensive feature testing +4. Connecting with Echo-live in OBS to test WebSocket protocol + +## Common Pitfalls and Gotchas + +1. **Async Context**: All WebSocket operations must be async. Use `asyncio.to_thread()` for blocking console I/O. +2. **Config Reloading**: Config is reloaded on each message send, not on server start. Changes take effect immediately. +3. **Message Escaping**: Double `//` at start escapes to send literal `/` text. The `@` character uses `\@` for escaping. +4. **Client Tracking**: Server maintains multiple dicts for client state (IDs, names, types, visibility, heartbeat counts). +5. **Signal Handling**: When `inhibit_ctrl_c` is enabled, SIGINT is ignored. Use `/quit` or disable with `/nocc`. +6. **Typewriting**: Requires jieba word segmentation for Chinese text, then converts to pinyin/zhuyin per character. + +## Dependencies and Security + +- Keep Poetry dependencies updated via `poetry update` +- Major dependencies: + - `aiohttp ^3.11.0` - WebSocket and HTTP server + - `rich ^14.0.0` - Terminal UI + - `pypinyin ^0.55.0` - Pinyin conversion + - `pyyaml ^6.0.1` - Config parsing + - `jieba ^0.42.1` - Chinese segmentation + - `markdown-it-py ^3.0.0` - Markdown parsing + +## Internationalization + +The project is primarily designed for Chinese-speaking users: +- UI messages and console output are in Chinese +- Documentation (README.md) is in Chinese +- Supports both Simplified Chinese input and processing +- Typewriting supports pinyin (拼音) and zhuyin (注音/Bopomofo) schemes + +## Future Development Considerations + +When extending the project: +- Maintain backward compatibility with config.yaml format +- Ensure new commands follow the established pattern +- Keep the WebSocket protocol aligned with Echo-live specifications +- Test with actual Echo-live instances in OBS +- Update `message_sample.txt` with examples of new features +- Consider performance impact of message parsing (it's per-message) diff --git a/.github/workflows/nuitka-build.yml b/.github/workflows/nuitka-build.yml index 622f89b..29e78a7 100644 --- a/.github/workflows/nuitka-build.yml +++ b/.github/workflows/nuitka-build.yml @@ -1,25 +1,25 @@ ---- -name: Build with Nuitka - -"on": - push: - branches: - - master - tags: - - 'v*' - pull_request: - workflow_dispatch: - -jobs: - build-windows: - runs-on: windows-latest - permissions: - contents: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - +--- +name: Build with Nuitka + +"on": + push: + branches: + - master + tags: + - 'v*' + pull_request: + workflow_dispatch: + +jobs: + build-windows: + runs-on: windows-latest + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Set up Python uses: actions/setup-python@v5 with: @@ -28,53 +28,66 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip + pip install . pytest pip install -r pyrequirements.txt + - name: Read package version + shell: pwsh + run: | + $version = python -c "import pathlib, tomllib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text(encoding='utf-8'))['tool']['poetry']['version'])" + "ECHO_CLIENT_VERSION=$version" >> $env:GITHUB_ENV + + - name: Verify package + run: | + python -m compileall echo_client main.py + python -m pytest + python -m pip install . + - name: Build with Nuitka run: | python -m nuitka ` - --mode=onefile ` - --onefile-child-grace-time=infinity ` - --include-package=websockets.asyncio ` - --include-package-data=pypinyin ` - --include-package-data=jieba ` - --windows-icon-from-ico=realme_sheep_triangle.ico ` - --assume-yes-for-downloads ` - --output-filename=echo-client.exe ` - --output-dir=dist ` + --mode=onefile ` + --onefile-child-grace-time=infinity ` + --include-package=aiohttp ` + --include-package-data=pypinyin ` + --include-package-data=jieba ` + --windows-icon-from-ico=realme_sheep_triangle.ico ` + --assume-yes-for-downloads ` + --output-filename=echo-client.exe ` + --output-dir=dist ` --remove-output ` --company-name="Echo-Client" ` --product-name="Echo Client" ` - --file-version="0.1.0" ` - --product-version="0.1.0" ` + --file-version="${{ env.ECHO_CLIENT_VERSION }}" ` + --product-version="${{ env.ECHO_CLIENT_VERSION }}" ` --file-description="Echo-live WebSocket CLI Server" ` --lto=yes ` - --jobs=4 ` - main.py - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: echo-client-windows-${{ github.sha }} - path: dist/echo-client.exe - - - name: Generate Changelog - if: startsWith(github.ref, 'refs/tags/') - id: changelog - uses: mikepenz/release-changelog-builder-action@v4 - with: - configuration: ".github/changelog-config.json" - ignorePreReleases: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Release - if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@v1 - with: - files: dist/echo-client.exe - body: ${{ steps.changelog.outputs.changelog }} - draft: false - prerelease: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + --jobs=4 ` + main.py + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: echo-client-windows-${{ github.sha }} + path: dist/echo-client.exe + + - name: Generate Changelog + if: startsWith(github.ref, 'refs/tags/') + id: changelog + uses: mikepenz/release-changelog-builder-action@v4 + with: + configuration: ".github/changelog-config.json" + ignorePreReleases: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: dist/echo-client.exe + body: ${{ steps.changelog.outputs.changelog }} + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pyinstaller-build.yml b/.github/workflows/pyinstaller-build.yml index 17486be..59be471 100644 --- a/.github/workflows/pyinstaller-build.yml +++ b/.github/workflows/pyinstaller-build.yml @@ -1,63 +1,69 @@ ---- -name: Build with PyInstaller - -"on": - push: - branches: - - master - tags: - - 'v*' - pull_request: - workflow_dispatch: - -jobs: - build-windows: - runs-on: windows-latest - permissions: - contents: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - +--- +name: Build with PyInstaller + +"on": + push: + branches: + - master + tags: + - 'v*' + pull_request: + workflow_dispatch: + +jobs: + build-windows: + runs-on: windows-latest + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install . pytest pip install pyinstaller + - name: Verify package + run: | + python -m compileall echo_client main.py + python -m pytest + python -m pip install . + - name: Build with PyInstaller run: | pyinstaller echo-client.spec - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: echo-client-pyinstaller-windows-${{ github.sha }} - path: dist/echo-client.exe - - - name: Generate Changelog - if: startsWith(github.ref, 'refs/tags/') - id: changelog - uses: mikepenz/release-changelog-builder-action@v4 - with: - configuration: ".github/changelog-config.json" - ignorePreReleases: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Release - if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@v1 - with: - files: dist/echo-client.exe - body: ${{ steps.changelog.outputs.changelog }} - draft: false - prerelease: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: echo-client-pyinstaller-windows-${{ github.sha }} + path: dist/echo-client.exe + + - name: Generate Changelog + if: startsWith(github.ref, 'refs/tags/') + id: changelog + uses: mikepenz/release-changelog-builder-action@v4 + with: + configuration: ".github/changelog-config.json" + ignorePreReleases: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: dist/echo-client.exe + body: ${{ steps.changelog.outputs.changelog }} + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 170020b..7b143bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,45 @@ # direnv cache .direnv -# python cache -__pycache__ -*.pyc -*.pyo -*.pyd -.Python -config.yaml - -# poetry build -dist/ -build/ - -# nix build -result - -# IDE -.vscode/ -.idea/ - -# Logs -*.log - -# PyInstaller -*.spec.bak -# Keep echo-client.spec in repository - -# Pytest -.pytest_cache/ -.coverage -htmlcov/ - -# Virtual environments -venv/ -env/ -ENV/ - -# OS files -.DS_Store -Thumbs.db \ No newline at end of file +# local protocol reference checkout +.reference/ + +# python cache +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +config.yaml + +# poetry build +dist/ +build/ + +# nix build +result + +# IDE +.vscode/ +.idea/ + +# Logs +*.log + +# PyInstaller +*.spec.bak +# Keep echo-client.spec in repository + +# Pytest +.pytest_cache/ +.coverage +htmlcov/ + +# Virtual environments +venv/ +env/ +ENV/ + +# OS files +.DS_Store +Thumbs.db diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 156d1a7..f07b3fe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,22 +1,22 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 - hooks: - - id: check-yaml - - id: trailing-whitespace - - id: check-ast +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + - id: trailing-whitespace + - id: check-ast - repo: https://github.com/psf/black - rev: 23.12.1 - hooks: - - id: black + rev: 24.10.0 + hooks: + - id: black - repo: https://github.com/pycqa/isort rev: 5.13.2 - hooks: - - id: isort - - repo: local - hooks: - - id: lint - name: lint code - entry: poetry run pylint - language: system - files: "^(.*)\\.pyi?$" + hooks: + - id: isort + - repo: local + hooks: + - id: lint + name: lint code + entry: poetry run pylint + language: system + files: "^(.*)\\.pyi?$" diff --git a/.pylintrc b/.pylintrc index 98cee80..d0174ac 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,7 +1,7 @@ -[MASTER] -jobs = 0 -max-line-length = 150 -disable = fixme, redefined-outer-name - -[MESSAGES CONTROL] -disable = R +[MASTER] +jobs = 0 +max-line-length = 150 +disable = fixme, redefined-outer-name + +[MESSAGES CONTROL] +disable = R diff --git a/README.md b/README.md index e0b42a3..219a096 100644 --- a/README.md +++ b/README.md @@ -1,191 +1,196 @@ -# Echo Client - -一个为 Echo-live/OBS 工作流打造的命令行控制台,专注于让无声系虚拟主播和需要批量发送字幕的创作者高效控制弹幕展示。它提供可视化的 CLI 体验、本地配置持久化、丰富的富文本格式,并支持打包为单文件可执行程序。 - -> ⚠️ 项目仍在积极开发中,行为可能随版本演化。本文档描述的是 `master` 分支当前实现。 - -## ✨ 主要特性 - -- **即开即用的本地 WebSocket 服务器**:在本机监听 Echo-live 广播端口,与 OBS 中的 Echo-live 无缝对接。 -- **交互式命令行体验**:基于 Rich 的彩色终端,提供命令提示与友好输出。 -- **富文本格式与快速标记**:支持 Markdown 强调语法与 `@` 前缀快捷码,快速叠加粗体、斜体、颜色、字号、类名等效果。 -- **Typewriting 与自动停顿**:按需生成打字机效果与自动插入停顿帧,让字幕播放更自然。 -- **可配置的消息修饰**:可自动为文本添加引号、括号,并为用户名套上【】以突出显示。 -- **可编程的消息后缀**:支持自动为文本追加自定义结尾字符(默认“喵”),并提供语义判断避免对无意义内容添尾。 -- **退出保护**:默认屏蔽 `Ctrl+C` 等中断信号,避免误触;可通过命令即时切换。 -- **批量脚本执行**:通过 `/source` 命令导入 `message_sample.txt` 等脚本文件,实现自动播报。 -- **跨平台打包**:使用 Nuitka 编译打包,可将工具封装为优化的单文件可执行程序。 - -## 🚀 快速开始 - -1. 在 OBS 中安装并配置最新的 Echo-live。 -2. 打开 Echo-live 的 `config.js`,设置 WebSocket: - - ```js - websocket_enable: true, - websocket_url: 'ws://127.0.0.1:3000' - ``` - - - 若 Echo-live 与本程序不在同一台设备,`websocket_url` 请改为服务器的 IP,且在 echo-client 的配置中把 `host` 改为 `0.0.0.0`。 +# Echo Client + +一个为 Echo-live/OBS 工作流打造的命令行控制台,专注于让无声系虚拟主播和需要批量发送字幕的创作者高效控制弹幕展示。它提供可视化的 CLI 体验、本地配置持久化、丰富的富文本格式,并支持打包为单文件可执行程序。 + +> ⚠️ 项目仍在积极开发中,行为可能随版本演化。本文档描述的是 `master` 分支当前实现。 + +## ✨ 主要特性 + +- **即开即用的本地 WebSocket 服务器**:在本机监听 Echo-live 广播端口,与 OBS 中的 Echo-live 无缝对接。 +- **交互式命令行体验**:基于 Rich 的彩色终端,提供命令提示与友好输出。 +- **富文本格式与快速标记**:支持 Markdown 强调语法与 `@` 前缀快捷码,快速叠加粗体、斜体、颜色、字号、类名等效果。 +- **Typewriting 与自动停顿**:按需生成打字机效果与自动插入停顿帧,让字幕播放更自然。 +- **可配置的消息修饰**:可自动为文本添加引号、括号,并为用户名套上【】以突出显示。 +- **可编程的消息后缀**:支持自动为文本追加自定义结尾字符(默认“喵”),并提供语义判断避免对无意义内容添尾。 +- **退出保护**:默认屏蔽 `Ctrl+C` 等中断信号,避免误触;可通过命令即时切换。 +- **批量脚本执行**:通过 `/source` 命令导入 `message_sample.txt` 等脚本文件,实现自动播报。 +- **跨平台打包**:使用 Nuitka 编译打包,可将工具封装为优化的单文件可执行程序。 + +## 🚀 快速开始 + +1. 在 OBS 中安装并配置最新的 Echo-live。 +2. 打开 Echo-live 的 `config.js`,设置 WebSocket: + + ```js + websocket_enable: true, + websocket_url: 'ws://127.0.0.1:3000' + ``` + + - 若 Echo-live 与本程序不在同一台设备,`websocket_url` 请改为服务器的 IP,且在 echo-client 的配置中把 `host` 改为 `0.0.0.0`。 3. 安装并运行 echo-client: ```powershell pip install echo-client - echo-client - ``` - - 或在源码仓库: - + echo-client + ``` + + 或在源码仓库: + ```powershell python -m pip install --upgrade pip - pip install -r requirements.txt + pip install . python -m echo_client.cli ``` 首次启动会在工作目录生成 `config.yaml`。终端会打印监听地址、配置路径等提示。 - -## 🔌 与 Echo-live 对接 - + +## 🔌 与 Echo-live 对接 + - Echo-live 的 WebSocket 客户端连接到 `ws://:`(默认 `127.0.0.1:3000`)。 +- 服务器基于 `aiohttp`,兼容根路径 `/`,并额外提供 `/ws` 作为 WebSocket 别名。 +- 可通过 `http://:/healthz` 检查服务健康状态和当前连接数量。 - 建议在 OBS 中刷新浏览器源以触发连接。 - 连接后,终端会显示客户端 ID、显示名称、心跳次数、实时展示状态等事件。 - -## ⚙️ 配置文件 - -配置保存于可执行文件或脚本所在目录的 `config.yaml`,字段说明如下: - -| 字段 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| `command_prefix` | `str` | `/` | 命令前缀。若要发送以 `/` 开头的消息,可输入 `//文本` 。| -| `username` | `str` | `Someone` | 推送给 Echo-live 的默认用户名,可交互命令 `/ren` 修改。| -| `host` | `str` | `127.0.0.1` | WebSocket 监听地址,跨设备使用请改为 `0.0.0.0`。| -| `port` | `int` | `3000` | WebSocket 监听端口。| -| `typewriting` | `bool` | `true` | 是否启用打字机同步。`/typewrite`(或 `/tt`)可切换。| -| `typewriting_scheme` | `str` | `pinyin` | 打字机模式,支持 `pinyin`(拼音)与 `zhuyin`(注音),`/scheme`(`/ts` 或 `/tts`)可切换。| -| `autopause` | `bool` | `false` | 自动插入停顿标记。`/autopause`(或 `/ta`)可切换。| -| `autopausestr` | `str` | `,,.。;;::!!` | 触发停顿的字符集合。| -| `autopausetime` | `int` | `10` | 停顿时长单位,取决于打印速度。| -| `print_speed` | `int` | `10` | 默认打印速度(毫秒/字符),`/speed `(或 `/ps`)可调整。| -| `quote_style` | `str` | `en` | 自动引号样式,可选 `en`(英文双引号)、`cn`(中文书名号)、`jp`(日式括号)、`custom`(使用下方自定义的左右符号)、`none`(禁用)。通过 `/quote