Files
titlebot-ng-ng/get_votes.py
T
2026-08-12 18:22:26 -05:00

211 lines
6.6 KiB
Python
Executable File

#!/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...")
# 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=30000)
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())