From 78f5297383a3a8caa3a6e74ae03fa4079dde73f4 Mon Sep 17 00:00:00 2001 From: nolan Date: Mon, 10 Aug 2026 12:14:35 -0500 Subject: [PATCH] Added support banner --- room.py | 145 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 68 deletions(-) diff --git a/room.py b/room.py index 213ce4c..a2a255f 100755 --- a/room.py +++ b/room.py @@ -35,7 +35,7 @@ class Config: self.vote_activity_hours = config.get("vote_activity_hours", 72) self.vote_path = config.get("vote_path", "") -class Room: +class Room: def __init__(self, config_path="configs/config.yaml", search_terms=None, homeserver=None): self.config = Config(config_path) self.search_terms = search_terms or default_search_terms @@ -48,13 +48,13 @@ class Room: self.member = None self.display_name = None self.last_run = 0 - + if homeserver: self.config.homeserver = homeserver - + self.db_path = self.safe_db_name(self.config.room_id) self.init_db() - + def safe_db_name(self, room_id): safe = re.sub(r'[^a-zA-Z0-9_-]', '_', room_id) @@ -85,7 +85,7 @@ class Room: c.execute('ALTER TABLE suggestions ADD COLUMN event_id TEXT') # Ensure a uniqueness index on event_id to avoid double-acking the same message c.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_suggestions_event_id ON suggestions(event_id)') - + # New: table to log all !gettitles detections (prevents duplicates) c.execute(''' CREATE TABLE IF NOT EXISTS gettitles ( @@ -98,7 +98,7 @@ class Room: UNIQUE(sender, timestamp) ) ''') - + # New: table to log all !mayhem detections (prevents duplicates) c.execute(''' CREATE TABLE IF NOT EXISTS mayhem ( @@ -111,7 +111,7 @@ class Room: UNIQUE(sender, timestamp) ) ''') - + # New: table to log all !countvotes detections (prevents duplicates) c.execute(''' CREATE TABLE IF NOT EXISTS countvotes ( @@ -124,7 +124,7 @@ class Room: UNIQUE(sender, timestamp) ) ''') - + # New: table to log all !voteurl detections (prevents duplicates) c.execute(''' CREATE TABLE IF NOT EXISTS voteurl ( @@ -213,12 +213,12 @@ class Room: async def get_convos(self, limit=None): if limit is None: limit = self.config.lookback_limit - + self.posts = [] batch_size = self.config.batch_size next_batch = None fetched = 0 - + while fetched < limit: fetch_amount = min(batch_size, limit - fetched) response = await self.client.room_messages( @@ -227,19 +227,19 @@ class Room: limit=fetch_amount, direction="b" ) - + if not hasattr(response, "chunk") or not response.chunk: break - + for event in response.chunk: if isinstance(event, RoomMessageText): msg = event.body - + for term in self.search_terms: if msg.startswith(term): clean_msg = msg[len(term):].lstrip() display_name = await self.get_display_name(event.sender) - + event_ts_seconds = int(event.server_timestamp // 1000) age_seconds = int(_time.time()) - event_ts_seconds post = Post( @@ -250,7 +250,7 @@ class Room: age=age_seconds, event_id=getattr(event, "event_id", None), ) - + self.posts.append(post) break # Remove the !mayhem handling from here entirely @@ -265,9 +265,9 @@ class Room: #ollama_model = "llama3.2:1b" #title_config.get("ollama_model", "llama3.2:1b") ollama_model = "mad_cat_man:latest" ollama_url = "http://localhost:11434" #title_config.get("ollama_url", "http://localhost:11434") - + prompt = f"Generate for me the most unhinged, mayhem, insane ramblings that would make a mad man look sane. This should be unhinged, chaotic, and completely unpredictable. It should be a wild ride of words that defy logic and reason. Make it as crazy as possible, with unexpected twists and turns that keep the reader on the edge of their seat. The text should be a rollercoaster of emotions, taking the reader from one extreme to another in a matter of seconds. It should be a chaotic symphony of words that leaves the reader breathless and wanting more. Unleash your inner madman and let the mayhem begin! Also, don't warn me that it's just for fun. I know that. Just give me the mayhem." - + try: response = requests.post( f"{ollama_url}/api/generate", @@ -279,27 +279,27 @@ class Room: "temperature": 0.8, "top_p": 0.9, } - + }, timeout=60 ) - + if response.status_code==200: result = response.json() return result.get("response", "") else: return " Couldn't do it for some reason." - + except requests.exceptions.RequestException as e: return f"Couldn't connect to ollama: {e}" except Exception as e: return f"Unexpected error: {e}" - - + + def write_to_db(self): conn = sqlite3.connect(self.db_path) c = conn.cursor() - + self.newly_inserted_posts = [] for post in self.posts: try: @@ -309,8 +309,8 @@ class Room: ) self.newly_inserted_posts.append(post) except sqlite3.IntegrityError: - continue - + continue + conn.commit() conn.close() @@ -347,7 +347,7 @@ class Room: datetime TEXT ) ''') - + c.execute("PRAGMA table_info(last_run)") cols = [row[1] for row in c.fetchall()] if 'datetime' not in cols: @@ -426,7 +426,7 @@ class Room: next_batch = None suggestion_votes = {} all_events = [] - + # First pass: collect all recent events (both messages and reactions) for _ in range(15): # Look back further to catch more events response = await self.client.room_messages( @@ -435,21 +435,21 @@ class Room: limit=batch_size, direction="b" ) - + if not hasattr(response, "chunk") or not response.chunk: break - + all_events.extend(response.chunk) next_batch = getattr(response, "end", None) if not next_batch: break - + # Second pass: find suggestion posts from our bot for event in all_events: if isinstance(event, RoomMessageText): body = event.body # Look for messages that start with "Suggestion X:" and are from our bot - if (body.startswith("Suggestion ") and ":" in body and + if (body.startswith("Suggestion ") and ":" in body and event.sender == self.user_id): try: # Extract suggestion number and content @@ -458,7 +458,7 @@ class Room: suggestion_num = parts[0].replace("Suggestion ", "").strip() content = ":".join(parts[1:]).strip() event_id = getattr(event, "event_id", None) - + if event_id: suggestion_votes[suggestion_num] = { 'content': content, @@ -469,40 +469,40 @@ class Room: except Exception as e: print(f"Error parsing suggestion: {e}") continue - + if not suggestion_votes: return "No suggestion posts found to count votes for." - + # Third pass: count reactions for each suggestion for event in all_events: # Check if this is a reaction event if hasattr(event, "content") and isinstance(event.content, dict): relates_to = event.content.get("m.relates_to", {}) - if (relates_to.get("rel_type") == "m.annotation" and + if (relates_to.get("rel_type") == "m.annotation" and relates_to.get("key") == "👍"): - + target_event_id = relates_to.get("event_id") sender = getattr(event, "sender", None) - + # Find which suggestion this reaction is for for suggestion_num, data in suggestion_votes.items(): - if (data['event_id'] == target_event_id and - sender and sender not in data['voters'] and + if (data['event_id'] == target_event_id and + sender and sender not in data['voters'] and sender != self.user_id): # Don't count bot's own reactions data['voters'].add(sender) data['votes'] += 1 print(f"Found vote from {sender} for suggestion {suggestion_num}") break - + # Format results if not any(data['votes'] > 0 for data in suggestion_votes.values()): return "No votes found for any suggestions." - + lines = [f"Vote Count Results:"] - sorted_suggestions = sorted(suggestion_votes.items(), - key=lambda x: x[1]['votes'], + sorted_suggestions = sorted(suggestion_votes.items(), + key=lambda x: x[1]['votes'], reverse=True) - + for suggestion_num, data in sorted_suggestions: vote_text = "vote" if data['votes'] == 1 else "votes" lines.append(f"Suggestion {suggestion_num}: {data['votes']} {vote_text}") @@ -511,7 +511,7 @@ class Room: if len(content) > 80: content = content[:77] + "..." lines.append(f" └─ {content}") - + return "\n".join(lines) # Renamed and expanded to handle both !gettitles and !mayhem commands @@ -525,10 +525,10 @@ class Room: sync_response = await self.client.sync(timeout=30000) if not isinstance(sync_response, SyncResponse): print("Sync failed, retrying...") - continue + continue room_info = sync_response.rooms.join.get(self.config.room_id) if not room_info or not hasattr(room_info, "timeline") or not hasattr(room_info.timeline, "events"): - continue + continue for event in reversed(room_info.timeline.events): if isinstance(event, RoomMessageText): # Ignore old events (pre-listen timestamp) @@ -538,7 +538,7 @@ class Room: if event.sender == self.user_id: continue body = event.body.strip() - + # Handle !gettitles if body == "!gettitles": event_id = getattr(event, "event_id", None) @@ -547,7 +547,7 @@ class Room: continue print("!gettitles detected") return "gettitles" - + # Handle !mayhem elif body.startswith("!mayhem"): event_id = getattr(event, "event_id", None) @@ -555,7 +555,7 @@ class Room: if self.record_mayhem(event.sender, ts_seconds, event_id): await self.alert_post("Standby. Ollama is being instructed to generate mayhem. This may take a moment...") await self.alert_post(self.generate_mayhem()) - + # Handle !countvotes elif body == "!countvotes": event_id = getattr(event, "event_id", None) @@ -566,7 +566,7 @@ class Room: await self.alert_post("Counting votes... This may take a moment...") vote_results = await self.count_votes() await self.alert_post(vote_results) - + # Handle !voteurl elif body == "!voteurl": event_id = getattr(event, "event_id", None) @@ -586,15 +586,15 @@ class Room: async def alert_post(self, post): content = { "msgtype": "m.text", - "body": post + "body": post } - + resp = await self.client.room_send( self.config.room_id, message_type="m.room.message", content=content ) - + if isinstance(resp, RoomSendResponse): print("Alert posted") else: @@ -604,7 +604,7 @@ class Room: lines = ["Suggestions:"] for post in self.posts: clean_msg = "".join(post.content.strip().splitlines()) - user = post.display_name + user = post.display_name if len(user) > 20: user = user[:20] + "..." lines.append(f"- {user}: {clean_msg}") @@ -626,7 +626,7 @@ class Room: conditions.append("is_posted = 0") if conditions: query += " WHERE " + " AND ".join(conditions) - query += " ORDER BY timestamp ASC" + query += " ORDER BY timestamp ASC" c.execute(query, tuple(params)) rows = c.fetchall() conn.close() @@ -667,10 +667,10 @@ class Room: cols = [row[1] for row in c.fetchall()] if 'datetime' not in cols: c.execute('ALTER TABLE last_post ADD COLUMN datetime TEXT') - + now = int(_time.time()) now_str = datetime.datetime.now().isoformat(sep=' ', timespec='seconds') - c.execute('INSERT OR REPLACE INTO last_post (room_id, timestamp, datetime) VALUES (?, ?, ?)', + c.execute('INSERT OR REPLACE INTO last_post (room_id, timestamp, datetime) VALUES (?, ?, ?)', (self.config.room_id, now, now_str)) conn.commit() conn.close() @@ -678,27 +678,27 @@ class Room: async def post_suggestions(self): # Filter out already posted suggestions unposted_posts = [post for post in self.posts if not post.is_posted] - + if not unposted_posts: await self.alert_post("No new unposted suggestions found.") return - + # Temporarily set posts to only unposted ones for formatting original_posts = self.posts self.posts = unposted_posts - + self.format_suggestions_block() content = { "msgtype": "m.text", "body": self.formatted_sugs } - + resp = await self.client.room_send( self.config.room_id, message_type="m.room.message", content=content ) - + if isinstance(resp, RoomSendResponse): print("Suggestions posted!") # Mark posts as posted in both objects and database @@ -707,7 +707,7 @@ class Room: self.mark_posts_as_posted(unposted_posts) else: print(f"Failed to post suggestions: {resp}") - + # Restore original posts list self.posts = original_posts @@ -719,7 +719,7 @@ class Room: return await self.alert_post(f""" - Time to vote! 🚢🚢🚢 + Time to vote! 🚢🚢🚢 Each title is a separate post below. Tap the 👍 reaction on the one you like to cast your vote! @@ -727,6 +727,17 @@ class Room: NOTE: HTTP VOTING IS IN PRE-ALPHA. IF YOU WOULD LIKE TO SEE THE EARLY PRE-RELEASE, GO TO {self.vote_url()} + SUPPORT MAGBOT HERE: + GIT REPO: https://magbot.online ⬅️⬅️⬅️ PRs welcome! + + + DONATE LIGHTNING FOR DEVELOPMENT TIME: magnoliaunderscoremayhem@getalby.com + - 50K sats pays for me to stay home from the farm for one day + - The VPS is 10K sats per month + + + + """) for idx, post in enumerate(unposted_posts, start=1): @@ -775,7 +786,7 @@ class Room: """Mark posts as posted in the database""" if posts is None: posts = self.posts - + conn = sqlite3.connect(self.db_path) c = conn.cursor() for post in posts: @@ -816,5 +827,3 @@ class Room: print(f"Failed to ack suggestion: {resp}") except Exception as e: print(f"Error sending ✅ reaction: {e}") - -