# Twitch Viewbot Detector

This utility monitors a Twitch channel to detect potential viewbotting. It calculates the **Engagement Rate** by comparing the number of active chat messages against the concurrent viewer count. A very high viewer count with disproportionately low chat activity is a common indicator of viewbotting.

**Modules Used:**
*   `twitchio`: To connect to Twitch Chat and the Twitch API.
*   [[programming/python/modules/python-dotenv-module|python-dotenv]]: To manage credentials.
*   [[programming/python/modules/argparse-module|argparse]]: To handle command-line arguments.

## Prerequisites

1.  **Twitch Developer Account**: Register an application on the Twitch Developer Console.
2.  **Credentials**: Get your **Client ID** and **Client Secret**.
3.  **Chat Token**: Generate an OAuth token for chat (e.g., using a token generator tool).

## Installation

```bash
pip install twitchio python-dotenv
```

## Setup

Create a `.env` file:
```text
TMI_TOKEN=oauth:your_chat_token
CLIENT_ID=your_client_id
CLIENT_SECRET=your_client_secret
```

## The Code

Save this as `twitch_monitor.py`.

```python
import os
import asyncio
import argparse
import time
from twitchio.ext import commands
from dotenv import load_dotenv

load_dotenv()

class ViewbotDetector(commands.Bot):
    def __init__(self, target_channel):
        super().__init__(
            token=os.environ['TMI_TOKEN'],
            client_id=os.environ['CLIENT_ID'],
            client_secret=os.environ['CLIENT_SECRET'],
            prefix='!',
            initial_channels=[target_channel]
        )
        self.target_channel = target_channel
        self.message_count = 0
        self.monitoring = True

    async def event_ready(self):
        print(f"Logged in as | {self.nick}")
        print(f"Monitoring channel: {self.target_channel}")
        print("-" * 60)
        print(f"{'Time':<10} | {'Viewers':<10} | {'Chats/min':<10} | {'Engagement %':<15}")
        print("-" * 60)
        
        # Start the background monitoring task
        self.loop.create_task(self.monitor_loop())

    async def event_message(self, message):
        if message.echo:
            return
        self.message_count += 1

    async def monitor_loop(self):
        # Wait for bot to be fully ready
        await self.wait_for_ready()
        
        while self.monitoring:
            # Wait 60 seconds to gather chat stats
            await asyncio.sleep(60)
            
            try:
                # Fetch stream info from API
                streams = await self.fetch_streams(user_logins=[self.target_channel])
                
                if not streams:
                    print(f"[{time.strftime('%H:%M')}] Channel is offline.")
                    # Reset count and wait
                    self.message_count = 0
                    continue
                
                stream = streams[0]
                viewers = stream.viewer_count
                chats = self.message_count
                
                # Calculate Engagement
                engagement = 0.0
                if viewers > 0:
                    engagement = (chats / viewers) * 100
                
                timestamp = time.strftime("%H:%M")
                print(f"{timestamp:<10} | {viewers:<10} | {chats:<10} | {engagement:.2f}%")
                
                # Heuristic Check
                # Normal engagement varies, but < 0.5% with high viewers is suspicious
                if viewers > 100 and engagement < 0.5:
                    print(f"  [!] Suspiciously low activity for {viewers} viewers.")

                # Reset message count for next cycle
                self.message_count = 0
                
            except Exception as e:
                print(f"Error: {e}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Twitch Viewbot Detector")
    parser.add_argument("channel", help="Target Twitch Channel Name")
    
    args = parser.parse_args()
    
    # Ensure required env vars exist
    if not all(k in os.environ for k in ['TMI_TOKEN', 'CLIENT_ID', 'CLIENT_SECRET']):
        print("Error: Missing credentials in .env file.")
        exit(1)

    bot = ViewbotDetector(args.channel)
    bot.run()
```

## Usage

```bash
python twitch_monitor.py ninja
```

**Note:** Engagement rates vary by content type (e.g., esports tournaments often have lower chat/viewer ratios than interactive streamers). Use this as a heuristic, not definitive proof.

[[programming/python/python]]