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