From 2378565a799fc2e687cbd9320906eccea7574799 Mon Sep 17 00:00:00 2001 From: Jake Shore Date: Sun, 25 Jan 2026 01:12:03 -0500 Subject: [PATCH] Initial commit: Discord TLDR - automated server summaries --- .gitignore | 25 +++ LICENSE | 21 +++ README.md | 88 +++++++++++ skill/SKILL.md | 111 +++++++++++++ skill/cron-config.json | 52 +++++++ standalone/.env.example | 18 +++ standalone/Dockerfile | 10 ++ standalone/README.md | 126 +++++++++++++++ standalone/discord_tldr.py | 299 ++++++++++++++++++++++++++++++++++++ standalone/requirements.txt | 9 ++ 10 files changed, 759 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 skill/SKILL.md create mode 100644 skill/cron-config.json create mode 100644 standalone/.env.example create mode 100644 standalone/Dockerfile create mode 100644 standalone/README.md create mode 100644 standalone/discord_tldr.py create mode 100644 standalone/requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3351a9b --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Environment +.env +*.env.local + +# Python +__pycache__/ +*.py[cod] +*$py.class +.Python +venv/ +.venv/ +*.egg-info/ +dist/ +build/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store + +# Logs +*.log +logs/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..14fac91 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f49b359 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# Discord TLDR + +Automatically summarize Discord server conversations and post digests to a dedicated channel. Never miss important discussions again. + +## Two Ways to Use + +### 🤖 Option 1: Clawdbot Skill (Recommended if you use Clawdbot) + +Zero code required. Just configure cron jobs and let Clawdbot handle the rest. + +**Pros:** +- No separate bot to host +- Uses your existing Clawdbot setup +- AI summarization built-in +- Easy to customize prompts + +**Setup:** See [`skill/SKILL.md`](skill/SKILL.md) + +--- + +### 🐍 Option 2: Standalone Bot + +A self-contained Python Discord bot. Deploy anywhere. + +**Pros:** +- Works without Clawdbot +- Full control over deployment +- Docker-ready +- Manual trigger commands + +**Setup:** See [`standalone/README.md`](standalone/README.md) + +--- + +## How It Works + +1. **Collect** - Reads messages from all accessible channels since last summary +2. **Summarize** - Uses Claude/GPT to create a concise digest +3. **Post** - Sends the summary to your #tldr channel +4. **Repeat** - Runs on schedule (default: 6 AM, 1 PM, 10 PM) + +## Example Output + +``` +📋 **TLDR Summary** (Jan 25, 2026 - 1:00 PM) + +Busy morning! Major progress on the API, some hiring discussions, +and the eternal tabs-vs-spaces debate resurfaces. + +**#general** +• Welcomed @newbie to the team +• Q1 roadmap discussion - focusing on mobile first +• Friday team lunch @ 12:30 + +**#engineering** +• Shipped v2.1.0 to staging +• Found and fixed the auth token bug +• Code review backlog cleared + +**#hiring** +• 3 new senior eng candidates in pipeline +• Technical interview scheduled for Thursday + +**Action Items:** +- [ ] @alice: Finalize API docs by Wednesday +- [ ] @bob: Set up staging environment monitoring +- [ ] Everyone: Submit Q1 goals by EOW +``` + +## Why? + +- **Async-friendly**: Not everyone can keep up with Discord in real-time +- **Context preservation**: Important decisions don't get buried +- **Onboarding**: New members can quickly catch up on team dynamics +- **Documentation**: Creates a lightweight log of team activity + +## Contributing + +PRs welcome! Ideas for improvement: +- Slack/Teams support +- Weekly digest mode +- Thread summarization +- Sentiment analysis +- Custom summary templates + +## License + +MIT - do whatever you want with it diff --git a/skill/SKILL.md b/skill/SKILL.md new file mode 100644 index 0000000..b625335 --- /dev/null +++ b/skill/SKILL.md @@ -0,0 +1,111 @@ +# Discord TLDR Skill + +Automatically summarize Discord server conversations and post digests to a dedicated #tldr channel. + +## Overview + +This skill configures scheduled summaries of your Discord server's activity. Perfect for: +- Teams who want async-friendly catch-up +- Servers with multiple active channels +- Anyone who doesn't want to scroll through hundreds of messages + +## Requirements + +- Clawdbot with Discord channel configured +- Bot must have read access to channels you want summarized +- A dedicated #tldr channel for posting summaries + +## Setup + +### 1. Create your #tldr channel + +Create a text channel in your Discord server (e.g., `#tldr` or `#daily-digest`). Note the channel ID. + +### 2. Get your Guild ID + +Your Discord server's guild ID. You can get this by enabling Developer Mode in Discord, then right-clicking your server name. + +### 3. Add the cron jobs + +Run these commands in Clawdbot or add to your config: + +```bash +# Morning summary (6 AM) +clawdbot cron add \ + --name "tldr-morning" \ + --schedule "0 6 * * *" \ + --tz "America/New_York" \ + --message "Read all recent messages from the Discord server (guild YOUR_GUILD_ID) across all channels since the last TLDR summary. Create a concise bullet-point summary of key discussions, decisions, ideas, and action items. Post the summary to the #tldr channel (channel id YOUR_TLDR_CHANNEL_ID)." \ + --to "channel:YOUR_TLDR_CHANNEL_ID" \ + --channel discord + +# Afternoon summary (1 PM) +clawdbot cron add \ + --name "tldr-afternoon" \ + --schedule "0 13 * * *" \ + --tz "America/New_York" \ + --message "Read all recent messages from the Discord server (guild YOUR_GUILD_ID) across all channels since the last TLDR summary. Create a concise bullet-point summary of key discussions, decisions, ideas, and action items. Post the summary to the #tldr channel (channel id YOUR_TLDR_CHANNEL_ID)." \ + --to "channel:YOUR_TLDR_CHANNEL_ID" \ + --channel discord + +# Night summary (10 PM) +clawdbot cron add \ + --name "tldr-night" \ + --schedule "0 22 * * *" \ + --tz "America/New_York" \ + --message "Read all recent messages from the Discord server (guild YOUR_GUILD_ID) across all channels since the last TLDR summary. Create a concise bullet-point summary of key discussions, decisions, ideas, and action items. Post the summary to the #tldr channel (channel id YOUR_TLDR_CHANNEL_ID)." \ + --to "channel:YOUR_TLDR_CHANNEL_ID" \ + --channel discord +``` + +### 4. Customize (optional) + +**Change frequency:** Modify the cron expressions: +- `0 9 * * *` = 9 AM daily +- `0 */6 * * *` = every 6 hours +- `0 9 * * 1-5` = 9 AM weekdays only + +**Change timezone:** Replace `America/New_York` with your timezone. + +**Customize the prompt:** Edit the message to: +- Exclude certain channels +- Focus on specific topics +- Change summary format (bullets, paragraphs, etc.) +- Add action item extraction +- Tag specific roles for important updates + +## Example Output + +``` +📋 **TLDR Summary** (Jan 25, 2026 - Morning) + +**#general** +• Team discussed jacket-making skills and the legend of Nicholai's sibling +• Buba wrote an epic poem about said jacket (instant classic) + +**#dev** +• Merged PR #42 for the new API endpoint +• Blocked on auth token refresh issue - @jake investigating + +**#random** +• Heated debate about best pizza toppings (pineapple discourse continues) + +**Action Items:** +- [ ] Jake: Fix token refresh bug +- [ ] Team: Review Q1 roadmap by Friday +``` + +## Troubleshooting + +**Summaries not posting?** +- Check that Clawdbot has message read permissions in all channels +- Verify the tldr channel ID is correct +- Run `clawdbot cron list` to confirm jobs are enabled + +**Summaries missing channels?** +- Bot needs explicit read access to private channels +- Some channels may be excluded by Discord permissions + +## License + +MIT - do whatever you want with it diff --git a/skill/cron-config.json b/skill/cron-config.json new file mode 100644 index 0000000..ecd0f37 --- /dev/null +++ b/skill/cron-config.json @@ -0,0 +1,52 @@ +{ + "jobs": [ + { + "name": "tldr-morning", + "schedule": { + "kind": "cron", + "expr": "0 6 * * *", + "tz": "America/New_York" + }, + "sessionTarget": "isolated", + "wakeMode": "next-heartbeat", + "payload": { + "kind": "agentTurn", + "message": "Read all recent messages from the Discord server (guild GUILD_ID) across all channels since the last TLDR summary. Create a concise bullet-point summary of key discussions, decisions, ideas, and action items. Post the summary to the #tldr channel (channel id TLDR_CHANNEL_ID).", + "to": "channel:TLDR_CHANNEL_ID", + "channel": "discord" + } + }, + { + "name": "tldr-afternoon", + "schedule": { + "kind": "cron", + "expr": "0 13 * * *", + "tz": "America/New_York" + }, + "sessionTarget": "isolated", + "wakeMode": "next-heartbeat", + "payload": { + "kind": "agentTurn", + "message": "Read all recent messages from the Discord server (guild GUILD_ID) across all channels since the last TLDR summary. Create a concise bullet-point summary of key discussions, decisions, ideas, and action items. Post the summary to the #tldr channel (channel id TLDR_CHANNEL_ID).", + "to": "channel:TLDR_CHANNEL_ID", + "channel": "discord" + } + }, + { + "name": "tldr-night", + "schedule": { + "kind": "cron", + "expr": "0 22 * * *", + "tz": "America/New_York" + }, + "sessionTarget": "isolated", + "wakeMode": "next-heartbeat", + "payload": { + "kind": "agentTurn", + "message": "Read all recent messages from the Discord server (guild GUILD_ID) across all channels since the last TLDR summary. Create a concise bullet-point summary of key discussions, decisions, ideas, and action items. Post the summary to the #tldr channel (channel id TLDR_CHANNEL_ID).", + "to": "channel:TLDR_CHANNEL_ID", + "channel": "discord" + } + } + ] +} diff --git a/standalone/.env.example b/standalone/.env.example new file mode 100644 index 0000000..def09b1 --- /dev/null +++ b/standalone/.env.example @@ -0,0 +1,18 @@ +# Discord Configuration +DISCORD_TOKEN=your_discord_bot_token_here +DISCORD_GUILD_ID=123456789012345678 +DISCORD_TLDR_CHANNEL_ID=123456789012345678 + +# LLM Provider (anthropic or openai) +LLM_PROVIDER=anthropic +ANTHROPIC_API_KEY=sk-ant-xxxxx +# OPENAI_API_KEY=sk-xxxxx + +# Schedule (24h format, comma-separated) +SUMMARY_HOURS=6,13,22 + +# How far back to look for messages (hours) +LOOKBACK_HOURS=8 + +# Channels to exclude (comma-separated channel IDs) +# EXCLUDE_CHANNELS=123456789,987654321 diff --git a/standalone/Dockerfile b/standalone/Dockerfile new file mode 100644 index 0000000..a287d7c --- /dev/null +++ b/standalone/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY discord_tldr.py . + +CMD ["python", "discord_tldr.py"] diff --git a/standalone/README.md b/standalone/README.md new file mode 100644 index 0000000..90bd779 --- /dev/null +++ b/standalone/README.md @@ -0,0 +1,126 @@ +# Discord TLDR Bot (Standalone) + +A standalone Discord bot that automatically summarizes server conversations and posts digests to a dedicated channel. + +## Features + +- 📋 Automatic scheduled summaries (configurable times) +- 🤖 AI-powered summarization (Claude or GPT) +- 📊 Channel-by-channel breakdown +- ⚡ Manual trigger via `!tldr` command +- 🐳 Docker-ready for easy deployment +- 🔒 Respects channel permissions + +## Quick Start + +### 1. Create a Discord Bot + +1. Go to [Discord Developer Portal](https://discord.com/developers/applications) +2. Create a new application +3. Go to Bot → Add Bot +4. Enable these Privileged Intents: + - Message Content Intent + - Server Members Intent (optional) +5. Copy the bot token + +### 2. Invite the Bot + +Generate an invite URL with these permissions: +- Read Messages/View Channels +- Send Messages +- Read Message History + +``` +https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=68608&scope=bot +``` + +### 3. Configure Environment + +```bash +cp .env.example .env +# Edit .env with your values +``` + +### 4. Run + +**Direct:** +```bash +pip install -r requirements.txt +python discord_tldr.py +``` + +**Docker:** +```bash +docker build -t discord-tldr . +docker run --env-file .env discord-tldr +``` + +**Docker Compose:** +```yaml +version: '3.8' +services: + tldr: + build: . + env_file: .env + restart: unless-stopped +``` + +## Commands + +| Command | Description | +|---------|-------------| +| `!tldr` | Generate summary now (uses default lookback) | +| `!tldr 24` | Generate summary for last 24 hours | +| `!tldr-status` | Check bot status and schedule | + +## Configuration + +| Variable | Default | Description | +|----------|---------|-------------| +| `DISCORD_TOKEN` | required | Bot token | +| `DISCORD_GUILD_ID` | required | Server ID | +| `DISCORD_TLDR_CHANNEL_ID` | required | Channel for summaries | +| `LLM_PROVIDER` | `anthropic` | `anthropic` or `openai` | +| `ANTHROPIC_API_KEY` | - | Claude API key | +| `OPENAI_API_KEY` | - | GPT API key | +| `SUMMARY_HOURS` | `6,13,22` | Hours to post (24h format) | +| `LOOKBACK_HOURS` | `8` | Hours to look back | +| `EXCLUDE_CHANNELS` | - | Channel IDs to skip | + +## Example Output + +``` +📋 **TLDR Summary** (Jan 25, 2026 - 6:00 AM) + +Quick catch-up: Active discussions about the new API launch and some +spirited debate about code review practices. + +**#general** +• Team welcomed new member @sarah +• Discussion about Q1 roadmap priorities +• Decided to postpone the demo to next week + +**#dev** +• Merged the authentication refactor (PR #142) +• Debugging session for the webhook timeout issue +• @jake will investigate the memory leak + +**#random** +• Friday lunch plans: Thai food won the vote +• Shared some quality memes about merge conflicts + +**Action Items:** +- @jake: Investigate memory leak by EOD Monday +- @team: Review roadmap doc before Wednesday standup +``` + +## Hosting Options + +- **VPS**: Any cheap VPS (DigitalOcean, Linode, Vultr) +- **Railway/Render**: Easy container deployment +- **Home server**: Run in Docker with auto-restart +- **Raspberry Pi**: Works great for small servers + +## License + +MIT diff --git a/standalone/discord_tldr.py b/standalone/discord_tldr.py new file mode 100644 index 0000000..1f8614f --- /dev/null +++ b/standalone/discord_tldr.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 +""" +Discord TLDR Bot +Summarizes Discord server conversations and posts to a #tldr channel. + +Standalone version - no Clawdbot required. +Uses Discord.py for Discord API and Anthropic/OpenAI for summarization. +""" + +import os +import asyncio +import logging +from datetime import datetime, timedelta, timezone +from typing import Optional +import discord +from discord.ext import commands, tasks + +# Optional: use anthropic or openai for summarization +try: + import anthropic + HAS_ANTHROPIC = True +except ImportError: + HAS_ANTHROPIC = False + +try: + import openai + HAS_OPENAI = True +except ImportError: + HAS_OPENAI = False + +# Configuration (override with environment variables) +DISCORD_TOKEN = os.getenv("DISCORD_TOKEN") +GUILD_ID = int(os.getenv("DISCORD_GUILD_ID", "0")) +TLDR_CHANNEL_ID = int(os.getenv("DISCORD_TLDR_CHANNEL_ID", "0")) + +# LLM Configuration +LLM_PROVIDER = os.getenv("LLM_PROVIDER", "anthropic") # "anthropic" or "openai" +ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") + +# Summary schedule (hours in 24h format, comma-separated) +SUMMARY_HOURS = [int(h) for h in os.getenv("SUMMARY_HOURS", "6,13,22").split(",")] + +# Channels to exclude (comma-separated channel IDs) +EXCLUDE_CHANNELS = [int(c) for c in os.getenv("EXCLUDE_CHANNELS", "").split(",") if c] + +# How far back to look for messages (hours) +LOOKBACK_HOURS = int(os.getenv("LOOKBACK_HOURS", "8")) + +# Logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("discord-tldr") + +# Bot setup +intents = discord.Intents.default() +intents.message_content = True +intents.guilds = True +bot = commands.Bot(command_prefix="!", intents=intents) + + +class MessageCollector: + """Collects messages from Discord channels.""" + + def __init__(self, guild: discord.Guild, exclude_channels: list[int]): + self.guild = guild + self.exclude_channels = exclude_channels + + async def collect_messages(self, since: datetime) -> dict[str, list[dict]]: + """Collect messages from all text channels since the given time.""" + messages_by_channel = {} + + for channel in self.guild.text_channels: + if channel.id in self.exclude_channels: + continue + if channel.id == TLDR_CHANNEL_ID: + continue # Don't summarize the tldr channel itself + + try: + messages = [] + async for msg in channel.history(after=since, limit=500): + if msg.author.bot: + continue + messages.append({ + "author": msg.author.display_name, + "content": msg.content, + "timestamp": msg.created_at.isoformat(), + "attachments": len(msg.attachments), + "reactions": sum(r.count for r in msg.reactions) if msg.reactions else 0 + }) + + if messages: + messages_by_channel[channel.name] = list(reversed(messages)) + logger.info(f"Collected {len(messages)} messages from #{channel.name}") + + except discord.Forbidden: + logger.warning(f"No access to #{channel.name}") + except Exception as e: + logger.error(f"Error collecting from #{channel.name}: {e}") + + return messages_by_channel + + +class Summarizer: + """Summarizes messages using an LLM.""" + + def __init__(self, provider: str = "anthropic"): + self.provider = provider + + if provider == "anthropic" and HAS_ANTHROPIC: + self.client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY) + elif provider == "openai" and HAS_OPENAI: + self.client = openai.OpenAI(api_key=OPENAI_API_KEY) + else: + self.client = None + logger.warning(f"LLM provider '{provider}' not available, using basic summarization") + + def _format_messages_for_prompt(self, messages_by_channel: dict) -> str: + """Format collected messages into a prompt-friendly string.""" + parts = [] + for channel, messages in messages_by_channel.items(): + parts.append(f"\n## #{channel}") + for msg in messages: + parts.append(f"[{msg['author']}]: {msg['content']}") + return "\n".join(parts) + + async def summarize(self, messages_by_channel: dict) -> str: + """Generate a summary of the messages.""" + if not messages_by_channel: + return "No new messages to summarize." + + formatted = self._format_messages_for_prompt(messages_by_channel) + + prompt = f"""Summarize the following Discord server conversations into a concise TLDR digest. + +Format your response as: +1. A brief overview (1-2 sentences) +2. Bullet points organized by channel, highlighting: + - Key discussions and decisions + - Important ideas or proposals + - Action items or next steps + - Notable moments or highlights + +Keep it scannable and useful for someone catching up. + +--- +MESSAGES: +{formatted} +--- + +Generate the TLDR summary:""" + + if self.provider == "anthropic" and self.client: + response = self.client.messages.create( + model="claude-sonnet-4-20250514", + max_tokens=1500, + messages=[{"role": "user", "content": prompt}] + ) + return response.content[0].text + + elif self.provider == "openai" and self.client: + response = self.client.chat.completions.create( + model="gpt-4o", + max_tokens=1500, + messages=[{"role": "user", "content": prompt}] + ) + return response.choices[0].message.content + + else: + # Basic fallback: just list message counts + lines = ["📋 **Activity Summary**\n"] + for channel, messages in messages_by_channel.items(): + lines.append(f"• **#{channel}**: {len(messages)} messages") + lines.append("\n*Install anthropic or openai package for AI summaries*") + return "\n".join(lines) + + +class TLDRBot: + """Main bot class that orchestrates collection and summarization.""" + + def __init__(self): + self.collector = None + self.summarizer = Summarizer(LLM_PROVIDER) + self.last_summary_time = None + + async def generate_and_post_summary(self): + """Generate a summary and post it to the TLDR channel.""" + guild = bot.get_guild(GUILD_ID) + if not guild: + logger.error(f"Guild {GUILD_ID} not found") + return + + tldr_channel = guild.get_channel(TLDR_CHANNEL_ID) + if not tldr_channel: + logger.error(f"TLDR channel {TLDR_CHANNEL_ID} not found") + return + + # Determine lookback time + if self.last_summary_time: + since = self.last_summary_time + else: + since = datetime.now(timezone.utc) - timedelta(hours=LOOKBACK_HOURS) + + # Collect messages + self.collector = MessageCollector(guild, EXCLUDE_CHANNELS) + messages = await self.collector.collect_messages(since) + + if not messages: + logger.info("No new messages to summarize") + return + + # Generate summary + summary = await self.summarizer.summarize(messages) + + # Format and post + now = datetime.now() + header = f"📋 **TLDR Summary** ({now.strftime('%b %d, %Y - %I:%M %p')})\n\n" + + # Discord has a 2000 char limit + full_message = header + summary + if len(full_message) > 2000: + # Split into multiple messages + chunks = [full_message[i:i+1990] for i in range(0, len(full_message), 1990)] + for chunk in chunks: + await tldr_channel.send(chunk) + else: + await tldr_channel.send(full_message) + + self.last_summary_time = datetime.now(timezone.utc) + logger.info("Posted TLDR summary") + + +tldr_bot = TLDRBot() + + +@bot.event +async def on_ready(): + logger.info(f"Logged in as {bot.user}") + check_summary_time.start() + + +@tasks.loop(minutes=1) +async def check_summary_time(): + """Check if it's time to post a summary.""" + now = datetime.now() + if now.hour in SUMMARY_HOURS and now.minute == 0: + await tldr_bot.generate_and_post_summary() + + +@bot.command(name="tldr") +async def manual_tldr(ctx, hours: int = None): + """Manually trigger a TLDR summary. Usage: !tldr [hours]""" + if hours: + tldr_bot.last_summary_time = datetime.now(timezone.utc) - timedelta(hours=hours) + await ctx.send("Generating TLDR summary...") + await tldr_bot.generate_and_post_summary() + + +@bot.command(name="tldr-status") +async def tldr_status(ctx): + """Check TLDR bot status.""" + last = tldr_bot.last_summary_time + last_str = last.strftime('%Y-%m-%d %H:%M UTC') if last else "Never" + schedule = ", ".join(f"{h}:00" for h in SUMMARY_HOURS) + + await ctx.send( + f"**TLDR Bot Status**\n" + f"• Last summary: {last_str}\n" + f"• Schedule: {schedule}\n" + f"• Lookback: {LOOKBACK_HOURS} hours\n" + f"• LLM Provider: {LLM_PROVIDER}\n" + f"• Excluded channels: {len(EXCLUDE_CHANNELS)}" + ) + + +def main(): + if not DISCORD_TOKEN: + print("Error: DISCORD_TOKEN environment variable not set") + print("\nRequired environment variables:") + print(" DISCORD_TOKEN - Your Discord bot token") + print(" DISCORD_GUILD_ID - Your server's guild ID") + print(" DISCORD_TLDR_CHANNEL_ID - Channel ID for posting summaries") + print("\nOptional:") + print(" LLM_PROVIDER - 'anthropic' or 'openai' (default: anthropic)") + print(" ANTHROPIC_API_KEY - Anthropic API key (if using Claude)") + print(" OPENAI_API_KEY - OpenAI API key (if using GPT)") + print(" SUMMARY_HOURS - Comma-separated hours (default: 6,13,22)") + print(" LOOKBACK_HOURS - Hours to look back (default: 8)") + print(" EXCLUDE_CHANNELS - Comma-separated channel IDs to skip") + return + + if not GUILD_ID or not TLDR_CHANNEL_ID: + print("Error: DISCORD_GUILD_ID and DISCORD_TLDR_CHANNEL_ID must be set") + return + + bot.run(DISCORD_TOKEN) + + +if __name__ == "__main__": + main() diff --git a/standalone/requirements.txt b/standalone/requirements.txt new file mode 100644 index 0000000..c69ec0d --- /dev/null +++ b/standalone/requirements.txt @@ -0,0 +1,9 @@ +# Core +discord.py>=2.3.0 + +# LLM providers (install at least one) +anthropic>=0.18.0 +openai>=1.12.0 + +# Optional: for better async handling +aiohttp>=3.9.0