#!/usr/bin/env python3 """ Script to monitor Matrix chat for !getvotes command and post vote results. Reads from voteServer/vote_summary.json and posts results ordered from least to most votes. """ import asyncio import json import os import sys import time as _time from room import Room async def main(): """Main function to monitor for !getvotes command and post results.""" # Initialize room connection config_path = sys.argv[1] if len(sys.argv) > 1 else "configs/config.yaml" room = Room(config_path=config_path) # Login to Matrix if not await room.login_to_matrix(): print("Failed to login to Matrix") sys.exit(1) print("Logged in and monitoring for !getvotes command...") print("Press Ctrl+C to exit") # Add getvotes table to database if it doesn't exist init_getvotes_table(room) # Start listening for commands from "now" since_ms = int(_time.time() * 1000) try: while True: # Wait for !getvotes command command = await wait_for_getvotes(room, since_ms) if command: print("!getvotes command detected, posting vote results...") # Clear the web poll by marking all pending suggestions as posted room.mark_submissions_posted() # Read and format vote results vote_message = format_vote_results(room) # Post to chat await room.alert_post(vote_message) print("Vote results posted successfully") except KeyboardInterrupt: print("\nShutting down...") finally: await room.close() def init_getvotes_table(room): """Initialize the getvotes tracking table in the database.""" import dbutil conn = dbutil.connect(room.db_path) c = conn.cursor() c.execute(''' CREATE TABLE IF NOT EXISTS getvotes ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_id TEXT, sender TEXT, timestamp INTEGER, datetime TEXT, UNIQUE(event_id), UNIQUE(sender, timestamp) ) ''') dbutil.commit(conn) conn.close() def get_last_getvotes_timestamp(room): """Get the timestamp of the last !getvotes command for this room.""" import dbutil conn = dbutil.connect(room.db_path) c = conn.cursor() c.execute(''' SELECT MAX(timestamp) FROM getvotes ''') row = c.fetchone() conn.close() if row and row[0]: return row[0] return None def record_getvotes(room, sender, timestamp, event_id=None): """Record a !getvotes detection; returns True if newly recorded (not duplicate).""" import dbutil import datetime conn = dbutil.connect(room.db_path) c = conn.cursor() dt_str = datetime.datetime.fromtimestamp(int(timestamp)).isoformat(sep=' ', timespec='seconds') c.execute(''' INSERT OR IGNORE INTO getvotes (event_id, sender, timestamp, datetime) VALUES (?, ?, ?, ?) ''', (event_id, sender, int(timestamp), dt_str)) inserted = c.rowcount == 1 dbutil.commit(conn) conn.close() return inserted async def wait_for_getvotes(room, since_ms): """Wait for !getvotes command in the chat.""" from nio import SyncResponse, RoomMessageText sync_response = await room.client.sync(timeout=room.config.command_sync_timeout) if not isinstance(sync_response, SyncResponse): return None room_info = sync_response.rooms.join.get(room.config.room_id) if not room_info or not hasattr(room_info, "timeline") or not hasattr(room_info.timeline, "events"): return None for event in reversed(room_info.timeline.events): if isinstance(event, RoomMessageText): # Ignore old events (pre-listen timestamp) if getattr(event, "server_timestamp", 0) < since_ms: continue # Ignore our own messages if event.sender == room.user_id: continue body = event.body.strip() # Handle !getvotes if body == "!getvotes": event_id = getattr(event, "event_id", None) ts_seconds = int(getattr(event, "server_timestamp", 0) // 1000) if not record_getvotes(room, event.sender, ts_seconds, event_id): continue # Duplicate, skip return "getvotes" return None def format_vote_results(room): """Read votes from database and format results ordered from least to most votes.""" import re # Generate database name based on room ID (same pattern as suggestions db) safe = re.sub(r'[^a-zA-Z0-9_-]', '_', room.config.room_id) vote_db = f"votes_{safe}.db" # Check if database exists if not os.path.exists(vote_db): return "❌ No vote data available yet. The voting system may not have received any votes." try: import dbutil # Read votes from database conn = dbutil.connect(vote_db) c = conn.cursor() # Get all votes grouped by submission c.execute(''' SELECT selected_submission, COUNT(*) as vote_count, GROUP_CONCAT(voter_display_name, ', ') as voters FROM votes GROUP BY selected_submission ORDER BY vote_count ASC ''') results = c.fetchall() # Get total votes c.execute('SELECT COUNT(*) FROM votes') total_votes = c.fetchone()[0] conn.close() if not results or total_votes == 0: return "📊 No votes have been cast yet." # Build the message lines = ["📊 Vote Results (ordered from least to most votes):", ""] for submission, count, voters in results: vote_word = "vote" if count == 1 else "votes" lines.append(f"• {submission}: {count} {vote_word}") lines.append("") lines.append(f"Total votes cast: {total_votes}") # Find winner(s) max_votes = results[-1][1] if results else 0 winners = [sub for sub, count, _ in results if count == max_votes] if max_votes > 0: if len(winners) > 1: lines.append(f"🏆 Tie between {len(winners)} submissions with {max_votes} votes each!") else: lines.append(f"🏆 Current leader: {winners[0]} ({max_votes} votes)") return "\n".join(lines) except Exception as e: return f"❌ Error reading vote data: {str(e)}" if __name__ == "__main__": asyncio.run(main())