commit 5f3816ee3515905bb825085a2eced3e1fe4b7790 Author: Slfhstd Date: Sun Mar 8 18:08:24 2026 +0000 initial commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..cf243be --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# Reddit Bot Docker Environment Configuration + +# .env file example (for Docker) +REDDIT_CLIENT_ID=your_client_id +REDDIT_CLIENT_SECRET=your_client_secret +REDDIT_USERNAME=your_username +REDDIT_PASSWORD=your_password +REDDIT_USER_AGENT=modbot by /u/your_username +REDDIT_SUBREDDIT=your_subreddit +REDDIT_WIKI_PAGE=modbot-config + +# Add this file to .gitignore if you commit your project diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3d641e5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +# Dockerfile for building modreplybot image +FROM python:3.11-slim +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY . . +CMD ["python", "modbot.py"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..d30c2e7 --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +# ModBot Reddit Bot + +This bot watches a subreddit for moderator reports containing triggers, approves posts, and leaves stickied comments. Triggers and comments are configured via a subreddit wiki page. All other settings are handled via environment variables. + +## Features +- Watches for moderator reports with triggers +- Approves posts and leaves stickied comments +- Triggers/comments configured via subreddit wiki +- Supports multiple triggers/comments +- Docker and baremetal support + +## Configuration + +### 1. Subreddit Wiki Page +Create a wiki page (e.g. `modbot-config`) in your subreddit using Automoderator YAML format. Example: + +``` +triggers: + - trigger: help + comment: | + Thank you for your report! + This post is now approved. + + - trigger: question + comment: | + This post has been approved. + Your question will be answered soon. +``` + +Each entry under `triggers` defines a trigger and its associated multi-line comment. + +### 2. Environment Variables +Create a `.env` file (or set env variables directly) with: + +``` +REDDIT_CLIENT_ID=your_client_id +REDDIT_CLIENT_SECRET=your_client_secret +REDDIT_USERNAME=your_username +REDDIT_PASSWORD=your_password +REDDIT_USER_AGENT=modbot by /u/your_username +REDDIT_SUBREDDIT=your_subreddit +REDDIT_WIKI_PAGE=modbot-config +``` + +## Installation + +### Docker Compose (Recommended) +1. Copy `.env.example` to `.env` and fill in your values. +2. Run: + +``` +docker compose up -d +``` + +### Docker Run +1. Copy `.env.example` to `.env` and fill in your values. +2. Run: + +``` +docker run --env-file .env slfhstd.uk/slfhstd/modreplybot:latest +``` + +### Baremetal (Direct Python) +1. Install Python 3.11+ +2. Install dependencies: + +``` +pip install -r requirements.txt +``` + +3. Set environment variables or create a `.env` file. +4. Run: + +``` +python modbot.py +``` + +## Building the Docker Image +If you want to build your own image: + +``` +docker build -t modreplybot . +``` + +## Troubleshooting +- Ensure your Reddit credentials are correct and have moderator permissions. +- The bot must be able to read the wiki page and approve posts. +- Check logs for errors. + +## License +MIT diff --git a/config.py b/config.py new file mode 100644 index 0000000..b80be1e --- /dev/null +++ b/config.py @@ -0,0 +1,28 @@ +import os +import praw + +class Config: + CLIENT_ID = os.environ.get('REDDIT_CLIENT_ID') + CLIENT_SECRET = os.environ.get('REDDIT_CLIENT_SECRET') + USERNAME = os.environ.get('REDDIT_USERNAME') + PASSWORD = os.environ.get('REDDIT_PASSWORD') + USER_AGENT = os.environ.get('REDDIT_USER_AGENT', 'modbot by /u/your_username') + SUBREDDIT = os.environ.get('REDDIT_SUBREDDIT') + WIKI_PAGE = os.environ.get('REDDIT_WIKI_PAGE', 'modbot-config') + + @staticmethod + def validate(): + required = [Config.CLIENT_ID, Config.CLIENT_SECRET, Config.USERNAME, Config.PASSWORD, Config.SUBREDDIT] + if not all(required): + raise ValueError('Missing required Reddit environment variables.') + + +def get_reddit(): + Config.validate() + return praw.Reddit( + client_id=Config.CLIENT_ID, + client_secret=Config.CLIENT_SECRET, + username=Config.USERNAME, + password=Config.PASSWORD, + user_agent=Config.USER_AGENT + ) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6e4a38c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +version: '3.8' +services: + modbot: + image: slfhstd.uk/slfhstd/modreplybot:latest + env_file: + - .env + restart: unless-stopped diff --git a/modbot.py b/modbot.py new file mode 100644 index 0000000..e82fdf2 --- /dev/null +++ b/modbot.py @@ -0,0 +1,65 @@ +import praw +from config import get_reddit, Config + +import time + +class ModBot: + def __init__(self): + self.reddit = get_reddit() + self.subreddit = self.reddit.subreddit(Config.SUBREDDIT) + self.wiki_page = Config.WIKI_PAGE + self.triggers = [] + self.comments = [] + + def fetch_wiki_config(self): + import yaml + try: + wiki_content = self.subreddit.wiki[self.wiki_page].content_md + # Example Automoderator YAML format: + # triggers: + # - trigger: help + # comment: | + # Thank you for your report! + # This post is now approved. + # - trigger: question + # comment: | + # This post has been approved. + config = yaml.safe_load(wiki_content) + self.triggers = [] + self.comments = [] + for entry in config.get('triggers', []): + self.triggers.append(entry.get('trigger', '').strip()) + self.comments.append(entry.get('comment', '').strip()) + except Exception as e: + print(f"Error fetching wiki config: {e}") + + def run(self): + print("ModBot started. Watching for mod reports...") + while True: + self.fetch_wiki_config() + for report in self.subreddit.mod.reports(limit=25): + self.handle_report(report) + time.sleep(30) # Poll every 30 seconds + + def handle_report(self, report): + if not hasattr(report, 'mod_reports') or not report.mod_reports: + return + for mod_report in report.mod_reports: + report_text = mod_report[0].lower() + for idx, trigger in enumerate(self.triggers): + if trigger.lower() in report_text: + self.approve_and_comment(report, self.comments[idx]) + break + + def approve_and_comment(self, submission, comment_text): + try: + submission.mod.approve() + comment = submission.reply(comment_text) + comment.mod.distinguish(sticky=True) + print(f"Approved and commented on: {submission.id}") + except Exception as e: + print(f"Error approving/commenting: {e}") + +if __name__ == "__main__": + bot = ModBot() + bot.run() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..404db15 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +praw +pyyaml \ No newline at end of file