# Autonomous Reactions Feature

## Overview
Miku now has the ability to autonomously react to messages with emojis selected by the LLM. This feature has two modes:
1. **Scheduled reactions**: Every 20 minutes with a 50% chance
2. **Real-time reactions**: 50% chance to react to each new message in the autonomous channel

## How It Works

### Scheduled Reactions
- **Frequency**: Every 20 minutes (independent from other autonomous actions)
- **Probability**: 50% chance each interval
- **Target**: Randomly selects a recent message (last 50 messages, within 12 hours) from the autonomous channel
- **Emoji Selection**: LLM chooses the most appropriate emoji based on message content

### Real-Time Reactions
- **Trigger**: Every new message posted in the autonomous channel
- **Probability**: 50% chance per message
- **Target**: The newly posted message
- **Emoji Selection**: LLM chooses the most appropriate emoji based on message content

### LLM-Based Emoji Selection
Instead of using mood-based emoji sets, Miku now asks the LLM to select the most contextually appropriate emoji for each message. The LLM considers:
- Message content and tone
- Context and sentiment
- Natural reaction patterns

This makes reactions feel more natural and appropriate to the specific message content, regardless of Miku's current mood.

## Behavior Details

### Message Selection Criteria (Scheduled)
- Only reacts to messages from other users (not her own)
- Only considers messages less than 12 hours old
- Randomly selects from up to 50 recent messages
- Skips the action if no suitable messages are found

### Real-Time Reaction Criteria
- Only triggers in the autonomous channel
- Only for messages from other users (not Miku's own)
- 50% probability per message
- Reacts immediately to the new message

### Special Cases
- **When Asleep**: Miku will not react to messages when her mood is "asleep" or when she's in sleep mode
- **Permissions**: If the bot lacks "Add Reactions" permission in a channel, it will log an error but continue normally
- **Invalid Emoji**: If LLM returns an invalid response, falls back to 💙

## Manual Triggering

### From the Web UI
1. Open the Miku Control Panel (http://your-server:3939)
2. Go to the **Actions** tab
3. Select a target server (or "All Servers")
4. Click the **"React to Message"** button

### API Endpoint
```bash
POST /autonomous/reaction
Content-Type: application/json

{
  "guild_id": 123456789  # Optional - omit to trigger for all servers
}
```

Response:
```json
{
  "status": "ok",
  "message": "Autonomous reaction queued for server 123456789"
}
```

## Technical Implementation

### Scheduler Configuration
- **Job ID**: `autonomous_reaction_{guild_id}`
- **Trigger**: IntervalTrigger (every 20 minutes)
- **Probability**: 50% chance each interval
- **Independence**: Runs on a separate schedule from autonomous speaking (15 min), conversation detection (3 min), etc.

### Function Flow (Scheduled)
1. Scheduler triggers every 20 minutes
2. 50% probability check - may skip
3. Queues async task `miku_autonomous_reaction_for_server()` in bot's event loop
4. Fetches recent messages from autonomous channel (50 messages, 12 hour window)
5. Filters out bot's own messages and old messages
6. Randomly selects a message
7. Asks LLM to choose appropriate emoji
8. Adds reaction to the selected message

### Function Flow (Real-Time)
1. User posts message in autonomous channel
2. Bot's `on_message` event fires
3. 50% probability check - may skip
4. Immediately calls `miku_autonomous_reaction_for_server()` with the new message
5. Asks LLM to choose appropriate emoji
6. Adds reaction to the new message

### File Changes
- **`bot/utils/autonomous.py`**: 
  - Updated `miku_autonomous_reaction_for_server()` with:
    - 50% probability check
    - 12-hour message window (was 2 hours)
    - LLM-based emoji selection (was mood-based)
    - `force_message` parameter for real-time reactions
- **`bot/bot.py`**: 
  - Added real-time reaction trigger in `on_message` event
  - 50% chance to react to new messages in autonomous channel
- **`bot/server_manager.py`**: Added `_run_autonomous_reaction_for_server()` and scheduler job setup
- **`bot/api.py`**: Added `/autonomous/reaction` POST endpoint
- **`bot/static/index.html`**: Added "React to Message" button in Actions tab

## Benefits

### Dual-Mode System
- **Scheduled**: Keeps old messages engaged, prevents dead conversation feel
- **Real-Time**: Provides immediate feedback to active users

### LLM-Powered Intelligence
- Reactions are contextually appropriate to message content
- Not limited to mood-based emoji sets
- More natural and varied interaction style
- Adapts to different types of messages

### Probability-Based
- 50% chance prevents over-reacting
- Feels more natural and human-like
- Doesn't overwhelm chat with reactions

### Server-Specific
- Each server has its own reaction schedule
- Independent tracking per server
- Only reacts in designated autonomous channels

## Monitoring

Check the bot logs for autonomous reaction activity:

**Scheduled reactions:**
```
🎲 Autonomous reaction skipped for server 123456789 (50% chance)
✅ Autonomous reaction queued for server 123456789
✅ Autonomous reaction: Added 😊 to message from Username in ServerName
```

**Real-time reactions:**
```
🎯 Reacting to new message from Username
✅ Autonomous reaction: Added 🎉 to message from Username in ServerName
```

**Error messages:**
```
❌ Missing permissions to add reactions in server 123456789
📭 No recent messages to react to in server 123456789
💤 Miku is asleep in server 123456789, skipping autonomous reaction
⚠️ LLM returned invalid emoji, using fallback: 💙
```

## Configuration

### Change Scheduled Interval
Edit `bot/server_manager.py` in the `setup_server_scheduler()` function:
```python
scheduler.add_job(
    self._run_autonomous_reaction_for_server,
    IntervalTrigger(minutes=20),  # Change this value
    args=[guild_id, client],
    id=f"autonomous_reaction_{guild_id}"
)
```

### Change Probabilities
Edit `bot/utils/autonomous.py` in `miku_autonomous_reaction_for_server()`:
```python
if force_message is None and random.random() > 0.5:  # Change 0.5 to adjust probability
```

Edit `bot/bot.py` in the `on_message` event:
```python
if not is_dm and message.guild and random.random() <= 0.5:  # Change 0.5 to adjust probability
```

### Change Message History Window
Edit `bot/utils/autonomous.py` in `miku_autonomous_reaction_for_server()`:
```python
if age > 43200:  # Change 43200 (12 hours in seconds)
```

Then restart the bot for changes to take effect.
