Files
TestPostsBot/bot.py
T
2026-03-11 17:26:02 +00:00

197 lines
7.9 KiB
Python

# Reddit Test Posts Bot
# This script reads config from a subreddit wiki page and makes test posts
# when a moderator sends a chat message containing a configured trigger.
import praw
import time
import os
import threading
from config import fetch_config_from_wiki, validate_config_from_wiki, get_trigger_posts
REDDIT_CLIENT_ID = os.environ.get('REDDIT_CLIENT_ID')
REDDIT_CLIENT_SECRET = os.environ.get('REDDIT_CLIENT_SECRET')
REDDIT_USERNAME = os.environ.get('REDDIT_USERNAME')
REDDIT_PASSWORD = os.environ.get('REDDIT_PASSWORD')
REDDIT_USER_AGENT = os.environ.get('REDDIT_USER_AGENT', f'TestPostsBot/0.1 on {REDDIT_USERNAME}')
SUBREDDIT = os.environ.get('SUBREDDIT')
WIKI_PAGE = os.environ.get('WIKI_PAGE', 'testpostsbot_config')
def is_moderator(reddit, subreddit_name):
"""Check if the bot is a moderator of the subreddit."""
subreddit = reddit.subreddit(subreddit_name)
mods = [str(mod) for mod in subreddit.moderator()]
return reddit.user.me().name in mods
def make_posts(reddit, subreddit_name, posts):
"""Make the specified posts to the subreddit."""
subreddit = reddit.subreddit(subreddit_name)
for post in posts:
title = post.get('title', 'Test Post')
body = post.get('body', '')
print(f"[POSTING] Posting: {title}")
try:
subreddit.submit(title, selftext=body)
time.sleep(2) # avoid rate limits
except Exception as e:
print(f"[POSTING] Error posting '{title}': {e}")
def chat_message_watcher(reddit, subreddit_name):
"""
Watches for chat messages from moderators containing trigger keywords.
When a trigger is found, posts the configured posts for that trigger.
"""
chat_requests_file = os.path.join(os.path.dirname(__file__), 'DB', 'chat_wiki_requests.txt')
os.makedirs(os.path.dirname(chat_requests_file), exist_ok=True)
processed_message_ids = set()
# Load processed IDs from file
if os.path.exists(chat_requests_file):
with open(chat_requests_file, 'r', encoding='utf-8') as f:
for line in f:
processed_message_ids.add(line.strip())
subreddit = reddit.subreddit(subreddit_name)
while True:
try:
for message in reddit.inbox.stream():
if not hasattr(message, 'id') or message.id in processed_message_ids:
continue
processed_message_ids.add(message.id)
# Save processed ID to file
with open(chat_requests_file, 'a', encoding='utf-8') as f:
f.write(message.id + '\n')
# Check if message has body and author
if not hasattr(message, 'body'):
continue
author = getattr(message, 'author', None)
if not author:
continue
# Check if sender is a moderator
try:
is_mod = author in subreddit.moderator()
except Exception as e:
print(f"[CHAT WATCH] Error checking if {author} is mod: {e}")
continue
if not is_mod:
continue
message_body_lower = message.body.lower()
print(f"[CHAT WATCH] Moderator '{author}' sent message: {message.body[:100]}")
# Handle special 'reload-config' command
if 'reload-config' in message_body_lower:
print(f"[CHAT WATCH] Reload-config command detected.")
result = validate_config_from_wiki(reddit, subreddit_name, WIKI_PAGE)
if result:
print("[CHAT WATCH] Wiki config validated successfully.")
reply_text = "Config validated successfully. Config is valid YAML."
else:
print("[CHAT WATCH] Wiki config validation failed.")
reply_text = "Config validation failed. Check the wiki config YAML formatting."
try:
message.reply(reply_text)
print(f"[CHAT WATCH] Replied to message {message.id}.")
except Exception as e:
print(f"[CHAT WATCH] Error replying to message {message.id}: {e}")
continue
# Load current config to check for triggers
config = fetch_config_from_wiki(reddit, subreddit_name, WIKI_PAGE)
posts_config = config.get('posts', [])
trigger_found = False
# Check if message contains any trigger
for post_config in posts_config:
if not isinstance(post_config, dict):
continue
trigger = post_config.get('trigger', '').lower()
if trigger and trigger in message_body_lower:
trigger_found = True
print(f"[CHAT WATCH] Matched trigger '{trigger}' in message.")
# Get posts for this trigger
trigger_posts = get_trigger_posts(reddit, subreddit_name, WIKI_PAGE, trigger)
if trigger_posts:
print(f"[CHAT WATCH] Found {len(trigger_posts)} post(s) for trigger '{trigger}'.")
make_posts(reddit, subreddit_name, trigger_posts)
reply_text = f"Successfully posted {len(trigger_posts)} post(s) for trigger '{trigger}'."
else:
print(f"[CHAT WATCH] No posts found for trigger '{trigger}'.")
reply_text = f"No posts configured for trigger '{trigger}'."
try:
message.reply(reply_text)
print(f"[CHAT WATCH] Replied to message {message.id}.")
except Exception as e:
print(f"[CHAT WATCH] Error replying to message {message.id}: {e}")
# Only process the first matching trigger per message
break
except Exception as e:
print(f"[CHAT WATCH] Chat message watcher error: {e}")
import traceback
traceback.print_exc()
time.sleep(30)
def main():
"""Main function - runs bot constantly."""
reddit = praw.Reddit(
client_id=REDDIT_CLIENT_ID,
client_secret=REDDIT_CLIENT_SECRET,
username=REDDIT_USERNAME,
password=REDDIT_PASSWORD,
user_agent=REDDIT_USER_AGENT
)
if not is_moderator(reddit, SUBREDDIT):
print(f"Bot is not a moderator of r/{SUBREDDIT}. Cannot continue.")
return
# Validate initial config
if not validate_config_from_wiki(reddit, SUBREDDIT, WIKI_PAGE):
print(f"Initial wiki config is invalid. Please fix the config at r/{SUBREDDIT}/wiki/{WIKI_PAGE}")
return
print(f"[STARTUP] TestPostsBot started for r/{SUBREDDIT}")
print(f"[STARTUP] Config page: r/{SUBREDDIT}/wiki/{WIKI_PAGE}")
print(f"[STARTUP] Bot username: {reddit.user.me()}")
# Start chat message watcher in background thread
chat_thread = threading.Thread(
target=chat_message_watcher,
args=(reddit, SUBREDDIT),
daemon=True
)
chat_thread.start()
print("[STARTUP] Chat message watcher started.")
# Keep main thread alive
try:
while True:
time.sleep(60)
except KeyboardInterrupt:
print("[SHUTDOWN] Bot shutting down...")
if __name__ == '__main__':
main()
if __name__ == '__main__':
main()