Add comprehensive CLI tool with interactive shell mode
- Created miku-cli.py: Full-featured CLI for Miku bot API - Added interactive shell mode for continuous command execution - Implemented all API endpoints: status, mood, autonomous, DM, blocking, profile pictures - Consistent hyphenated command naming across shell and regular modes - Created API_REFERENCE.md: Complete API endpoint documentation - Created CLI_README.md: User guide with examples and usage instructions - Error handling and user-friendly output formatting
This commit is contained in:
460
API_REFERENCE.md
Normal file
460
API_REFERENCE.md
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
# Miku Discord Bot API Reference
|
||||||
|
|
||||||
|
The Miku bot exposes a FastAPI REST API on port 3939 for controlling and monitoring the bot.
|
||||||
|
|
||||||
|
## Base URL
|
||||||
|
```
|
||||||
|
http://localhost:3939
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### 📊 Status & Information
|
||||||
|
|
||||||
|
#### `GET /status`
|
||||||
|
Get current bot status and overview.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "online",
|
||||||
|
"mood": "neutral",
|
||||||
|
"servers": 2,
|
||||||
|
"active_schedulers": 2,
|
||||||
|
"server_moods": {
|
||||||
|
"123456789": "bubbly",
|
||||||
|
"987654321": "excited"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `GET /logs`
|
||||||
|
Get the last 100 lines of bot logs.
|
||||||
|
|
||||||
|
**Response:** Plain text log output
|
||||||
|
|
||||||
|
#### `GET /prompt`
|
||||||
|
Get the last full prompt sent to the LLM.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"prompt": "Last prompt text..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 😊 Mood Management
|
||||||
|
|
||||||
|
#### `GET /mood`
|
||||||
|
Get current DM mood.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mood": "neutral",
|
||||||
|
"description": "Mood description text..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `POST /mood`
|
||||||
|
Set DM mood.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mood": "bubbly"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"new_mood": "bubbly"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `POST /mood/reset`
|
||||||
|
Reset DM mood to neutral.
|
||||||
|
|
||||||
|
#### `POST /mood/calm`
|
||||||
|
Calm Miku down (set to neutral).
|
||||||
|
|
||||||
|
#### `GET /servers/{guild_id}/mood`
|
||||||
|
Get mood for specific server.
|
||||||
|
|
||||||
|
#### `POST /servers/{guild_id}/mood`
|
||||||
|
Set mood for specific server.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mood": "excited"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `POST /servers/{guild_id}/mood/reset`
|
||||||
|
Reset server mood to neutral.
|
||||||
|
|
||||||
|
#### `GET /servers/{guild_id}/mood/state`
|
||||||
|
Get complete mood state for server.
|
||||||
|
|
||||||
|
#### `GET /moods/available`
|
||||||
|
List all available moods.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"moods": {
|
||||||
|
"neutral": "😊",
|
||||||
|
"bubbly": "🥰",
|
||||||
|
"excited": "🤩",
|
||||||
|
"sleepy": "😴",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 😴 Sleep Management
|
||||||
|
|
||||||
|
#### `POST /sleep`
|
||||||
|
Force Miku to sleep.
|
||||||
|
|
||||||
|
#### `POST /wake`
|
||||||
|
Wake Miku up.
|
||||||
|
|
||||||
|
#### `POST /bedtime?guild_id={guild_id}`
|
||||||
|
Send bedtime reminder. If `guild_id` is provided, sends only to that server.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🤖 Autonomous Actions
|
||||||
|
|
||||||
|
#### `POST /autonomous/general?guild_id={guild_id}`
|
||||||
|
Trigger autonomous general message.
|
||||||
|
|
||||||
|
#### `POST /autonomous/engage?guild_id={guild_id}`
|
||||||
|
Trigger autonomous user engagement.
|
||||||
|
|
||||||
|
#### `POST /autonomous/tweet?guild_id={guild_id}`
|
||||||
|
Trigger autonomous tweet sharing.
|
||||||
|
|
||||||
|
#### `POST /autonomous/reaction?guild_id={guild_id}`
|
||||||
|
Trigger autonomous reaction to a message.
|
||||||
|
|
||||||
|
#### `POST /autonomous/custom?guild_id={guild_id}`
|
||||||
|
Send custom autonomous message.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"prompt": "Say something funny about cats"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `GET /autonomous/stats`
|
||||||
|
Get autonomous engine statistics for all servers.
|
||||||
|
|
||||||
|
**Response:** Detailed stats including message counts, activity, mood profiles, etc.
|
||||||
|
|
||||||
|
#### `GET /autonomous/v2/stats/{guild_id}`
|
||||||
|
Get autonomous V2 stats for specific server.
|
||||||
|
|
||||||
|
#### `GET /autonomous/v2/check/{guild_id}`
|
||||||
|
Check if autonomous action should happen for server.
|
||||||
|
|
||||||
|
#### `GET /autonomous/v2/status`
|
||||||
|
Get autonomous V2 status across all servers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🌐 Server Management
|
||||||
|
|
||||||
|
#### `GET /servers`
|
||||||
|
List all configured servers.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"guild_id": 123456789,
|
||||||
|
"guild_name": "My Server",
|
||||||
|
"autonomous_channel_id": 987654321,
|
||||||
|
"autonomous_channel_name": "general",
|
||||||
|
"bedtime_channel_ids": [111111111],
|
||||||
|
"enabled_features": ["autonomous", "bedtime"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `POST /servers`
|
||||||
|
Add a new server configuration.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"guild_id": 123456789,
|
||||||
|
"guild_name": "My Server",
|
||||||
|
"autonomous_channel_id": 987654321,
|
||||||
|
"autonomous_channel_name": "general",
|
||||||
|
"bedtime_channel_ids": [111111111],
|
||||||
|
"enabled_features": ["autonomous", "bedtime"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `DELETE /servers/{guild_id}`
|
||||||
|
Remove server configuration.
|
||||||
|
|
||||||
|
#### `PUT /servers/{guild_id}`
|
||||||
|
Update server configuration.
|
||||||
|
|
||||||
|
#### `POST /servers/{guild_id}/bedtime-range`
|
||||||
|
Set bedtime range for server.
|
||||||
|
|
||||||
|
#### `POST /servers/{guild_id}/memory`
|
||||||
|
Update server memory/context.
|
||||||
|
|
||||||
|
#### `GET /servers/{guild_id}/memory`
|
||||||
|
Get server memory/context.
|
||||||
|
|
||||||
|
#### `POST /servers/repair`
|
||||||
|
Repair server configurations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 💬 DM Management
|
||||||
|
|
||||||
|
#### `GET /dms/users`
|
||||||
|
List all users with DM history.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"user_id": "123456789",
|
||||||
|
"username": "User#1234",
|
||||||
|
"total_messages": 42,
|
||||||
|
"last_message_date": "2025-12-10T12:34:56",
|
||||||
|
"is_blocked": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `GET /dms/users/{user_id}`
|
||||||
|
Get details for specific user.
|
||||||
|
|
||||||
|
#### `GET /dms/users/{user_id}/conversations`
|
||||||
|
Get conversation history for user.
|
||||||
|
|
||||||
|
#### `GET /dms/users/{user_id}/search?query={query}`
|
||||||
|
Search user's DM history.
|
||||||
|
|
||||||
|
#### `GET /dms/users/{user_id}/export`
|
||||||
|
Export user's DM history.
|
||||||
|
|
||||||
|
#### `DELETE /dms/users/{user_id}`
|
||||||
|
Delete user's DM data.
|
||||||
|
|
||||||
|
#### `POST /dm/{user_id}/custom`
|
||||||
|
Send custom DM (LLM-generated).
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"prompt": "Ask about their day"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `POST /dm/{user_id}/manual`
|
||||||
|
Send manual DM (direct message).
|
||||||
|
|
||||||
|
**Form Data:**
|
||||||
|
- `message`: Message text
|
||||||
|
|
||||||
|
#### `GET /dms/blocked-users`
|
||||||
|
List blocked users.
|
||||||
|
|
||||||
|
#### `POST /dms/users/{user_id}/block`
|
||||||
|
Block a user.
|
||||||
|
|
||||||
|
#### `POST /dms/users/{user_id}/unblock`
|
||||||
|
Unblock a user.
|
||||||
|
|
||||||
|
#### `POST /dms/users/{user_id}/conversations/{conversation_id}/delete`
|
||||||
|
Delete specific conversation.
|
||||||
|
|
||||||
|
#### `POST /dms/users/{user_id}/conversations/delete-all`
|
||||||
|
Delete all conversations for user.
|
||||||
|
|
||||||
|
#### `POST /dms/users/{user_id}/delete-completely`
|
||||||
|
Completely delete user data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📊 DM Analysis
|
||||||
|
|
||||||
|
#### `POST /dms/analysis/run`
|
||||||
|
Run analysis on all DM conversations.
|
||||||
|
|
||||||
|
#### `POST /dms/users/{user_id}/analyze`
|
||||||
|
Analyze specific user's DMs.
|
||||||
|
|
||||||
|
#### `GET /dms/analysis/reports`
|
||||||
|
Get all analysis reports.
|
||||||
|
|
||||||
|
#### `GET /dms/analysis/reports/{user_id}`
|
||||||
|
Get analysis report for specific user.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🖼️ Profile Picture Management
|
||||||
|
|
||||||
|
#### `POST /profile-picture/change?guild_id={guild_id}`
|
||||||
|
Change profile picture. Optionally upload custom image.
|
||||||
|
|
||||||
|
**Form Data:**
|
||||||
|
- `file`: Image file (optional)
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"message": "Profile picture changed successfully",
|
||||||
|
"source": "danbooru",
|
||||||
|
"metadata": {
|
||||||
|
"url": "https://...",
|
||||||
|
"tags": ["hatsune_miku", "...]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `GET /profile-picture/metadata`
|
||||||
|
Get current profile picture metadata.
|
||||||
|
|
||||||
|
#### `POST /profile-picture/restore-fallback`
|
||||||
|
Restore original fallback profile picture.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🎨 Role Color Management
|
||||||
|
|
||||||
|
#### `POST /role-color/custom`
|
||||||
|
Set custom role color.
|
||||||
|
|
||||||
|
**Form Data:**
|
||||||
|
- `hex_color`: Hex color code (e.g., "#FF0000")
|
||||||
|
|
||||||
|
#### `POST /role-color/reset-fallback`
|
||||||
|
Reset role color to fallback (#86cecb).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 💬 Conversation Management
|
||||||
|
|
||||||
|
#### `GET /conversation/{user_id}`
|
||||||
|
Get conversation history for user.
|
||||||
|
|
||||||
|
#### `POST /conversation/reset`
|
||||||
|
Reset conversation history.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_id": "123456789"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📨 Manual Messaging
|
||||||
|
|
||||||
|
#### `POST /manual/send`
|
||||||
|
Send manual message to channel.
|
||||||
|
|
||||||
|
**Form Data:**
|
||||||
|
- `message`: Message text
|
||||||
|
- `channel_id`: Channel ID
|
||||||
|
- `files`: Files to attach (optional, multiple)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🎁 Figurine Notifications
|
||||||
|
|
||||||
|
#### `GET /figurines/subscribers`
|
||||||
|
List figurine subscribers.
|
||||||
|
|
||||||
|
#### `POST /figurines/subscribers`
|
||||||
|
Add figurine subscriber.
|
||||||
|
|
||||||
|
#### `DELETE /figurines/subscribers/{user_id}`
|
||||||
|
Remove figurine subscriber.
|
||||||
|
|
||||||
|
#### `POST /figurines/send_now`
|
||||||
|
Send figurine notification to all subscribers.
|
||||||
|
|
||||||
|
#### `POST /figurines/send_to_user`
|
||||||
|
Send figurine notification to specific user.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🖼️ Image Generation
|
||||||
|
|
||||||
|
#### `POST /image/generate`
|
||||||
|
Generate image using image generation service.
|
||||||
|
|
||||||
|
#### `GET /image/status`
|
||||||
|
Get image generation service status.
|
||||||
|
|
||||||
|
#### `POST /image/test-detection`
|
||||||
|
Test face detection on uploaded image.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 😀 Message Reactions
|
||||||
|
|
||||||
|
#### `POST /messages/react`
|
||||||
|
Add reaction to a message.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"channel_id": "123456789",
|
||||||
|
"message_id": "987654321",
|
||||||
|
"emoji": "😊"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Responses
|
||||||
|
|
||||||
|
All endpoints return errors in the following format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"message": "Error description"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP status codes:
|
||||||
|
- `200` - Success
|
||||||
|
- `400` - Bad request
|
||||||
|
- `404` - Not found
|
||||||
|
- `500` - Internal server error
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
Currently, the API does not require authentication. It's designed to run on localhost within a Docker network.
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
No rate limiting is currently implemented.
|
||||||
347
CLI_README.md
Normal file
347
CLI_README.md
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
# Miku CLI - Command Line Interface
|
||||||
|
|
||||||
|
A powerful command-line interface for controlling and monitoring the Miku Discord bot.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Make the script executable:
|
||||||
|
```bash
|
||||||
|
chmod +x miku-cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies:
|
||||||
|
```bash
|
||||||
|
pip install requests
|
||||||
|
```
|
||||||
|
|
||||||
|
3. (Optional) Create a symlink for easier access:
|
||||||
|
```bash
|
||||||
|
sudo ln -s $(pwd)/miku-cli.py /usr/local/bin/miku
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check bot status
|
||||||
|
./miku-cli.py status
|
||||||
|
|
||||||
|
# Get current mood
|
||||||
|
./miku-cli.py mood --get
|
||||||
|
|
||||||
|
# Set mood to bubbly
|
||||||
|
./miku-cli.py mood --set bubbly
|
||||||
|
|
||||||
|
# List available moods
|
||||||
|
./miku-cli.py mood --list
|
||||||
|
|
||||||
|
# Trigger autonomous message
|
||||||
|
./miku-cli.py autonomous general
|
||||||
|
|
||||||
|
# List servers
|
||||||
|
./miku-cli.py servers
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
./miku-cli.py logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
By default, the CLI connects to `http://localhost:3939`. To use a different URL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./miku-cli.py --url http://your-server:3939 status
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### Status & Information
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get bot status
|
||||||
|
./miku-cli.py status
|
||||||
|
|
||||||
|
# View recent logs
|
||||||
|
./miku-cli.py logs
|
||||||
|
|
||||||
|
# Get last LLM prompt
|
||||||
|
./miku-cli.py prompt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mood Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get current DM mood
|
||||||
|
./miku-cli.py mood --get
|
||||||
|
|
||||||
|
# Get server mood
|
||||||
|
./miku-cli.py mood --get --server 123456789
|
||||||
|
|
||||||
|
# Set mood
|
||||||
|
./miku-cli.py mood --set bubbly
|
||||||
|
./miku-cli.py mood --set excited --server 123456789
|
||||||
|
|
||||||
|
# Reset mood to neutral
|
||||||
|
./miku-cli.py mood --reset
|
||||||
|
./miku-cli.py mood --reset --server 123456789
|
||||||
|
|
||||||
|
# List available moods
|
||||||
|
./miku-cli.py mood --list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sleep Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Put Miku to sleep
|
||||||
|
./miku-cli.py sleep
|
||||||
|
|
||||||
|
# Wake Miku up
|
||||||
|
./miku-cli.py wake
|
||||||
|
|
||||||
|
# Send bedtime reminder
|
||||||
|
./miku-cli.py bedtime
|
||||||
|
./miku-cli.py bedtime --server 123456789
|
||||||
|
```
|
||||||
|
|
||||||
|
### Autonomous Actions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Trigger general autonomous message
|
||||||
|
./miku-cli.py autonomous general
|
||||||
|
./miku-cli.py autonomous general --server 123456789
|
||||||
|
|
||||||
|
# Trigger user engagement
|
||||||
|
./miku-cli.py autonomous engage
|
||||||
|
./miku-cli.py autonomous engage --server 123456789
|
||||||
|
|
||||||
|
# Share a tweet
|
||||||
|
./miku-cli.py autonomous tweet
|
||||||
|
./miku-cli.py autonomous tweet --server 123456789
|
||||||
|
|
||||||
|
# Trigger reaction
|
||||||
|
./miku-cli.py autonomous reaction
|
||||||
|
./miku-cli.py autonomous reaction --server 123456789
|
||||||
|
|
||||||
|
# Send custom autonomous message
|
||||||
|
./miku-cli.py autonomous custom --prompt "Tell a joke about programming"
|
||||||
|
./miku-cli.py autonomous custom --prompt "Say hello" --server 123456789
|
||||||
|
|
||||||
|
# Get autonomous stats
|
||||||
|
./miku-cli.py autonomous stats
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all configured servers
|
||||||
|
./miku-cli.py servers
|
||||||
|
```
|
||||||
|
|
||||||
|
### DM Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List users with DM history
|
||||||
|
./miku-cli.py dm-users
|
||||||
|
|
||||||
|
# Send custom DM (LLM-generated)
|
||||||
|
./miku-cli.py dm-custom 123456789 "Ask them how their day was"
|
||||||
|
|
||||||
|
# Send manual DM (direct message)
|
||||||
|
./miku-cli.py dm-manual 123456789 "Hello! How are you?"
|
||||||
|
|
||||||
|
# Block a user
|
||||||
|
./miku-cli.py block 123456789
|
||||||
|
|
||||||
|
# Unblock a user
|
||||||
|
./miku-cli.py unblock 123456789
|
||||||
|
|
||||||
|
# List blocked users
|
||||||
|
./miku-cli.py blocked-users
|
||||||
|
```
|
||||||
|
|
||||||
|
### Profile Picture
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Change profile picture (search Danbooru based on mood)
|
||||||
|
./miku-cli.py change-pfp
|
||||||
|
|
||||||
|
# Change to custom image
|
||||||
|
./miku-cli.py change-pfp --image /path/to/image.png
|
||||||
|
|
||||||
|
# Change for specific server mood
|
||||||
|
./miku-cli.py change-pfp --server 123456789
|
||||||
|
|
||||||
|
# Get current profile picture metadata
|
||||||
|
./miku-cli.py pfp-metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conversation Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Reset conversation history for a user
|
||||||
|
./miku-cli.py reset-conversation 123456789
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Messaging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Send message to channel
|
||||||
|
./miku-cli.py send 987654321 "Hello everyone!"
|
||||||
|
|
||||||
|
# Send message with file attachments
|
||||||
|
./miku-cli.py send 987654321 "Check this out!" --files image.png document.pdf
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Moods
|
||||||
|
|
||||||
|
- 😊 neutral
|
||||||
|
- 🥰 bubbly
|
||||||
|
- 🤩 excited
|
||||||
|
- 😴 sleepy
|
||||||
|
- 😡 angry
|
||||||
|
- 🙄 irritated
|
||||||
|
- 😏 flirty
|
||||||
|
- 💕 romantic
|
||||||
|
- 🤔 curious
|
||||||
|
- 😳 shy
|
||||||
|
- 🤪 silly
|
||||||
|
- 😢 melancholy
|
||||||
|
- 😤 serious
|
||||||
|
- 💤 asleep
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Morning Routine
|
||||||
|
```bash
|
||||||
|
# Wake up Miku
|
||||||
|
./miku-cli.py wake
|
||||||
|
|
||||||
|
# Set a bubbly mood
|
||||||
|
./miku-cli.py mood --set bubbly
|
||||||
|
|
||||||
|
# Send a general message to all servers
|
||||||
|
./miku-cli.py autonomous general
|
||||||
|
|
||||||
|
# Change profile picture to match mood
|
||||||
|
./miku-cli.py change-pfp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server-Specific Control
|
||||||
|
```bash
|
||||||
|
# Get server list
|
||||||
|
./miku-cli.py servers
|
||||||
|
|
||||||
|
# Set mood for specific server
|
||||||
|
./miku-cli.py mood --set excited --server 123456789
|
||||||
|
|
||||||
|
# Trigger engagement on that server
|
||||||
|
./miku-cli.py autonomous engage --server 123456789
|
||||||
|
```
|
||||||
|
|
||||||
|
### DM Interaction
|
||||||
|
```bash
|
||||||
|
# List users
|
||||||
|
./miku-cli.py dm-users
|
||||||
|
|
||||||
|
# Send custom message
|
||||||
|
./miku-cli.py dm-custom 123456789 "Ask them about their favorite anime"
|
||||||
|
|
||||||
|
# If user is spamming, block them
|
||||||
|
./miku-cli.py block 123456789
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
```bash
|
||||||
|
# Check status
|
||||||
|
./miku-cli.py status
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
./miku-cli.py logs
|
||||||
|
|
||||||
|
# Get autonomous stats
|
||||||
|
./miku-cli.py autonomous stats
|
||||||
|
|
||||||
|
# Check last prompt
|
||||||
|
./miku-cli.py prompt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
The CLI uses emoji and colored output for better readability:
|
||||||
|
|
||||||
|
- ✅ Success messages
|
||||||
|
- ❌ Error messages
|
||||||
|
- 😊 Mood indicators
|
||||||
|
- 🌐 Server information
|
||||||
|
- 💬 DM information
|
||||||
|
- 📊 Statistics
|
||||||
|
- 🖼️ Media information
|
||||||
|
|
||||||
|
## Scripting
|
||||||
|
|
||||||
|
The CLI is designed to be script-friendly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Morning routine script
|
||||||
|
./miku-cli.py wake
|
||||||
|
./miku-cli.py mood --set bubbly
|
||||||
|
./miku-cli.py autonomous general
|
||||||
|
|
||||||
|
# Wait 5 minutes
|
||||||
|
sleep 300
|
||||||
|
|
||||||
|
# Engage users
|
||||||
|
./miku-cli.py autonomous engage
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
The CLI exits with status code 1 on errors and 0 on success, making it suitable for use in scripts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
if ./miku-cli.py mood --set bubbly; then
|
||||||
|
echo "Mood set successfully"
|
||||||
|
else
|
||||||
|
echo "Failed to set mood"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
For complete API documentation, see [API_REFERENCE.md](./API_REFERENCE.md).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Connection Refused
|
||||||
|
If you get "Connection refused" errors:
|
||||||
|
1. Check that the bot API is running on port 3939
|
||||||
|
2. Verify the URL with `--url` parameter
|
||||||
|
3. Check Docker container status: `docker-compose ps`
|
||||||
|
|
||||||
|
### Permission Denied
|
||||||
|
Make the script executable:
|
||||||
|
```bash
|
||||||
|
chmod +x miku-cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Import Errors
|
||||||
|
Install required dependencies:
|
||||||
|
```bash
|
||||||
|
pip install requests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
Planned features:
|
||||||
|
- Configuration file support (~/.miku-cli.conf)
|
||||||
|
- Interactive mode
|
||||||
|
- Tab completion
|
||||||
|
- Color output control
|
||||||
|
- JSON output mode for scripting
|
||||||
|
- Batch operations
|
||||||
|
- Watch mode for real-time monitoring
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Feel free to extend the CLI with additional commands and features!
|
||||||
742
miku-cli.py
Normal file
742
miku-cli.py
Normal file
@@ -0,0 +1,742 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Miku Discord Bot CLI
|
||||||
|
A command-line interface for interacting with the Miku Discord bot API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import shlex
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Default API base URL
|
||||||
|
DEFAULT_API_URL = "http://192.168.1.2:3939"
|
||||||
|
|
||||||
|
class MikuCLI:
|
||||||
|
def __init__(self, base_url: str = DEFAULT_API_URL):
|
||||||
|
self.base_url = base_url.rstrip('/')
|
||||||
|
|
||||||
|
def _get(self, endpoint: str) -> Dict[str, Any]:
|
||||||
|
"""Make a GET request to the API."""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{self.base_url}{endpoint}")
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"❌ Error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _post(self, endpoint: str, data: Optional[Dict] = None, json_data: Optional[Dict] = None, files: Optional[Dict] = None) -> Dict[str, Any]:
|
||||||
|
"""Make a POST request to the API."""
|
||||||
|
try:
|
||||||
|
response = requests.post(f"{self.base_url}{endpoint}", data=data, json=json_data, files=files)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"❌ Error: {e}")
|
||||||
|
if hasattr(e.response, 'text'):
|
||||||
|
print(f"Response: {e.response.text}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _delete(self, endpoint: str) -> Dict[str, Any]:
|
||||||
|
"""Make a DELETE request to the API."""
|
||||||
|
try:
|
||||||
|
response = requests.delete(f"{self.base_url}{endpoint}")
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"❌ Error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _put(self, endpoint: str, json_data: Dict) -> Dict[str, Any]:
|
||||||
|
"""Make a PUT request to the API."""
|
||||||
|
try:
|
||||||
|
response = requests.put(f"{self.base_url}{endpoint}", json=json_data)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"❌ Error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ========== Status & Info ==========
|
||||||
|
def get_status(self):
|
||||||
|
"""Get bot status."""
|
||||||
|
result = self._get("/status")
|
||||||
|
print(f"✅ Bot Status: {result['status']}")
|
||||||
|
print(f"😊 DM Mood: {result['mood']}")
|
||||||
|
print(f"🌐 Servers: {result['servers']}")
|
||||||
|
print(f"📊 Active Schedulers: {result['active_schedulers']}")
|
||||||
|
if result.get('server_moods'):
|
||||||
|
print("\n📋 Server Moods:")
|
||||||
|
for guild_id, mood in result['server_moods'].items():
|
||||||
|
print(f" Server {guild_id}: {mood}")
|
||||||
|
|
||||||
|
def get_logs(self):
|
||||||
|
"""Get recent bot logs."""
|
||||||
|
result = self._get("/logs")
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
def get_prompt(self):
|
||||||
|
"""Get last prompt sent to LLM."""
|
||||||
|
result = self._get("/prompt")
|
||||||
|
print(f"Last prompt:\n{result['prompt']}")
|
||||||
|
|
||||||
|
# ========== Mood Management ==========
|
||||||
|
def get_mood(self, guild_id: Optional[int] = None):
|
||||||
|
"""Get current mood (global or per-server)."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._get(f"/servers/{guild_id}/mood")
|
||||||
|
print(f"😊 Server {guild_id} Mood: {result['mood']}")
|
||||||
|
print(f"📝 Description: {result['description']}")
|
||||||
|
else:
|
||||||
|
result = self._get("/mood")
|
||||||
|
print(f"😊 DM Mood: {result['mood']}")
|
||||||
|
print(f"📝 Description: {result['description']}")
|
||||||
|
|
||||||
|
def set_mood(self, mood: str, guild_id: Optional[int] = None):
|
||||||
|
"""Set mood (global or per-server)."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._post(f"/servers/{guild_id}/mood", json_data={"mood": mood})
|
||||||
|
else:
|
||||||
|
result = self._post("/mood", json_data={"mood": mood})
|
||||||
|
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
print(f"✅ Mood set to: {result['new_mood']}")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error: {result.get('message', 'Unknown error')}")
|
||||||
|
|
||||||
|
def reset_mood(self, guild_id: Optional[int] = None):
|
||||||
|
"""Reset mood to neutral."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._post(f"/servers/{guild_id}/mood/reset")
|
||||||
|
else:
|
||||||
|
result = self._post("/mood/reset")
|
||||||
|
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
print(f"✅ Mood reset to neutral")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error: {result.get('message', 'Unknown error')}")
|
||||||
|
|
||||||
|
def list_moods(self):
|
||||||
|
"""List available moods."""
|
||||||
|
result = self._get("/moods/available")
|
||||||
|
print("Available moods:")
|
||||||
|
for mood, emoji in result['moods'].items():
|
||||||
|
print(f" {emoji} {mood}")
|
||||||
|
|
||||||
|
# ========== Sleep Management ==========
|
||||||
|
def sleep(self):
|
||||||
|
"""Put Miku to sleep."""
|
||||||
|
result = self._post("/sleep")
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
def wake(self):
|
||||||
|
"""Wake Miku up."""
|
||||||
|
result = self._post("/wake")
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
# ========== Autonomous Actions ==========
|
||||||
|
def autonomous_general(self, guild_id: Optional[int] = None):
|
||||||
|
"""Trigger general autonomous message."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._post(f"/autonomous/general?guild_id={guild_id}")
|
||||||
|
else:
|
||||||
|
result = self._post("/autonomous/general")
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
def autonomous_engage(self, guild_id: Optional[int] = None):
|
||||||
|
"""Trigger autonomous user engagement."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._post(f"/autonomous/engage?guild_id={guild_id}")
|
||||||
|
else:
|
||||||
|
result = self._post("/autonomous/engage")
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
def autonomous_tweet(self, guild_id: Optional[int] = None):
|
||||||
|
"""Trigger autonomous tweet sharing."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._post(f"/autonomous/tweet?guild_id={guild_id}")
|
||||||
|
else:
|
||||||
|
result = self._post("/autonomous/tweet")
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
def autonomous_reaction(self, guild_id: Optional[int] = None):
|
||||||
|
"""Trigger autonomous reaction."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._post(f"/autonomous/reaction?guild_id={guild_id}")
|
||||||
|
else:
|
||||||
|
result = self._post("/autonomous/reaction")
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
def autonomous_custom(self, prompt: str, guild_id: Optional[int] = None):
|
||||||
|
"""Send custom autonomous message."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._post(f"/autonomous/custom?guild_id={guild_id}", json_data={"prompt": prompt})
|
||||||
|
else:
|
||||||
|
result = self._post("/autonomous/custom", json_data={"prompt": prompt})
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
def autonomous_stats(self):
|
||||||
|
"""Get autonomous engine stats."""
|
||||||
|
result = self._get("/autonomous/stats")
|
||||||
|
print(json.dumps(result, indent=2))
|
||||||
|
|
||||||
|
# ========== Server Management ==========
|
||||||
|
def list_servers(self):
|
||||||
|
"""List all configured servers."""
|
||||||
|
result = self._get("/servers")
|
||||||
|
if not result.get('servers'):
|
||||||
|
print("No servers configured")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n📋 Configured Servers ({len(result['servers'])}):\n")
|
||||||
|
for server in result['servers']:
|
||||||
|
print(f"🌐 {server['guild_name']} (ID: {server['guild_id']})")
|
||||||
|
print(f" Channel: #{server['autonomous_channel_name']} (ID: {server['autonomous_channel_id']})")
|
||||||
|
if server.get('bedtime_channel_ids'):
|
||||||
|
print(f" Bedtime Channels: {server['bedtime_channel_ids']}")
|
||||||
|
if server.get('enabled_features'):
|
||||||
|
print(f" Features: {', '.join(server['enabled_features'])}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
def bedtime(self, guild_id: Optional[int] = None):
|
||||||
|
"""Send bedtime reminder."""
|
||||||
|
if guild_id:
|
||||||
|
result = self._post(f"/bedtime?guild_id={guild_id}")
|
||||||
|
else:
|
||||||
|
result = self._post("/bedtime")
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
# ========== DM Management ==========
|
||||||
|
def list_dm_users(self):
|
||||||
|
"""List all users with DM history."""
|
||||||
|
result = self._get("/dms/users")
|
||||||
|
if not result.get('users'):
|
||||||
|
print("No DM users found")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n💬 Users with DM History ({len(result['users'])}):\n")
|
||||||
|
for user in result['users']:
|
||||||
|
print(f"\n👤 {user.get('username', 'Unknown')} (ID: {user.get('user_id', 'N/A')})")
|
||||||
|
if 'message_count' in user:
|
||||||
|
print(f" Messages: {user['message_count']}")
|
||||||
|
if 'last_message_date' in user:
|
||||||
|
print(f" Last seen: {user['last_message_date']}")
|
||||||
|
elif 'last_seen' in user:
|
||||||
|
print(f" Last seen: {user['last_seen']}")
|
||||||
|
elif 'timestamp' in user:
|
||||||
|
print(f" Last seen: {user['timestamp']}")
|
||||||
|
if user.get('is_blocked'):
|
||||||
|
print(f" ⛔ BLOCKED")
|
||||||
|
print()
|
||||||
|
|
||||||
|
def dm_custom(self, user_id: str, prompt: str):
|
||||||
|
"""Send custom DM to user."""
|
||||||
|
result = self._post(f"/dm/{user_id}/custom", json_data={"prompt": prompt})
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
print(f"✅ Custom DM queued for user {user_id}")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error: {result.get('message', 'Unknown error')}")
|
||||||
|
|
||||||
|
def dm_manual(self, user_id: str, message: str):
|
||||||
|
"""Send manual DM to user."""
|
||||||
|
result = self._post(f"/dm/{user_id}/manual", data={"message": message})
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
print(f"✅ Manual DM sent to user {user_id}")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error: {result.get('message', 'Unknown error')}")
|
||||||
|
|
||||||
|
def block_user(self, user_id: str):
|
||||||
|
"""Block a user from DMing."""
|
||||||
|
result = self._post(f"/dms/users/{user_id}/block")
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
print(f"✅ User {user_id} blocked")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error: {result.get('message', 'Unknown error')}")
|
||||||
|
|
||||||
|
def unblock_user(self, user_id: str):
|
||||||
|
"""Unblock a user."""
|
||||||
|
result = self._post(f"/dms/users/{user_id}/unblock")
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
print(f"✅ User {user_id} unblocked")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error: {result.get('message', 'Unknown error')}")
|
||||||
|
|
||||||
|
def list_blocked_users(self):
|
||||||
|
"""List blocked users."""
|
||||||
|
result = self._get("/dms/blocked-users")
|
||||||
|
if not result.get('blocked_users'):
|
||||||
|
print("No blocked users")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n⛔ Blocked Users ({len(result['blocked_users'])}):\n")
|
||||||
|
for user_id, username in result['blocked_users'].items():
|
||||||
|
print(f" {username} (ID: {user_id})")
|
||||||
|
|
||||||
|
# ========== Profile Picture ==========
|
||||||
|
def change_profile_picture(self, image_path: Optional[str] = None, guild_id: Optional[int] = None):
|
||||||
|
"""Change profile picture."""
|
||||||
|
files = None
|
||||||
|
if image_path:
|
||||||
|
files = {'file': open(image_path, 'rb')}
|
||||||
|
|
||||||
|
params = f"?guild_id={guild_id}" if guild_id else ""
|
||||||
|
result = self._post(f"/profile-picture/change{params}", files=files)
|
||||||
|
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
print(f"📸 Source: {result['source']}")
|
||||||
|
if result.get('metadata'):
|
||||||
|
print(f"📝 Metadata: {json.dumps(result['metadata'], indent=2)}")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error: {result.get('message', 'Unknown error')}")
|
||||||
|
|
||||||
|
def get_profile_metadata(self):
|
||||||
|
"""Get profile picture metadata."""
|
||||||
|
result = self._get("/profile-picture/metadata")
|
||||||
|
if result.get('metadata'):
|
||||||
|
print(json.dumps(result['metadata'], indent=2))
|
||||||
|
else:
|
||||||
|
print("No metadata found")
|
||||||
|
|
||||||
|
# ========== Conversation Management ==========
|
||||||
|
def reset_conversation(self, user_id: str):
|
||||||
|
"""Reset conversation history for a user."""
|
||||||
|
result = self._post("/conversation/reset", json_data={"user_id": user_id})
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
|
||||||
|
# ========== Manual Send ==========
|
||||||
|
def manual_send(self, channel_id: str, message: str, files: Optional[list] = None):
|
||||||
|
"""Send manual message to a channel."""
|
||||||
|
data = {"channel_id": channel_id, "message": message}
|
||||||
|
file_data = {}
|
||||||
|
|
||||||
|
if files:
|
||||||
|
for i, file_path in enumerate(files):
|
||||||
|
file_data[f'files'] = open(file_path, 'rb')
|
||||||
|
|
||||||
|
result = self._post("/manual/send", data=data, files=file_data if file_data else None)
|
||||||
|
|
||||||
|
if result['status'] == 'ok':
|
||||||
|
print(f"✅ {result['message']}")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error: {result.get('message', 'Unknown error')}")
|
||||||
|
|
||||||
|
|
||||||
|
def interactive_shell(base_url: str = DEFAULT_API_URL):
|
||||||
|
"""Run an interactive shell for Miku CLI commands."""
|
||||||
|
cli = MikuCLI(base_url)
|
||||||
|
|
||||||
|
print("╔══════════════════════════════════════════╗")
|
||||||
|
print("║ Miku Discord Bot - Interactive ║")
|
||||||
|
print("║ Shell Mode v1.0 ║")
|
||||||
|
print("╚══════════════════════════════════════════╝")
|
||||||
|
print(f"\n🌐 Connected to: {base_url}")
|
||||||
|
print("💡 Type 'help' for available commands")
|
||||||
|
print("💡 Type 'exit' or 'quit' to leave\n")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# Get user input
|
||||||
|
user_input = input("miku> ").strip()
|
||||||
|
|
||||||
|
if not user_input:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Parse the command
|
||||||
|
try:
|
||||||
|
parts = shlex.split(user_input)
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"❌ Invalid command syntax: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
command = parts[0].lower()
|
||||||
|
args = parts[1:] if len(parts) > 1 else []
|
||||||
|
|
||||||
|
# Handle built-in commands
|
||||||
|
if command in ['exit', 'quit', 'q']:
|
||||||
|
print("\n👋 Goodbye!")
|
||||||
|
break
|
||||||
|
elif command == 'help':
|
||||||
|
print_help()
|
||||||
|
continue
|
||||||
|
elif command == 'clear':
|
||||||
|
print("\033[H\033[J", end="")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Execute Miku commands
|
||||||
|
try:
|
||||||
|
execute_command(cli, command, args)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error executing command: {e}")
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n\n👋 Goodbye!")
|
||||||
|
break
|
||||||
|
except EOFError:
|
||||||
|
print("\n\n👋 Goodbye!")
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def print_help():
|
||||||
|
"""Print help for interactive mode."""
|
||||||
|
help_text = """
|
||||||
|
📖 Available Commands:
|
||||||
|
|
||||||
|
Status & Info:
|
||||||
|
status - Get bot status
|
||||||
|
logs - Get recent logs
|
||||||
|
prompt - Get current prompt
|
||||||
|
servers - List all servers
|
||||||
|
|
||||||
|
Mood Management:
|
||||||
|
mood-list - List available moods
|
||||||
|
mood-get [server_id] - Get current mood
|
||||||
|
mood-set <mood> [server_id] - Set mood
|
||||||
|
mood-reset [server_id] - Reset mood to neutral
|
||||||
|
|
||||||
|
Sleep/Wake:
|
||||||
|
sleep - Put bot to sleep
|
||||||
|
wake - Wake the bot up
|
||||||
|
bedtime [server_id] - Set bedtime for server
|
||||||
|
|
||||||
|
Autonomous Actions:
|
||||||
|
autonomous-general [server_id] - Generate general message
|
||||||
|
autonomous-engage [server_id] - Engage with conversation
|
||||||
|
autonomous-tweet [server_id] - Post tweet-style message
|
||||||
|
autonomous-reaction [server_id] - Generate reaction
|
||||||
|
autonomous-custom <prompt> [server_id] - Custom autonomous action
|
||||||
|
autonomous-stats - Show autonomous statistics
|
||||||
|
|
||||||
|
DM Management:
|
||||||
|
dm-users - List users with DM history
|
||||||
|
dm-custom <user_id> <prompt> - Send custom DM
|
||||||
|
dm-manual <user_id> <msg> - Send manual DM
|
||||||
|
reset-conversation <user_id> - Reset conversation history
|
||||||
|
|
||||||
|
User Blocking:
|
||||||
|
block <user_id> - Block a user
|
||||||
|
unblock <user_id> - Unblock a user
|
||||||
|
blocked-users - List blocked users
|
||||||
|
|
||||||
|
Profile Pictures:
|
||||||
|
change-pfp [image_path] [server_id] - Change profile picture
|
||||||
|
pfp-metadata - Get profile picture metadata
|
||||||
|
|
||||||
|
Manual Actions:
|
||||||
|
send <channel_id> <message> - Send message to channel
|
||||||
|
|
||||||
|
Shell Commands:
|
||||||
|
help - Show this help
|
||||||
|
clear - Clear screen
|
||||||
|
exit, quit, q - Exit interactive mode
|
||||||
|
"""
|
||||||
|
print(help_text)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_command(cli: MikuCLI, command: str, args: list):
|
||||||
|
"""Execute a command in interactive mode."""
|
||||||
|
|
||||||
|
# Status & Info
|
||||||
|
if command == 'status':
|
||||||
|
cli.get_status()
|
||||||
|
elif command == 'logs':
|
||||||
|
cli.get_logs()
|
||||||
|
elif command == 'prompt':
|
||||||
|
cli.get_prompt()
|
||||||
|
elif command == 'servers':
|
||||||
|
cli.list_servers()
|
||||||
|
|
||||||
|
# Mood Management
|
||||||
|
elif command == 'mood-list':
|
||||||
|
cli.list_moods()
|
||||||
|
elif command == 'mood-get':
|
||||||
|
server_id = int(args[0]) if args and args[0].isdigit() else None
|
||||||
|
cli.get_mood(server_id)
|
||||||
|
elif command == 'mood-set':
|
||||||
|
if not args:
|
||||||
|
print("❌ Usage: mood-set <mood> [server_id]")
|
||||||
|
return
|
||||||
|
mood = args[0]
|
||||||
|
server_id = int(args[1]) if len(args) > 1 and args[1].isdigit() else None
|
||||||
|
cli.set_mood(mood, server_id)
|
||||||
|
elif command == 'mood-reset':
|
||||||
|
server_id = int(args[0]) if args and args[0].isdigit() else None
|
||||||
|
cli.reset_mood(server_id)
|
||||||
|
|
||||||
|
# Sleep/Wake
|
||||||
|
elif command == 'sleep':
|
||||||
|
cli.sleep()
|
||||||
|
elif command == 'wake':
|
||||||
|
cli.wake()
|
||||||
|
elif command == 'bedtime':
|
||||||
|
server_id = int(args[0]) if args and args[0].isdigit() else None
|
||||||
|
cli.bedtime(server_id)
|
||||||
|
|
||||||
|
# Autonomous Actions
|
||||||
|
elif command == 'autonomous-general':
|
||||||
|
server_id = int(args[0]) if args and args[0].isdigit() else None
|
||||||
|
cli.autonomous_general(server_id)
|
||||||
|
elif command == 'autonomous-engage':
|
||||||
|
server_id = int(args[0]) if args and args[0].isdigit() else None
|
||||||
|
cli.autonomous_engage(server_id)
|
||||||
|
elif command == 'autonomous-tweet':
|
||||||
|
server_id = int(args[0]) if args and args[0].isdigit() else None
|
||||||
|
cli.autonomous_tweet(server_id)
|
||||||
|
elif command == 'autonomous-reaction':
|
||||||
|
server_id = int(args[0]) if args and args[0].isdigit() else None
|
||||||
|
cli.autonomous_reaction(server_id)
|
||||||
|
elif command == 'autonomous-custom':
|
||||||
|
if not args:
|
||||||
|
print("❌ Usage: autonomous-custom <prompt> [server_id]")
|
||||||
|
return
|
||||||
|
# Find server_id if provided
|
||||||
|
server_id = None
|
||||||
|
prompt_parts = []
|
||||||
|
for arg in args:
|
||||||
|
if arg.isdigit():
|
||||||
|
server_id = int(arg)
|
||||||
|
else:
|
||||||
|
prompt_parts.append(arg)
|
||||||
|
prompt = ' '.join(prompt_parts)
|
||||||
|
cli.autonomous_custom(prompt, server_id)
|
||||||
|
elif command == 'autonomous-stats':
|
||||||
|
cli.autonomous_stats()
|
||||||
|
|
||||||
|
# DM Management
|
||||||
|
elif command == 'dm-users':
|
||||||
|
cli.list_dm_users()
|
||||||
|
elif command == 'dm-custom':
|
||||||
|
if len(args) < 2:
|
||||||
|
print("❌ Usage: dm-custom <user_id> <prompt>")
|
||||||
|
return
|
||||||
|
user_id = args[0]
|
||||||
|
prompt = ' '.join(args[1:])
|
||||||
|
cli.dm_custom(user_id, prompt)
|
||||||
|
elif command == 'dm-manual':
|
||||||
|
if len(args) < 2:
|
||||||
|
print("❌ Usage: dm-manual <user_id> <message>")
|
||||||
|
return
|
||||||
|
user_id = args[0]
|
||||||
|
message = ' '.join(args[1:])
|
||||||
|
cli.dm_manual(user_id, message)
|
||||||
|
elif command == 'reset-conversation':
|
||||||
|
if not args:
|
||||||
|
print("❌ Usage: reset-conversation <user_id>")
|
||||||
|
return
|
||||||
|
cli.reset_conversation(args[0])
|
||||||
|
|
||||||
|
# User Blocking
|
||||||
|
elif command == 'block':
|
||||||
|
if not args:
|
||||||
|
print("❌ Usage: block <user_id>")
|
||||||
|
return
|
||||||
|
cli.block_user(args[0])
|
||||||
|
elif command == 'unblock':
|
||||||
|
if not args:
|
||||||
|
print("❌ Usage: unblock <user_id>")
|
||||||
|
return
|
||||||
|
cli.unblock_user(args[0])
|
||||||
|
elif command == 'blocked-users':
|
||||||
|
cli.list_blocked_users()
|
||||||
|
|
||||||
|
# Profile Pictures
|
||||||
|
elif command == 'change-pfp':
|
||||||
|
image_path = args[0] if args else None
|
||||||
|
server_id = int(args[1]) if len(args) > 1 and args[1].isdigit() else None
|
||||||
|
cli.change_profile_picture(image_path, server_id)
|
||||||
|
elif command == 'pfp-metadata':
|
||||||
|
cli.get_profile_metadata()
|
||||||
|
|
||||||
|
# Manual Actions
|
||||||
|
elif command == 'send':
|
||||||
|
if len(args) < 2:
|
||||||
|
print("❌ Usage: send <channel_id> <message>")
|
||||||
|
return
|
||||||
|
channel_id = args[0]
|
||||||
|
message = ' '.join(args[1:])
|
||||||
|
cli.manual_send(channel_id, message)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f"❌ Unknown command: {command}")
|
||||||
|
print("💡 Type 'help' for available commands")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Miku Discord Bot CLI",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
Examples:
|
||||||
|
%(prog)s status # Get bot status
|
||||||
|
%(prog)s mood --get # Get current DM mood
|
||||||
|
%(prog)s mood --set bubbly # Set DM mood to bubbly
|
||||||
|
%(prog)s mood --set excited --server 123 # Set server mood
|
||||||
|
%(prog)s mood --list # List available moods
|
||||||
|
%(prog)s autonomous general --server 123 # Trigger autonomous message
|
||||||
|
%(prog)s servers # List all servers
|
||||||
|
%(prog)s dm-users # List DM users
|
||||||
|
%(prog)s sleep # Put Miku to sleep
|
||||||
|
%(prog)s wake # Wake Miku up
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument('--url', default=DEFAULT_API_URL, help=f'API base URL (default: {DEFAULT_API_URL})')
|
||||||
|
|
||||||
|
subparsers = parser.add_subparsers(dest='command', help='Command to execute')
|
||||||
|
|
||||||
|
# Shell mode
|
||||||
|
subparsers.add_parser('shell', help='Start interactive shell mode')
|
||||||
|
|
||||||
|
# Status commands
|
||||||
|
subparsers.add_parser('status', help='Get bot status')
|
||||||
|
subparsers.add_parser('logs', help='Get recent logs')
|
||||||
|
subparsers.add_parser('prompt', help='Get last LLM prompt')
|
||||||
|
|
||||||
|
# Mood commands
|
||||||
|
mood_parser = subparsers.add_parser('mood', help='Mood management')
|
||||||
|
mood_parser.add_argument('--get', action='store_true', help='Get current mood')
|
||||||
|
mood_parser.add_argument('--set', metavar='MOOD', help='Set mood')
|
||||||
|
mood_parser.add_argument('--reset', action='store_true', help='Reset mood to neutral')
|
||||||
|
mood_parser.add_argument('--list', action='store_true', help='List available moods')
|
||||||
|
mood_parser.add_argument('--server', type=int, help='Server/guild ID')
|
||||||
|
|
||||||
|
# Sleep commands
|
||||||
|
subparsers.add_parser('sleep', help='Put Miku to sleep')
|
||||||
|
subparsers.add_parser('wake', help='Wake Miku up')
|
||||||
|
|
||||||
|
# Autonomous commands
|
||||||
|
auto_parser = subparsers.add_parser('autonomous', help='Autonomous actions')
|
||||||
|
auto_parser.add_argument('action', choices=['general', 'engage', 'tweet', 'reaction', 'custom', 'stats'])
|
||||||
|
auto_parser.add_argument('--prompt', help='Custom prompt (for custom action)')
|
||||||
|
auto_parser.add_argument('--server', type=int, help='Server/guild ID')
|
||||||
|
|
||||||
|
# Server commands
|
||||||
|
subparsers.add_parser('servers', help='List configured servers')
|
||||||
|
|
||||||
|
bedtime_parser = subparsers.add_parser('bedtime', help='Send bedtime reminder')
|
||||||
|
bedtime_parser.add_argument('--server', type=int, help='Server/guild ID')
|
||||||
|
|
||||||
|
# DM commands
|
||||||
|
subparsers.add_parser('dm-users', help='List users with DM history')
|
||||||
|
|
||||||
|
dm_custom_parser = subparsers.add_parser('dm-custom', help='Send custom DM')
|
||||||
|
dm_custom_parser.add_argument('user_id', help='User ID')
|
||||||
|
dm_custom_parser.add_argument('prompt', help='Custom prompt')
|
||||||
|
|
||||||
|
dm_manual_parser = subparsers.add_parser('dm-manual', help='Send manual DM')
|
||||||
|
dm_manual_parser.add_argument('user_id', help='User ID')
|
||||||
|
dm_manual_parser.add_argument('message', help='Message to send')
|
||||||
|
|
||||||
|
block_parser = subparsers.add_parser('block', help='Block a user')
|
||||||
|
block_parser.add_argument('user_id', help='User ID')
|
||||||
|
|
||||||
|
unblock_parser = subparsers.add_parser('unblock', help='Unblock a user')
|
||||||
|
unblock_parser.add_argument('user_id', help='User ID')
|
||||||
|
|
||||||
|
subparsers.add_parser('blocked-users', help='List blocked users')
|
||||||
|
|
||||||
|
# Profile picture commands
|
||||||
|
pfp_parser = subparsers.add_parser('change-pfp', help='Change profile picture')
|
||||||
|
pfp_parser.add_argument('--image', help='Path to image file')
|
||||||
|
pfp_parser.add_argument('--server', type=int, help='Server/guild ID')
|
||||||
|
|
||||||
|
subparsers.add_parser('pfp-metadata', help='Get profile picture metadata')
|
||||||
|
|
||||||
|
# Conversation commands
|
||||||
|
conv_parser = subparsers.add_parser('reset-conversation', help='Reset conversation history')
|
||||||
|
conv_parser.add_argument('user_id', help='User ID')
|
||||||
|
|
||||||
|
# Manual send command
|
||||||
|
send_parser = subparsers.add_parser('send', help='Send manual message to channel')
|
||||||
|
send_parser.add_argument('channel_id', help='Channel ID')
|
||||||
|
send_parser.add_argument('message', help='Message to send')
|
||||||
|
send_parser.add_argument('--files', nargs='+', help='File paths to attach')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# If no command provided, start shell mode
|
||||||
|
if not args.command:
|
||||||
|
interactive_shell(args.url)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# If shell command, start interactive mode
|
||||||
|
if args.command == 'shell':
|
||||||
|
interactive_shell(args.url)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
cli = MikuCLI(args.url)
|
||||||
|
|
||||||
|
# Execute commands
|
||||||
|
try:
|
||||||
|
if args.command == 'status':
|
||||||
|
cli.get_status()
|
||||||
|
elif args.command == 'logs':
|
||||||
|
cli.get_logs()
|
||||||
|
elif args.command == 'prompt':
|
||||||
|
cli.get_prompt()
|
||||||
|
elif args.command == 'mood':
|
||||||
|
if args.list:
|
||||||
|
cli.list_moods()
|
||||||
|
elif args.get:
|
||||||
|
cli.get_mood(args.server)
|
||||||
|
elif args.set:
|
||||||
|
cli.set_mood(args.set, args.server)
|
||||||
|
elif args.reset:
|
||||||
|
cli.reset_mood(args.server)
|
||||||
|
else:
|
||||||
|
mood_parser.print_help()
|
||||||
|
elif args.command == 'sleep':
|
||||||
|
cli.sleep()
|
||||||
|
elif args.command == 'wake':
|
||||||
|
cli.wake()
|
||||||
|
elif args.command == 'autonomous':
|
||||||
|
if args.action == 'general':
|
||||||
|
cli.autonomous_general(args.server)
|
||||||
|
elif args.action == 'engage':
|
||||||
|
cli.autonomous_engage(args.server)
|
||||||
|
elif args.action == 'tweet':
|
||||||
|
cli.autonomous_tweet(args.server)
|
||||||
|
elif args.action == 'reaction':
|
||||||
|
cli.autonomous_reaction(args.server)
|
||||||
|
elif args.action == 'custom':
|
||||||
|
if not args.prompt:
|
||||||
|
print("❌ --prompt is required for custom action")
|
||||||
|
sys.exit(1)
|
||||||
|
cli.autonomous_custom(args.prompt, args.server)
|
||||||
|
elif args.action == 'stats':
|
||||||
|
cli.autonomous_stats()
|
||||||
|
elif args.command == 'servers':
|
||||||
|
cli.list_servers()
|
||||||
|
elif args.command == 'bedtime':
|
||||||
|
cli.bedtime(args.server)
|
||||||
|
elif args.command == 'dm-users':
|
||||||
|
cli.list_dm_users()
|
||||||
|
elif args.command == 'dm-custom':
|
||||||
|
cli.dm_custom(args.user_id, args.prompt)
|
||||||
|
elif args.command == 'dm-manual':
|
||||||
|
cli.dm_manual(args.user_id, args.message)
|
||||||
|
elif args.command == 'block':
|
||||||
|
cli.block_user(args.user_id)
|
||||||
|
elif args.command == 'unblock':
|
||||||
|
cli.unblock_user(args.user_id)
|
||||||
|
elif args.command == 'blocked-users':
|
||||||
|
cli.list_blocked_users()
|
||||||
|
elif args.command == 'change-pfp':
|
||||||
|
cli.change_profile_picture(args.image, args.server)
|
||||||
|
elif args.command == 'pfp-metadata':
|
||||||
|
cli.get_profile_metadata()
|
||||||
|
elif args.command == 'reset-conversation':
|
||||||
|
cli.reset_conversation(args.user_id)
|
||||||
|
elif args.command == 'send':
|
||||||
|
cli.manual_send(args.channel_id, args.message, args.files)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n\n👋 Bye!")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user