From 144d9c16186189998b916f6b265b40320ee73828 Mon Sep 17 00:00:00 2001 From: Space-Banane Date: Sat, 7 Feb 2026 12:01:15 +0100 Subject: [PATCH] initial code upload --- .github/workflows/test.yml | 36 +++++++++ .gitignore | 2 + License | 16 ++++ config.example.json | 11 +++ docker-compose.yml | 10 +++ main.py | 146 +++++++++++++++++++++++++++++++++++++ readme.md | 63 ++++++++++++++++ requirements.txt | 3 + 8 files changed, 287 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 License create mode 100644 config.example.json create mode 100644 docker-compose.yml create mode 100644 main.py create mode 100644 readme.md create mode 100644 requirements.txt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..283bece --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,36 @@ +name: Python CI + +on: + push: + branches: [ "main", "master" ] + pull_request: + branches: [ "main", "master" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Check syntax + run: | + python -m py_compile main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e52e650 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +config.json +__pycache__/ \ No newline at end of file diff --git a/License b/License new file mode 100644 index 0000000..3f52ec0 --- /dev/null +++ b/License @@ -0,0 +1,16 @@ +MIT No Attribution + +Copyright (c) 2025 + +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. + +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. \ No newline at end of file diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..afd9d1a --- /dev/null +++ b/config.example.json @@ -0,0 +1,11 @@ +{ + "bot_token": "YOUR BOT TOKEN", + "prefix": ";", + "command_name": "reddit", + "comment_limit": 2, + "delete_user_message_on_dismiss": true, + "embed_color_submission": "blue", + "embed_color_comments": "green", + "uptime_kuma_url": "", + "uptime_kuma_interval": 60 +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..23a3cb9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +services: + bot: + working_dir: /app + command: bash -c "pip install -r requirements.txt && python main.py" + container_name: reddit-fix-please + restart: unless-stopped + environment: + - PYTHONUNBUFFERED=1 + volumes: + - .:/app \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..16d77dd --- /dev/null +++ b/main.py @@ -0,0 +1,146 @@ +import discord +from discord.ext import commands +import redditwarp.SYNC +import json +import os +import aiohttp +import asyncio + +# Load configuration +config_path = os.path.join(os.path.dirname(__file__), "config.json") +with open(config_path, "r") as f: + config = json.load(f) + +bot_token = config.get("bot_token") +prefix = config.get("prefix", ";") +command_name = config.get("command_name", "rediddy") +comment_limit = config.get("comment_limit", 2) +delete_user_msg = config.get("delete_user_message_on_dismiss", True) +uptime_kuma_url = config.get("uptime_kuma_url", "") +uptime_kuma_interval = config.get("uptime_kuma_interval", 60) + +# Color mapping +COLORS = { + "blue": discord.Color.blue(), + "green": discord.Color.green(), + "red": discord.Color.red(), + "gold": discord.Color.gold(), + "purple": discord.Color.purple(), +} +sub_color = COLORS.get( + config.get("embed_color_submission", "blue"), discord.Color.blue() +) +comment_color = COLORS.get( + config.get("embed_color_comments", "green"), discord.Color.green() +) + +# Setup Discord bot intents +intents = discord.Intents.default() +intents.message_content = True + +bot = commands.Bot(command_prefix=prefix, intents=intents) +reddit_client = redditwarp.SYNC.Client() + + +@bot.event +async def on_ready(): + print(f"Logged in as {bot.user} (ID: {bot.user.id})") + print("------") + if uptime_kuma_url: + bot.loop.create_task(uptime_kuma_heartbeat()) + + +async def uptime_kuma_heartbeat(): + async with aiohttp.ClientSession() as session: + while not bot.is_closed(): + try: + async with session.get(uptime_kuma_url) as resp: + if resp.status == 200: + print("Uptime Kuma heartbeat sent.") + else: + print(f"Uptime Kuma error: {resp.status}") + except Exception as e: + print(f"Uptime Kuma connection failed: {e}") + await asyncio.sleep(uptime_kuma_interval) + + +class RediddyView(discord.ui.View): + def __init__(self, author_msg: discord.Message, submission_url: str): + super().__init__(timeout=60) + self.author_msg = author_msg + self.add_item(discord.ui.Button(label="Open Submission", url=submission_url)) + + @discord.ui.button(label="Dismiss", style=discord.ButtonStyle.danger) + async def dismiss( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if delete_user_msg: + try: + await self.author_msg.delete() + except discord.Forbidden: + pass # May not have permission to delete user messages + await interaction.message.delete() + + +@bot.command(name=command_name) +async def fetch_reddit_submission(ctx, subreddit_name: str, *, query: str): + """Searches Reddit and returns the top post and comments in embeds.""" + try: + # Search for the subreddit name to get a valid subreddit object/ID + sr_list = reddit_client.p.subreddit.search_names(subreddit_name) + if not sr_list: + await ctx.send(f"Subreddit '{subreddit_name}' not found.") + return + + target_sr = sr_list[0] + + # Search for submissions within that subreddit + submissions = reddit_client.p.submission.search(target_sr, query, 1, sort="top") + + found = False + for submission_brief in submissions: + found = True + # Get full submission details + submission = reddit_client.p.submission.get(submission_brief.id) + title = submission.d.get("title", "Reddit Post") + selftext = submission.d.get("selftext", "No content.") + permalink = f"https://www.reddit.com{submission.d.get('permalink')}" + + # Submission Embed + main_embed = discord.Embed( + title=title, + url=permalink, + description=selftext[:2000] if selftext else "No content.", + color=sub_color, + ) + main_embed.set_author(name=f"r/{target_sr}") + + embeds = [main_embed] + + # Fetch comments based on limit + try: + comment_tree = reddit_client.p.comment_tree.fetch( + submission.id, limit=comment_limit, sort="top" + ) + for i, child in enumerate(comment_tree.children[:comment_limit]): + comment_body = child.value.body + comment_embed = discord.Embed( + title=f"Top Comment #{i+1}", + description=comment_body[:1000], + color=comment_color, + ) + embeds.append(comment_embed) + except Exception as e: + print(f"Error fetching comments: {e}") + + view = RediddyView(ctx.message, permalink) + await ctx.send(embeds=embeds, view=view) + + if not found: + await ctx.send(f"No results found for '{query}' in {target_sr}.") + + except Exception as e: + await ctx.send(f"An error occurred: {e}") + + +bot.run(bot_token) diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..ef23518 --- /dev/null +++ b/readme.md @@ -0,0 +1,63 @@ +# Reddit Fix Please - Discord Bot + +A configurable Discord bot that searches Reddit subreddits for specific queries and returns the top submission along with its top comments in clean embeds. + +## Features + +- **Reddit Search**: Finds the most relevant "top" submission for a given subreddit and query. +- **Rich Embeds**: Displays the submission title, body, and top comments in easy-to-read Discord embeds. +- **Interactive UI**: + - **Open Submission**: Direct link button to the Reddit thread. + - **Dismiss**: Cleans up the bot's response and (optionally) the user's command message. +- **Fully Configurable**: Manage everything from tokens to UI colors via `config.json`. +- **Uptime Kuma Integration**: Support for push monitor heartbeats to track bot status. + +## Setup + +### Prerequisites + +- Python 3.8+ +- A Discord Bot Token (from [Discord Developer Portal](https://discord.com/developers/applications)) (enable "Message Content Intent" for the bot) + +### Installation + +1. Install the required dependencies: + ```bash + pip install discord.py redditwarp + ``` + +2. Copy `config.example.json` to `config.json`: + ```bash + cp config.example.json config.json + ``` + +3. Open `config.json` and fill in your `bot_token`. + +### Configuration Options + +| Key | Description | Default | +|-----|-------------|---------| +| `bot_token` | Your Discord bot application token | 😬 | +| `prefix` | The character(s) used to trigger the bot | `;` | +| `command_name` | The name of the search command | `reddit` | +| `comment_limit` | Number of top comments to display | `2` | +| `delete_user_message_on_dismiss` | Whether the Dismiss button deletes the user's message | `true` | +| `embed_color_submission` | Color of the main submission embed | `blue` | +| `embed_color_comments` | Color of the comment embeds | `green` | +| `uptime_kuma_url` | Uptime Kuma Push Monitor URL | `""` | +| `uptime_kuma_interval` | Interval in seconds for heartbeats | `60` | + +## Usage + +Start the bot: +```bash +python main.py +``` + +In Discord, use the configured command (default is `;reddit`): +```text +;reddit [subreddit] [query] +``` + +**Example:** +`;reddit valorantechsupport van-57` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..72acf02 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +redditwarp +discord.py +aiohttp