Added Valentines Bot
This commit is contained in:
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
493
ValentinesEvent.py
Normal file
493
ValentinesEvent.py
Normal file
@@ -0,0 +1,493 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import praw
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# DATABASE MANAGER
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class DatabaseManager:
|
||||||
|
|
||||||
|
def __init__(self, db_name="valentines_event.db"):
|
||||||
|
self.conn = sqlite3.connect(db_name, check_same_thread=False)
|
||||||
|
self.cursor = self.conn.cursor()
|
||||||
|
self.create_tables()
|
||||||
|
|
||||||
|
def create_tables(self):
|
||||||
|
# Participants table
|
||||||
|
self.cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS participants (
|
||||||
|
username TEXT PRIMARY KEY,
|
||||||
|
joined_at REAL
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
self.cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS commented_users (
|
||||||
|
username TEXT PRIMARY KEY
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Wishes table
|
||||||
|
self.cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS wishes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
recipient TEXT,
|
||||||
|
sender TEXT,
|
||||||
|
message TEXT,
|
||||||
|
received_at REAL,
|
||||||
|
FOREIGN KEY(recipient) REFERENCES participants(username),
|
||||||
|
UNIQUE(sender, recipient)
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Conversations table
|
||||||
|
self.cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS conversations (
|
||||||
|
sender TEXT PRIMARY KEY,
|
||||||
|
state TEXT,
|
||||||
|
pending_recipient TEXT,
|
||||||
|
pending_message TEXT,
|
||||||
|
timestamp REAL
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Tracks if the final gift has been sent to a user
|
||||||
|
self.cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS deliveries (
|
||||||
|
username TEXT PRIMARY KEY,
|
||||||
|
delivered_at TIMESTAMP
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def add_participant(self, username):
|
||||||
|
try:
|
||||||
|
self.cursor.execute("INSERT INTO participants VALUES (?, ?)",
|
||||||
|
(username, datetime.datetime.now()))
|
||||||
|
self.conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def add_commented_user(self, username):
|
||||||
|
try:
|
||||||
|
self.cursor.execute("INSERT INTO commented_users VALUES (?)",
|
||||||
|
(username, ))
|
||||||
|
self.conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_commented_users(self, username):
|
||||||
|
try:
|
||||||
|
self.cursor.execute("SELECT * FROM commented_users WHERE username=?", (username,))
|
||||||
|
return self.cursor.fetchone()
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def save_wish(self, sender, recipient, message):
|
||||||
|
"""Inserts or Replaces a wish"""
|
||||||
|
self.cursor.execute("SELECT username FROM participants WHERE username = ?", (recipient,))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.add_participant(recipient)
|
||||||
|
|
||||||
|
self.cursor.execute('''
|
||||||
|
INSERT OR REPLACE INTO wishes (recipient, sender, message, received_at)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
''', (recipient, sender, message, datetime.datetime.now()))
|
||||||
|
self.conn.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_existing_wish(self, sender, recipient):
|
||||||
|
self.cursor.execute("SELECT message FROM wishes WHERE sender = ? AND recipient = ?", (sender, recipient))
|
||||||
|
result = self.cursor.fetchone()
|
||||||
|
return result[0] if result else None
|
||||||
|
|
||||||
|
def get_forest_data(self):
|
||||||
|
self.cursor.execute('''
|
||||||
|
SELECT p.username, COUNT(w.id) as wish_count
|
||||||
|
FROM participants p
|
||||||
|
LEFT JOIN wishes w ON p.username = w.recipient
|
||||||
|
GROUP BY p.username
|
||||||
|
ORDER BY p.username ASC
|
||||||
|
''')
|
||||||
|
return self.cursor.fetchall()
|
||||||
|
|
||||||
|
def set_conversation_state(self, sender, state, recipient, message):
|
||||||
|
self.cursor.execute('''
|
||||||
|
INSERT OR REPLACE INTO conversations (sender, state, pending_recipient, pending_message, timestamp)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
''', (sender, state, recipient, message, datetime.datetime.now()))
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def get_conversation(self, sender):
|
||||||
|
self.cursor.execute("SELECT state, pending_recipient, pending_message FROM conversations WHERE sender = ?",
|
||||||
|
(sender,))
|
||||||
|
return self.cursor.fetchone()
|
||||||
|
|
||||||
|
def clear_conversation(self, sender):
|
||||||
|
self.cursor.execute("DELETE FROM conversations WHERE sender = ?", (sender,))
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def get_undelivered_users(self):
|
||||||
|
"""Returns list of usernames who have wishes but haven't received them yet."""
|
||||||
|
self.cursor.execute('''
|
||||||
|
SELECT DISTINCT p.username
|
||||||
|
FROM participants p
|
||||||
|
JOIN wishes w ON p.username = w.recipient
|
||||||
|
LEFT JOIN deliveries d ON p.username = d.username
|
||||||
|
WHERE d.username IS NULL
|
||||||
|
''')
|
||||||
|
return [row[0] for row in self.cursor.fetchall()]
|
||||||
|
|
||||||
|
def get_user_wishes(self, username):
|
||||||
|
self.cursor.execute("SELECT sender, message FROM wishes WHERE recipient = ?", (username,))
|
||||||
|
return self.cursor.fetchall()
|
||||||
|
|
||||||
|
def mark_delivered(self, username):
|
||||||
|
self.cursor.execute("INSERT INTO deliveries VALUES (?, ?)", (username, datetime.datetime.now()))
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SAFETY FILTER
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class SafetyFilter:
|
||||||
|
def __init__(self, blocklist):
|
||||||
|
self.blocklist = blocklist
|
||||||
|
|
||||||
|
def is_safe(self, text):
|
||||||
|
"""
|
||||||
|
Returns True if text is safe, False if it contains blocked terms.
|
||||||
|
Performs a simple substring check (case-insensitive).
|
||||||
|
"""
|
||||||
|
if not text:
|
||||||
|
return True
|
||||||
|
|
||||||
|
text_lower = text.lower()
|
||||||
|
for bad_word in self.blocklist:
|
||||||
|
# Check if the bad word exists in the text
|
||||||
|
# Note: logic can be improved with regex \b word boundaries if needed
|
||||||
|
if bad_word in text_lower:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# BOT LOGIC
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class ValentinesBot:
|
||||||
|
|
||||||
|
# Store the last updated overall count of wishes.
|
||||||
|
last_updated_count = 0
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
print("💕 Initializing Valentine's Helper...")
|
||||||
|
self.reddit = praw.Reddit(
|
||||||
|
client_id=config.eventBotClientId,
|
||||||
|
client_secret=config.eventBotSecret,
|
||||||
|
user_agent=config.user_agent,
|
||||||
|
username=config.username,
|
||||||
|
password=config.password
|
||||||
|
)
|
||||||
|
self.reddit.validate_on_submit = True
|
||||||
|
self.db = DatabaseManager()
|
||||||
|
self.safety = SafetyFilter(config.blocklist)
|
||||||
|
self.subreddit = self.reddit.subreddit(config.subreddit_name)
|
||||||
|
self.last_dashboard_update = 0
|
||||||
|
|
||||||
|
self.username_pattern = re.compile(r"^u/[A-Za-z0-9_-]{3,20}$", re.IGNORECASE)
|
||||||
|
self.yes_pattern = re.compile(r"^(yes|y|yeah|yup|ja|si|oui|confirm)$", re.IGNORECASE)
|
||||||
|
self.no_pattern = re.compile(r"^(no|nop|cancel|nah|not quite|negative|nuh uh)$", re.IGNORECASE)
|
||||||
|
|
||||||
|
def get_visual_icon(self, count):
|
||||||
|
if count == 0:
|
||||||
|
return "🌱"
|
||||||
|
elif count == 1:
|
||||||
|
return "💕 (1 Heart)"
|
||||||
|
elif count <= 5:
|
||||||
|
return "💕" * count + f" ({count} Hearts)"
|
||||||
|
else:
|
||||||
|
return f"💖💖💖 ({count} Hearts!)"
|
||||||
|
|
||||||
|
def verify_user_flair(self, username):
|
||||||
|
try:
|
||||||
|
flair_list = list(self.subreddit.flair(redditor=username))
|
||||||
|
if not flair_list: return False
|
||||||
|
user_flair = flair_list[0]
|
||||||
|
if user_flair['flair_text'] or user_flair['flair_css_class']: return True
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Error checking flair for {username}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_dashboard(self):
|
||||||
|
now = time.time()
|
||||||
|
if now - self.last_dashboard_update < config.update_post_interval: return
|
||||||
|
|
||||||
|
data = self.db.get_forest_data()
|
||||||
|
overall_count = 0
|
||||||
|
if data:
|
||||||
|
for username, count in data:
|
||||||
|
overall_count += count
|
||||||
|
|
||||||
|
if overall_count == self.last_updated_count:
|
||||||
|
# Nothing changed.
|
||||||
|
print(f"No change in dashboard, last checked {datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H:%M:%S')}...", end="\r")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("📝 Updating Valentine's Garden Dashboard...")
|
||||||
|
self.last_updated_count = overall_count
|
||||||
|
body = "# 💝 The Valentine's Garden 💝\n\n"
|
||||||
|
body += "### How to Participate:\n"
|
||||||
|
body += "1. Send a DM to u/{}\n".format(config.username)
|
||||||
|
body += "2. **First Line:** The username (MUST start with `u/` e.g. `u/AmoreMio666`)\n"
|
||||||
|
body += "3. **Next Lines:** Your message.\n\n"
|
||||||
|
body += "**Note:** The bot will ask you to confirm before saving.\n\n"
|
||||||
|
body += "See the announcement post [here](https://www.reddit.com/r/NoNutYearlyCommunity/comments/1pkmvwh/introducing_the_nnyc_christmas_wishbot/)\n"
|
||||||
|
body += "---\n\n"
|
||||||
|
body += "| User | Garden Status |\n| :--- | :--- |\n"
|
||||||
|
|
||||||
|
if not data: body += "| The Garden is quiet... | Be the first to send a message! |\n"
|
||||||
|
for username, count in data:
|
||||||
|
icon = self.get_visual_icon(count)
|
||||||
|
body += f"| u/{username} | {icon} |\n"
|
||||||
|
|
||||||
|
body += "\n\n---\n*Updated: " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "*"
|
||||||
|
|
||||||
|
try:
|
||||||
|
submission = self.reddit.submission(id=config.valentines_post_id)
|
||||||
|
submission.edit(body, )
|
||||||
|
self.last_dashboard_update = now
|
||||||
|
print("✅ Dashboard updated.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error updating dashboard: {e}")
|
||||||
|
|
||||||
|
def parse_new_wish_request(self, message):
|
||||||
|
body_lines = message.body.strip().split('\n')
|
||||||
|
body_lines = [line.strip() for line in body_lines if line.strip()]
|
||||||
|
|
||||||
|
if not body_lines:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
first_line = body_lines[0]
|
||||||
|
|
||||||
|
if self.username_pattern.match(first_line):
|
||||||
|
target_user = first_line[2:].lower()
|
||||||
|
wish_content = '\n'.join(body_lines[1:])
|
||||||
|
return target_user, wish_content
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def process_inbox(self):
|
||||||
|
try:
|
||||||
|
for message in self.reddit.inbox.unread():
|
||||||
|
if message.was_comment: continue
|
||||||
|
|
||||||
|
sender = message.author.name if message.author else "Unknown"
|
||||||
|
print(f"📩 Message from {sender}")
|
||||||
|
|
||||||
|
conversation = self.db.get_conversation(sender)
|
||||||
|
|
||||||
|
if conversation:
|
||||||
|
self.handle_confirmation(message, sender, conversation)
|
||||||
|
else:
|
||||||
|
self.handle_new_request(message, sender)
|
||||||
|
|
||||||
|
message.mark_read()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Error checking inbox: {e}")
|
||||||
|
|
||||||
|
def handle_new_request(self, message, sender):
|
||||||
|
target_user, wish_content = self.parse_new_wish_request(message)
|
||||||
|
|
||||||
|
if not self.verify_user_flair(sender):
|
||||||
|
message.reply(f"Sorry, u/{sender} but you do not have an active Flair in r/{config.subreddit_name}.\n"
|
||||||
|
"We only allow messages for active, flaired community members.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# ERROR: Bad Format
|
||||||
|
if not target_user:
|
||||||
|
reply = ("I couldn't understand your request. Please format your message exactly like this:\n\n"
|
||||||
|
"`u/TargetUsername`\n\n"
|
||||||
|
"`Your message here...`\n\n"
|
||||||
|
"Make sure the username is on the **first line** and starts with **u/**.")
|
||||||
|
message.reply(reply)
|
||||||
|
return
|
||||||
|
|
||||||
|
# ERROR: Empty Body
|
||||||
|
if not wish_content:
|
||||||
|
message.reply(f"You didn't write a message for u/{target_user.lower()}! Please try again with text in the body.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# SAFETY CHECK
|
||||||
|
if not self.safety.is_safe(wish_content):
|
||||||
|
print(f"⛔ Blocked unsafe content from {sender}")
|
||||||
|
message.reply("⛔ **Message Rejected**\n\n"
|
||||||
|
"Your message contains words or phrases that are not allowed in this event. "
|
||||||
|
"Please ensure your message is kind and follows the community guidelines.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# ERROR: No Flair
|
||||||
|
if not self.verify_user_flair(target_user.lower()):
|
||||||
|
message.reply(f"Sorry, u/{target_user.lower()} does not have a User Flair in r/{config.subreddit_name}.\n"
|
||||||
|
"We only allow messages for active, flaired community members.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# CHECK: Existing Wish
|
||||||
|
existing_msg = self.db.check_existing_wish(sender, target_user.lower())
|
||||||
|
|
||||||
|
if existing_msg:
|
||||||
|
self.db.set_conversation_state(sender, "CONFIRM_REPLACE", target_user.lower(), wish_content)
|
||||||
|
reply = (f"⚠️ **You already sent a message to u/{target_user}.**\n\n"
|
||||||
|
f"**Old Message:** {existing_msg[:100]}...\n\n"
|
||||||
|
f"**Do you want to REPLACE it with:**\n"
|
||||||
|
f"> {wish_content}\n\n"
|
||||||
|
f"Reply **YES** to confirm or **NO** to cancel.")
|
||||||
|
message.reply(reply)
|
||||||
|
else:
|
||||||
|
self.db.set_conversation_state(sender, "CONFIRM_NEW", target_user.lower(), wish_content)
|
||||||
|
reply = (f"💌 **Valentine's Confirmation** 💌\n\n"
|
||||||
|
f"I extracted that you want to send a message to **u/{target_user.lower()}**.\n\n"
|
||||||
|
f"**Message:**\n"
|
||||||
|
f"> {wish_content}\n\n"
|
||||||
|
f"Is this correct? Reply **YES** to save or **NO** to cancel.")
|
||||||
|
message.reply(reply)
|
||||||
|
|
||||||
|
def handle_confirmation(self, message, sender, conversation):
|
||||||
|
state, target, content = conversation
|
||||||
|
user_response = message.body.strip().lower()
|
||||||
|
|
||||||
|
# SAFETY CHECK (Re-check in case DB state was manipulated or rules changed)
|
||||||
|
if not self.safety.is_safe(content):
|
||||||
|
self.db.clear_conversation(sender)
|
||||||
|
message.reply("⛔ **Message Rejected** during confirmation. Content violates safety rules.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.yes_pattern.match(user_response):
|
||||||
|
self.db.save_wish(sender, target.lower(), content)
|
||||||
|
self.db.clear_conversation(sender)
|
||||||
|
|
||||||
|
print(f"💾 Message saved for {target.lower()}")
|
||||||
|
if self.db.get_commented_users(target.lower()):
|
||||||
|
print("User already existed before, no need to update")
|
||||||
|
else:
|
||||||
|
print("New user, add comment")
|
||||||
|
self.db.add_commented_user(target.lower())
|
||||||
|
submission = self.reddit.submission(id=config.valentines_post_id)
|
||||||
|
submission.reply(config.comment_base_text.format(username=target.lower()))
|
||||||
|
|
||||||
|
if state == "CONFIRM_REPLACE":
|
||||||
|
message.reply(f"✅ Your message for u/{target.lower()} has been **updated**! 💕")
|
||||||
|
print(f"{sender} has updated their message for u/{target.lower()} to **{content}**.")
|
||||||
|
else:
|
||||||
|
message.reply(f"✅ Your message for u/{target.lower()} has been **saved**! 💕")
|
||||||
|
print(f"{sender} has saved their message for u/{target.lower()}: **{content}**.")
|
||||||
|
|
||||||
|
elif self.no_pattern.match(user_response):
|
||||||
|
self.db.clear_conversation(sender)
|
||||||
|
message.reply("❌ Cancelled. You can send a new message to start over.")
|
||||||
|
print(f"🚫 Action cancelled by {sender}")
|
||||||
|
|
||||||
|
else:
|
||||||
|
message.reply("I didn't catch that. Please reply **YES** to confirm or **NO** to cancel.")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
print("🚀 Valentine's Bot Started. Press Ctrl+C to stop.")
|
||||||
|
while True:# 1. Process new wishes
|
||||||
|
self.process_inbox()
|
||||||
|
|
||||||
|
# 2. Update the public post
|
||||||
|
self.update_dashboard()
|
||||||
|
|
||||||
|
# 3. Check if it's Valentine's Day yet
|
||||||
|
self.check_distribution()
|
||||||
|
|
||||||
|
time.sleep(config.check_interval)
|
||||||
|
|
||||||
|
def check_distribution(self):
|
||||||
|
"""Checks if it is Feb 14th UTC (Midnight) or later and triggers distribution."""
|
||||||
|
now_utc = datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
|
||||||
|
# Check: Is it February AND is it the 14th or later?
|
||||||
|
if now_utc.month == 2 and now_utc.day >= 14:
|
||||||
|
self.distribute_gifts()
|
||||||
|
|
||||||
|
def distribute_gifts(self):
|
||||||
|
recipients = self.db.get_undelivered_users()
|
||||||
|
|
||||||
|
# Silent exit if no pending deliveries (prevents log spam)
|
||||||
|
if not recipients:
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"💝 Happy Valentine's Day! Distributing messages to {len(recipients)} users...")
|
||||||
|
|
||||||
|
for username in recipients:
|
||||||
|
wishes = self.db.get_user_wishes(username.lower())
|
||||||
|
if not wishes: continue
|
||||||
|
|
||||||
|
print(f"💌 Preparing messages for u/{username.lower()}...")
|
||||||
|
|
||||||
|
# --- PAGINATION LOGIC ---
|
||||||
|
messages_to_send = []
|
||||||
|
current_part_content = ""
|
||||||
|
current_part_num = 1
|
||||||
|
|
||||||
|
# Header for first message
|
||||||
|
header = f"Happy Valentine's Day u/{username.lower()}!\n\nThe wait is over. Here are your messages:\n\n---\n\n"
|
||||||
|
current_part_content += header
|
||||||
|
|
||||||
|
for sender, message_text in wishes:
|
||||||
|
# Format the single wish
|
||||||
|
wish_block = f"**From: u/{sender}**\n> {message_text}\n\n---\n\n"
|
||||||
|
|
||||||
|
# Check character limit (safe buffer of 9000 chars)
|
||||||
|
if len(current_part_content) + len(wish_block) > 9000:
|
||||||
|
# Current part full. Close it and start new one.
|
||||||
|
current_part_content += f"\n*(Continued in Part {current_part_num + 1}...)*"
|
||||||
|
messages_to_send.append(current_part_content)
|
||||||
|
|
||||||
|
current_part_num += 1
|
||||||
|
current_part_content = f"**Valentine's Messages Part {current_part_num}**\n\n---\n\n"
|
||||||
|
current_part_content += wish_block
|
||||||
|
else:
|
||||||
|
current_part_content += wish_block
|
||||||
|
|
||||||
|
# Append the footer to the last part
|
||||||
|
current_part_content += f"\nYou received {len(wishes)} heart(s). Have a wonderful Valentine's Day!\n"
|
||||||
|
current_part_content += f"\n*Automated message from r/{config.subreddit_name}*"
|
||||||
|
messages_to_send.append(current_part_content)
|
||||||
|
|
||||||
|
# --- SENDING LOOP ---
|
||||||
|
try:
|
||||||
|
for index, body in enumerate(messages_to_send):
|
||||||
|
subject_line = "💌 Open your Valentine's Time Capsule! 💝"
|
||||||
|
if len(messages_to_send) > 1:
|
||||||
|
subject_line += f" (Part {index + 1}/{len(messages_to_send)})"
|
||||||
|
|
||||||
|
self.reddit.redditor(username).message(subject=subject_line, message=body)
|
||||||
|
# Small sleep between parts to ensure order and avoid spam trigger
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
self.db.mark_delivered(username.lower())
|
||||||
|
print(f"✅ Delivered {len(messages_to_send)} part(s) to u/{username.lower()}")
|
||||||
|
time.sleep(5) # Delay between USERS
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Failed to send to u/{username.lower()}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
bot = ValentinesBot()
|
||||||
|
bot.run()
|
||||||
@@ -15,6 +15,8 @@ password='q}7Y~6f-xNzLdBt'
|
|||||||
subreddit_name="NoNutYearlyCommunity"
|
subreddit_name="NoNutYearlyCommunity"
|
||||||
#The ID of the "Master Post" to keep track of the wishes
|
#The ID of the "Master Post" to keep track of the wishes
|
||||||
master_post_id="1pkmvbo"
|
master_post_id="1pkmvbo"
|
||||||
|
#The ID of the Valentines Master Post to keep track of messages
|
||||||
|
valentines_post_id="1pkmvwh"
|
||||||
#Interval in seconds to check for new messages
|
#Interval in seconds to check for new messages
|
||||||
check_interval=60
|
check_interval=60
|
||||||
#Interval in seconds to update the dashboard if something changed
|
#Interval in seconds to update the dashboard if something changed
|
||||||
|
|||||||
806
nnwc_wrapped_data_2025.json
Normal file
806
nnwc_wrapped_data_2025.json
Normal file
@@ -0,0 +1,806 @@
|
|||||||
|
{
|
||||||
|
"droid_tect": {
|
||||||
|
"total_annual_karma": 6075,
|
||||||
|
"total_posts": 1073,
|
||||||
|
"total_comments": 1221,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Gamble \ud83c\udfb0\ud83c\udfb2\ud83c\udccf",
|
||||||
|
"percentage": "51.7%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Holly Jolly Time \ud83c\udf84\u26c4\ud83e\udd8c\ud83c\udf81\ud83c\udf6a\ud83c\udf1f",
|
||||||
|
"percentage": "27.6%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Moderator Announcement \ud83d\udce2",
|
||||||
|
"percentage": "10.3%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1or2hbn",
|
||||||
|
"title": "This ain't a goodbye, rather a see you later... Probably",
|
||||||
|
"score": 14,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or2hbn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1or2hbn",
|
||||||
|
"title": "This ain't a goodbye, rather a see you later... Probably",
|
||||||
|
"score": 14,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or2hbn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1or2hbn",
|
||||||
|
"title": "This ain't a goodbye, rather a see you later... Probably",
|
||||||
|
"score": 14,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or2hbn"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nswv4at",
|
||||||
|
"text_preview": "Hmmm\\~ \nNot sure yet, but I would think that till...",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgx97h/_/nswv4at"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nswueqa",
|
||||||
|
"text_preview": "Thank you darling~ \u2764\ufe0f\ud83d\udda4\u2764\ufe0f\ud83d\udda4",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgx97h/_/nswueqa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsqago3",
|
||||||
|
"text_preview": "Oh my, i love surprises \ud83d\ude0d\ud83d\ude0d\ud83d\ude0d\ud83d\ude0d\ud83d\ude0d",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pg2n4g/_/nsqago3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Sunday @ 7 UTC",
|
||||||
|
"top_interactors": [
|
||||||
|
{
|
||||||
|
"username": "shad2shad2",
|
||||||
|
"count": 111
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "AltForHornyShit999",
|
||||||
|
"count": 74
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "Nixtius",
|
||||||
|
"count": 74
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Trying_out_new_stuff": {
|
||||||
|
"total_annual_karma": 8696,
|
||||||
|
"total_posts": 1258,
|
||||||
|
"total_comments": 481,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Appreciation \u263a\ufe0f",
|
||||||
|
"percentage": "64.7%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Holly Jolly Time \ud83c\udf84\u26c4\ud83e\udd8c\ud83c\udf81\ud83c\udf6a\ud83c\udf1f",
|
||||||
|
"percentage": "23.5%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "\ud83c\udf30 Year 1 in a Nutshell! \ud83e\udd5c",
|
||||||
|
"percentage": "11.8%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1ou19e7",
|
||||||
|
"title": "Let's enjoy some winter season cookies together!",
|
||||||
|
"score": 13,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1ou19e7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1opqtc7",
|
||||||
|
"title": "How was your week so far? Feeling motivated still?",
|
||||||
|
"score": 13,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1opqtc7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pds4cc",
|
||||||
|
"title": "No matter the time of year...",
|
||||||
|
"score": 13,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pds4cc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nsmbnxu",
|
||||||
|
"text_preview": "True!",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nsmbnxu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nska8ue",
|
||||||
|
"text_preview": "Hugs please",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nska8ue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsmbnxu",
|
||||||
|
"text_preview": "True!",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nsmbnxu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Saturday @ 5 UTC",
|
||||||
|
"top_interactors": [
|
||||||
|
{
|
||||||
|
"username": "shad2shad2",
|
||||||
|
"count": 74
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "Splicer3",
|
||||||
|
"count": 37
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "ai_chat_dude",
|
||||||
|
"count": 37
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ai_chat_dude": {
|
||||||
|
"total_annual_karma": 1771,
|
||||||
|
"total_posts": 222,
|
||||||
|
"total_comments": 592,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Holly Jolly Time \ud83c\udf84\u26c4\ud83e\udd8c\ud83c\udf81\ud83c\udf6a\ud83c\udf1f",
|
||||||
|
"percentage": "66.7%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Meme!\ud83d\ude0b",
|
||||||
|
"percentage": "16.7%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Horny\ud83d\ude08",
|
||||||
|
"percentage": "16.7%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1pe0a9j",
|
||||||
|
"title": "Code Red you say? I see only jingling bells... \ud83d\udd14\ud83d\udd14",
|
||||||
|
"score": 19,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pe0a9j"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pe0a9j",
|
||||||
|
"title": "Code Red you say? I see only jingling bells... \ud83d\udd14\ud83d\udd14",
|
||||||
|
"score": 19,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pe0a9j"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pe0a9j",
|
||||||
|
"title": "Code Red you say? I see only jingling bells... \ud83d\udd14\ud83d\udd14",
|
||||||
|
"score": 19,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pe0a9j"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nslelr0",
|
||||||
|
"text_preview": "You can still put your boots out even if the stock...",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nslelr0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nslelr0",
|
||||||
|
"text_preview": "You can still put your boots out even if the stock...",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nslelr0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nslelr0",
|
||||||
|
"text_preview": "You can still put your boots out even if the stock...",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nslelr0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Sunday @ 7 UTC",
|
||||||
|
"top_interactors": [
|
||||||
|
{
|
||||||
|
"username": "Loyal_Diver",
|
||||||
|
"count": 37
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "droid_tect",
|
||||||
|
"count": 37
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"WallLordBBC1": {
|
||||||
|
"total_annual_karma": 1551,
|
||||||
|
"total_posts": 555,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Meme!\ud83d\ude0b",
|
||||||
|
"percentage": "53.3%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Holly Jolly Time \ud83c\udf84\u26c4\ud83e\udd8c\ud83c\udf81\ud83c\udf6a\ud83c\udf1f",
|
||||||
|
"percentage": "13.3%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Defeat Diary",
|
||||||
|
"percentage": "6.7%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1p4ezbn",
|
||||||
|
"title": "Special Appreciation post for a certain mommy",
|
||||||
|
"score": 7,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1p4ezbn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1p4ezbn",
|
||||||
|
"title": "Special Appreciation post for a certain mommy",
|
||||||
|
"score": 7,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1p4ezbn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1p4ezbn",
|
||||||
|
"title": "Special Appreciation post for a certain mommy",
|
||||||
|
"score": 7,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1p4ezbn"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Thursday @ 4 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"Extension-Rain-2314": {
|
||||||
|
"total_annual_karma": 701,
|
||||||
|
"total_posts": 111,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Holly Jolly Time \ud83c\udf84\u26c4\ud83e\udd8c\ud83c\udf81\ud83c\udf6a\ud83c\udf1f",
|
||||||
|
"percentage": "33.3%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "\ud83c\udf30 Year 1 in a Nutshell! \ud83e\udd5c",
|
||||||
|
"percentage": "33.3%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Moderator Announcement \ud83d\udce2",
|
||||||
|
"percentage": "33.3%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1or3fbu",
|
||||||
|
"title": "This isn\u2019t a bye but it\u2019s more of a I don\u2019t know.",
|
||||||
|
"score": 15,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or3fbu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1or3fbu",
|
||||||
|
"title": "This isn\u2019t a bye but it\u2019s more of a I don\u2019t know.",
|
||||||
|
"score": 15,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or3fbu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1or3fbu",
|
||||||
|
"title": "This isn\u2019t a bye but it\u2019s more of a I don\u2019t know.",
|
||||||
|
"score": 15,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or3fbu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Thursday @ 15 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"shad2shad2": {
|
||||||
|
"total_annual_karma": 2373,
|
||||||
|
"total_posts": 185,
|
||||||
|
"total_comments": 814,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Horny\ud83d\ude08",
|
||||||
|
"percentage": "60.0%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "Meme!\ud83d\ude0b",
|
||||||
|
"percentage": "20.0%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flair": "\ud83c\udf30 Year 1 in a Nutshell! \ud83e\udd5c",
|
||||||
|
"percentage": "20.0%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1p5jzq9",
|
||||||
|
"title": "The debt collector is here for Des again \ud83e\udd2d (read the instructions)",
|
||||||
|
"score": 9,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1p5jzq9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1p5jzq9",
|
||||||
|
"title": "The debt collector is here for Des again \ud83e\udd2d (read the instructions)",
|
||||||
|
"score": 9,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1p5jzq9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1p5jzq9",
|
||||||
|
"title": "The debt collector is here for Des again \ud83e\udd2d (read the instructions)",
|
||||||
|
"score": 9,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1p5jzq9"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nsqd0cz",
|
||||||
|
"text_preview": "oh.... Maiden is the bestest gift \ud83d\ude0d\ud83e\udd70\ud83e\udd70\ud83e\udd70",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgcdo7/_/nsqd0cz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsqay65",
|
||||||
|
"text_preview": "umm Maiden.... you should wrap the gifts, not your...",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgcdo7/_/nsqay65"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsqd0cz",
|
||||||
|
"text_preview": "oh.... Maiden is the bestest gift \ud83d\ude0d\ud83e\udd70\ud83e\udd70\ud83e\udd70",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgcdo7/_/nsqd0cz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Sunday @ 8 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"pensiveColliders": {
|
||||||
|
"total_annual_karma": 111,
|
||||||
|
"total_posts": 37,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Lore\ud83d\udcda\u270d\ufe0f",
|
||||||
|
"percentage": "100.0%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1pdth3f",
|
||||||
|
"title": "2025 No Nut November Highlights",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pdth3f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pdth3f",
|
||||||
|
"title": "2025 No Nut November Highlights",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pdth3f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pdth3f",
|
||||||
|
"title": "2025 No Nut November Highlights",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pdth3f"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Thursday @ 7 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"the_redpandaguy": {
|
||||||
|
"total_annual_karma": 111,
|
||||||
|
"total_posts": 37,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Appreciation \u263a\ufe0f",
|
||||||
|
"percentage": "100.0%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1pd2c0a",
|
||||||
|
"title": "To late for the other sub so I'm doing it here \ud83e\udee0\ud83d\ude05",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pd2c0a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pd2c0a",
|
||||||
|
"title": "To late for the other sub so I'm doing it here \ud83e\udee0\ud83d\ude05",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pd2c0a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pd2c0a",
|
||||||
|
"title": "To late for the other sub so I'm doing it here \ud83e\udee0\ud83d\ude05",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pd2c0a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Wednesday @ 12 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"formallyknwnaswraith": {
|
||||||
|
"total_annual_karma": 111,
|
||||||
|
"total_posts": 37,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Holly Jolly Time \ud83c\udf84\u26c4\ud83e\udd8c\ud83c\udf81\ud83c\udf6a\ud83c\udf1f",
|
||||||
|
"percentage": "100.0%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1pc9lbp",
|
||||||
|
"title": "Its Christmas season and Lust says hello \u2661",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pc9lbp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pc9lbp",
|
||||||
|
"title": "Its Christmas season and Lust says hello \u2661",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pc9lbp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pc9lbp",
|
||||||
|
"title": "Its Christmas season and Lust says hello \u2661",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pc9lbp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Tuesday @ 14 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"Twinklesoles91": {
|
||||||
|
"total_annual_karma": 2571,
|
||||||
|
"total_posts": 74,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Hunter\ud83d\ude08",
|
||||||
|
"percentage": "100.0%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1opvhjf",
|
||||||
|
"title": "How's your week been boys",
|
||||||
|
"score": 54,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1opvhjf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1opvhjf",
|
||||||
|
"title": "How's your week been boys",
|
||||||
|
"score": 54,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1opvhjf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1opvhjf",
|
||||||
|
"title": "How's your week been boys",
|
||||||
|
"score": 54,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1opvhjf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Tuesday @ 10 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"desperatesoldier420": {
|
||||||
|
"total_annual_karma": 111,
|
||||||
|
"total_posts": 37,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Holly Jolly Time \ud83c\udf84\u26c4\ud83e\udd8c\ud83c\udf81\ud83c\udf6a\ud83c\udf1f",
|
||||||
|
"percentage": "100.0%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1pbywyr",
|
||||||
|
"title": "A Decemberween Mackerel",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pbywyr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pbywyr",
|
||||||
|
"title": "A Decemberween Mackerel",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pbywyr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1pbywyr",
|
||||||
|
"title": "A Decemberween Mackerel",
|
||||||
|
"score": 3,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pbywyr"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Tuesday @ 4 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"[deleted]": {
|
||||||
|
"total_annual_karma": 74,
|
||||||
|
"total_posts": 37,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Meme!\ud83d\ude0b",
|
||||||
|
"percentage": "100.0%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1owmy6k",
|
||||||
|
"title": "Rolling into My 20 Day Streak Like",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1owmy6k"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1owmy6k",
|
||||||
|
"title": "Rolling into My 20 Day Streak Like",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1owmy6k"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1owmy6k",
|
||||||
|
"title": "Rolling into My 20 Day Streak Like",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1owmy6k"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Friday @ 4 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"StillWrap9384": {
|
||||||
|
"total_annual_karma": 404,
|
||||||
|
"total_posts": 37,
|
||||||
|
"total_comments": 0,
|
||||||
|
"top_flairs": [
|
||||||
|
{
|
||||||
|
"flair": "Appreciation \u263a\ufe0f",
|
||||||
|
"percentage": "100.0%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_posts": [
|
||||||
|
{
|
||||||
|
"id": "1or0x7d",
|
||||||
|
"title": "ITS MY BIRTHDAY EVERYONE \u2727\u2060\u25dd\u2060(\u2060\u2070\u2060\u25bf\u2060\u2070\u2060)\u2060\u25dc\u2060\u2727",
|
||||||
|
"score": 12,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or0x7d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1or0x7d",
|
||||||
|
"title": "ITS MY BIRTHDAY EVERYONE \u2727\u2060\u25dd\u2060(\u2060\u2070\u2060\u25bf\u2060\u2070\u2060)\u2060\u25dc\u2060\u2727",
|
||||||
|
"score": 12,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or0x7d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1or0x7d",
|
||||||
|
"title": "ITS MY BIRTHDAY EVERYONE \u2727\u2060\u25dd\u2060(\u2060\u2070\u2060\u25bf\u2060\u2070\u2060)\u2060\u25dc\u2060\u2727",
|
||||||
|
"score": 12,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1or0x7d"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_comments": [],
|
||||||
|
"most_active_time": "Friday @ 17 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"AltForHornyShit999": {
|
||||||
|
"total_annual_karma": 148,
|
||||||
|
"total_posts": 0,
|
||||||
|
"total_comments": 148,
|
||||||
|
"top_flairs": [],
|
||||||
|
"top_posts": [],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nswvem7",
|
||||||
|
"text_preview": "Because um... y-youre too pretty ;-; some of us ca...",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgx97h/_/nswvem7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nswun8u",
|
||||||
|
"text_preview": "Y-youre not really going to dress up like this all...",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgx97h/_/nswun8u"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nswsh3n",
|
||||||
|
"text_preview": "Omg Droid ;-; youre so pretty",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgx97h/_/nswsh3n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Monday @ 9 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"Nixtius": {
|
||||||
|
"total_annual_karma": 111,
|
||||||
|
"total_posts": 0,
|
||||||
|
"total_comments": 111,
|
||||||
|
"top_flairs": [],
|
||||||
|
"top_posts": [],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nsv4dwp",
|
||||||
|
"text_preview": ":o",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pg2n4g/_/nsv4dwp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsv49o1",
|
||||||
|
"text_preview": ":o",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgx97h/_/nsv49o1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nskba4b",
|
||||||
|
"text_preview": "I\u2019ll try \ud83e\udee3",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pf9n4j/_/nskba4b"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Monday @ 1 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"Loyal_Diver": {
|
||||||
|
"total_annual_karma": 148,
|
||||||
|
"total_posts": 0,
|
||||||
|
"total_comments": 111,
|
||||||
|
"top_flairs": [],
|
||||||
|
"top_posts": [],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nsmc8yi",
|
||||||
|
"text_preview": "Thanks for the lesson, I didn't know this \ud83d\ude0a",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfq5z0/_/nsmc8yi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsmc8yi",
|
||||||
|
"text_preview": "Thanks for the lesson, I didn't know this \ud83d\ude0a",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfq5z0/_/nsmc8yi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsmc8yi",
|
||||||
|
"text_preview": "Thanks for the lesson, I didn't know this \ud83d\ude0a",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfq5z0/_/nsmc8yi"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Sunday @ 21 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"Splicer3": {
|
||||||
|
"total_annual_karma": 74,
|
||||||
|
"total_posts": 0,
|
||||||
|
"total_comments": 37,
|
||||||
|
"top_flairs": [],
|
||||||
|
"top_posts": [],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nsrzsz0",
|
||||||
|
"text_preview": "*unwraps the gift*\n\nIts you! Yay friendship!",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgcdo7/_/nsrzsz0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsrzsz0",
|
||||||
|
"text_preview": "*unwraps the gift*\n\nIts you! Yay friendship!",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgcdo7/_/nsrzsz0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsrzsz0",
|
||||||
|
"text_preview": "*unwraps the gift*\n\nIts you! Yay friendship!",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pgcdo7/_/nsrzsz0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Sunday @ 15 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"NoZookeepergame2958": {
|
||||||
|
"total_annual_karma": 37,
|
||||||
|
"total_posts": 0,
|
||||||
|
"total_comments": 37,
|
||||||
|
"top_flairs": [],
|
||||||
|
"top_posts": [],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nsl14kk",
|
||||||
|
"text_preview": "There... Might be some truth in that...\n\n*Takes an...",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pbrx96/_/nsl14kk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsl14kk",
|
||||||
|
"text_preview": "There... Might be some truth in that...\n\n*Takes an...",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pbrx96/_/nsl14kk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nsl14kk",
|
||||||
|
"text_preview": "There... Might be some truth in that...\n\n*Takes an...",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pbrx96/_/nsl14kk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Saturday @ 12 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"Wonderful-Title6868": {
|
||||||
|
"total_annual_karma": 74,
|
||||||
|
"total_posts": 0,
|
||||||
|
"total_comments": 74,
|
||||||
|
"top_flairs": [],
|
||||||
|
"top_posts": [],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nskxdqk",
|
||||||
|
"text_preview": "\u2764\ufe0f",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pf9n4j/_/nskxdqk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nskvw99",
|
||||||
|
"text_preview": "No no don\u2019t be sowy \ud83e\udec2 it was a compliment~\u263a\ufe0f",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pf9n4j/_/nskvw99"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nskxdqk",
|
||||||
|
"text_preview": "\u2764\ufe0f",
|
||||||
|
"score": 1,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pf9n4j/_/nskxdqk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Saturday @ 11 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
},
|
||||||
|
"NerveCapable1513": {
|
||||||
|
"total_annual_karma": 148,
|
||||||
|
"total_posts": 0,
|
||||||
|
"total_comments": 74,
|
||||||
|
"top_flairs": [],
|
||||||
|
"top_posts": [],
|
||||||
|
"top_comments": [
|
||||||
|
{
|
||||||
|
"id": "nskftqk",
|
||||||
|
"text_preview": "It\u2019s a little chilly in my house\ud83d\ude23but I\u2019m glad you ...",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nskftqk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nskfko2",
|
||||||
|
"text_preview": "Maiden looks a little cold\u2026 maybe she should put o...",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nskfko2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nskftqk",
|
||||||
|
"text_preview": "It\u2019s a little chilly in my house\ud83d\ude23but I\u2019m glad you ...",
|
||||||
|
"score": 2,
|
||||||
|
"permalink": "https://reddit.com/r/NoNutYearlyCommunity/comments/1pfiv4h/_/nskftqk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"most_active_time": "Saturday @ 8 UTC",
|
||||||
|
"top_interactors": []
|
||||||
|
}
|
||||||
|
}
|
||||||
86
valentines_announcement.md
Normal file
86
valentines_announcement.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# 💝 Introducing the NNYC Valentine's Wish Bot! 💌
|
||||||
|
|
||||||
|
Hey NNYC Community! 💕
|
||||||
|
|
||||||
|
Remember our magical Christmas Wish Bot that brought so much joy last month? Well, **Cupid's got an upgrade**, and we're bringing back the love for Valentine's Day!
|
||||||
|
|
||||||
|
## 🌹 What's This All About?
|
||||||
|
|
||||||
|
Just like our Christmas time capsule, this is your chance to **send heartfelt messages** to your fellow community members that will be delivered **all at once on Valentine's Day (February 14th)**!
|
||||||
|
|
||||||
|
Whether it's appreciation, friendship, encouragement, or just spreading some love – this is your moment to make someone's day special! ❤️
|
||||||
|
|
||||||
|
## 📬 How to Participate (Super Easy!)
|
||||||
|
|
||||||
|
1. **Send a DM** to u/NNYC-EventBot
|
||||||
|
2. **First line:** The recipient's username starting with `u/` (example: `u/AmoreMio666`)
|
||||||
|
3. **Next lines:** Your heartfelt message!
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```
|
||||||
|
u/AmoreMio666
|
||||||
|
|
||||||
|
You've been an amazing support in this community!
|
||||||
|
Your positivity brightens everyone's day.
|
||||||
|
Happy Valentine's Day! 💕
|
||||||
|
```
|
||||||
|
|
||||||
|
The bot will ask you to confirm before saving – no accidents, no worries! 😊
|
||||||
|
|
||||||
|
## 🌱 Watch Your Garden Grow!
|
||||||
|
|
||||||
|
Just like the Christmas Forest, we now have **The Valentine's Garden**! 🌺
|
||||||
|
|
||||||
|
Check the pinned dashboard post to see who's receiving messages. The more hearts you have, the more love is coming your way on Valentine's Day! Each message = 💕 in your garden!
|
||||||
|
|
||||||
|
## ⏰ Important Dates
|
||||||
|
|
||||||
|
- **NOW - February 13th:** Send your Valentine's messages!
|
||||||
|
- **February 14th:** All messages will be delivered at once! 💌
|
||||||
|
|
||||||
|
You have **4 DAYS** to spread the love before the big reveal!
|
||||||
|
|
||||||
|
## 🎭 The Rules (Keep It Sweet!)
|
||||||
|
|
||||||
|
- ✅ **Be kind and genuine** – This is about spreading love and positivity
|
||||||
|
- ✅ **You can send to multiple people** – Share the love around!
|
||||||
|
- ✅ **You can update your message** – Changed your mind? Just send a new one to the same person
|
||||||
|
- ❌ **No spam or inappropriate content** – The bot has safety filters
|
||||||
|
- ❌ **Recipients must have user flair** – Active community members only
|
||||||
|
|
||||||
|
## 💭 Why Should I Participate?
|
||||||
|
|
||||||
|
Because imagine waking up on Valentine's Day to discover that people in this community took the time to send YOU heartfelt messages! 🥰
|
||||||
|
|
||||||
|
Last time with Christmas, we saw incredible messages of support, friendship, and community spirit. Let's make this Valentine's Day even more special!
|
||||||
|
|
||||||
|
## 🤔 FAQ
|
||||||
|
|
||||||
|
**Q: Can I send to the same person I sent a Christmas wish to?**
|
||||||
|
A: Absolutely! Spread that love! 💖
|
||||||
|
|
||||||
|
**Q: What if I make a typo?**
|
||||||
|
A: Just send another message to the same person – it will replace the old one!
|
||||||
|
|
||||||
|
**Q: Will people know who sent them messages before Valentine's Day?**
|
||||||
|
A: Nope! It's a surprise until February 14th! 🎁
|
||||||
|
|
||||||
|
**Q: Can I send anonymous messages?**
|
||||||
|
A: No, all messages will show who they're from. Keep it genuine and kind!
|
||||||
|
|
||||||
|
**Q: What if someone doesn't have flair?**
|
||||||
|
A: The bot will let you know! Only active, flaired members can participate.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💝 Let's Make This Valentine's Day Unforgettable!
|
||||||
|
|
||||||
|
The Christmas event showed us how powerful a few kind words can be. Let's do it again!
|
||||||
|
|
||||||
|
**Start sending your Valentine's messages NOW!** The garden is waiting to bloom! 🌸
|
||||||
|
|
||||||
|
*Questions? Drop them in the comments below!*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
^(Bot created by u/droid_tect | This is an automated event for r/NoNutYearlyCommunity)
|
||||||
Reference in New Issue
Block a user