From fffb9c50b40cb1724bdf7253d63904c28c78e9f8 Mon Sep 17 00:00:00 2001 From: nolan Date: Sun, 16 Aug 2026 12:02:37 -0400 Subject: [PATCH] ??? --- .gitignore | 3 +- get_user_list.py | 19 ++++++----- post_results.py | 1 + pyrightconfig.json | 6 ++++ room.py | 85 +++++++++++++++++++++++++--------------------- scraper.py | 1 + 6 files changed, 66 insertions(+), 49 deletions(-) create mode 100644 pyrightconfig.json diff --git a/.gitignore b/.gitignore index f3fb4d3..00a79fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ config/* - +.venv/ +__pycache__/ diff --git a/get_user_list.py b/get_user_list.py index 43abb81..59f2520 100755 --- a/get_user_list.py +++ b/get_user_list.py @@ -9,7 +9,7 @@ import asyncio import sqlite3 import time import sys -from nio import AsyncClient, LoginResponse, RoomMessagesResponse, SyncResponse +from nio import AsyncClient, LoginResponse, RoomMessageText, RoomMessagesResponse, SyncResponse from room import Room import dbutil @@ -79,6 +79,7 @@ class UserListManager: return [] # Sync to get room state + assert self.room.client is not None sync_resp = await self.room.client.sync(timeout=3000) if not isinstance(sync_resp, SyncResponse): print(f"Sync failed: {sync_resp}") @@ -119,6 +120,7 @@ class UserListManager: fetched = 0 # Fetch messages in batches to count posts per user + assert self.room.client is not None while fetched < lookback_limit: try: response = await self.room.client.room_messages( @@ -133,18 +135,17 @@ class UserListManager: fetched += len(response.chunk) for event in response.chunk: - if hasattr(event, 'sender') and hasattr(event, 'body'): + if isinstance(event, RoomMessageText): sender = event.sender post_counts[sender] = post_counts.get(sender, 0) + 1 # Track the most recent message timestamp for each user - if hasattr(event, 'server_timestamp'): - event_time = int(event.server_timestamp // 1000) - if sender not in last_activity or event_time > last_activity[sender]: - last_activity[sender] = event_time - # Track the absolute most recent post in the room - if event_time > most_recent_post: - most_recent_post = event_time + event_time = int(event.server_timestamp // 1000) + if sender not in last_activity or event_time > last_activity[sender]: + last_activity[sender] = event_time + # Track the absolute most recent post in the room + if event_time > most_recent_post: + most_recent_post = event_time next_batch = response.end diff --git a/post_results.py b/post_results.py index 4f38fad..3d51545 100755 --- a/post_results.py +++ b/post_results.py @@ -35,6 +35,7 @@ async def main(): return # Initial sync establishes the next-batch; after this, listen from "now" + assert room.client is not None await room.client.sync(timeout=300) room.write_room_slugs() diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..4c002de --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,6 @@ +{ + "venvPath": ".", + "venv": ".venv", + "pythonVersion": "3.14", + "typeCheckingMode": "basic" +} diff --git a/room.py b/room.py index 35a261a..27d9df7 100755 --- a/room.py +++ b/room.py @@ -10,7 +10,7 @@ import yaml import sqlite3 import re import asyncio -from nio import AsyncClient, LoginResponse, RoomMessageText, SyncResponse, RoomSendResponse +from nio import AsyncClient, LoginResponse, MessageDirection, ProfileGetDisplayNameResponse, RoomMessageText, RoomMessagesResponse, RoomSendResponse, SyncResponse from post import Post import requests import dbutil @@ -64,7 +64,7 @@ class Room: def init_db(self): conn = dbutil.connect(self.db_path) c = conn.cursor() - c.execute(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS suggestions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT, @@ -78,17 +78,17 @@ class Room: ) ''') # Add is_posted column if it doesn't exist - c.execute("PRAGMA table_info(suggestions)") + _ = c.execute("PRAGMA table_info(suggestions)") cols = [row[1] for row in c.fetchall()] if 'is_posted' not in cols: - c.execute('ALTER TABLE suggestions ADD COLUMN is_posted INTEGER DEFAULT 0') + _ = c.execute('ALTER TABLE suggestions ADD COLUMN is_posted INTEGER DEFAULT 0') if 'event_id' not in cols: - c.execute('ALTER TABLE suggestions ADD COLUMN event_id TEXT') + _ = 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)') + _ = 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(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS gettitles ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_id TEXT, @@ -101,7 +101,7 @@ class Room: ''') # New: table to log all !mayhem detections (prevents duplicates) - c.execute(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS mayhem ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_id TEXT, @@ -114,7 +114,7 @@ class Room: ''') # New: table to log all !countvotes detections (prevents duplicates) - c.execute(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS countvotes ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_id TEXT, @@ -127,7 +127,7 @@ class Room: ''') # New: table to log all !voteurl detections (prevents duplicates) - c.execute(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS voteurl ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_id TEXT, @@ -161,13 +161,14 @@ class Room: return False async def get_display_name(self, user_id): + assert self.client is not None room = self.client.rooms.get(self.config.room_id) if room and user_id in room.users: member = room.users[user_id] if hasattr(member, "display_name") and member.display_name: return member.display_name resp = await self.client.get_displayname(user_id) - if hasattr(resp, "displayname") and resp.displayname: + if isinstance(resp, ProfileGetDisplayNameResponse) and resp.displayname: return resp.displayname return user_id @@ -219,6 +220,7 @@ class Room: batch_size = self.config.batch_size next_batch = None fetched = 0 + assert self.client is not None while fetched < limit: fetch_amount = min(batch_size, limit - fetched) @@ -226,10 +228,10 @@ class Room: self.config.room_id, start=next_batch, limit=fetch_amount, - direction="b" + direction=MessageDirection.back ) - if not hasattr(response, "chunk") or not response.chunk: + if not isinstance(response, RoomMessagesResponse) or not response.chunk: break for event in response.chunk: @@ -304,7 +306,7 @@ class Room: self.newly_inserted_posts = [] for post in self.posts: try: - c.execute( + _ = c.execute( "INSERT INTO suggestions (user, display_name, message, timestamp, age, is_posted, event_id) VALUES (?, ?, ?, ?, ?, ?, ?)", post.as_tuple() ) @@ -319,21 +321,21 @@ class Room: room_id = self.config.room_id conn = dbutil.connect("last_run.db") c = conn.cursor() - c.execute(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS last_run ( room_id TEXT PRIMARY KEY, timestamp INTEGER, datetime TEXT ) ''') - c.execute("PRAGMA table_info(last_run)") + _ = c.execute("PRAGMA table_info(last_run)") cols = [row[1] for row in c.fetchall()] if 'datetime' not in cols: - c.execute('ALTER TABLE last_run ADD COLUMN datetime TEXT') + _ = c.execute('ALTER TABLE last_run ADD COLUMN datetime TEXT') now = int(_time.time()) now_str = datetime.datetime.now().isoformat(sep=' ', timespec='seconds') - c.execute('INSERT OR REPLACE INTO last_run (room_id, timestamp, datetime) VALUES (?, ?, ?)', (room_id, now, now_str)) + _ = c.execute('INSERT OR REPLACE INTO last_run (room_id, timestamp, datetime) VALUES (?, ?, ?)', (room_id, now, now_str)) dbutil.commit(conn) conn.close() @@ -341,7 +343,7 @@ class Room: room_id = self.config.room_id conn = dbutil.connect("last_run.db") c = conn.cursor() - c.execute(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS last_run ( room_id TEXT PRIMARY KEY, timestamp INTEGER, @@ -349,11 +351,11 @@ class Room: ) ''') - c.execute("PRAGMA table_info(last_run)") + _ = c.execute("PRAGMA table_info(last_run)") cols = [row[1] for row in c.fetchall()] if 'datetime' not in cols: - c.execute('ALTER TABLE last_run ADD COLUMN datetime TEXT') - c.execute('SELECT timestamp FROM last_run WHERE room_id = ?', (room_id,)) + _ = c.execute('ALTER TABLE last_run ADD COLUMN datetime TEXT') + _ = c.execute('SELECT timestamp FROM last_run WHERE room_id = ?', (room_id,)) row = c.fetchone() conn.close() if row: @@ -366,7 +368,7 @@ class Room: conn = dbutil.connect(self.db_path) c = conn.cursor() dt_str = datetime.datetime.fromtimestamp(int(timestamp)).isoformat(sep=' ', timespec='seconds') - c.execute(''' + _ = c.execute(''' INSERT OR IGNORE INTO gettitles (event_id, sender, timestamp, datetime) VALUES (?, ?, ?, ?) ''', (event_id, sender, int(timestamp), dt_str)) @@ -381,7 +383,7 @@ class Room: conn = dbutil.connect(self.db_path) c = conn.cursor() dt_str = datetime.datetime.fromtimestamp(int(timestamp)).isoformat(sep=' ', timespec='seconds') - c.execute(''' + _ = c.execute(''' INSERT OR IGNORE INTO mayhem (event_id, sender, timestamp, datetime) VALUES (?, ?, ?, ?) ''', (event_id, sender, int(timestamp), dt_str)) @@ -396,7 +398,7 @@ class Room: conn = dbutil.connect(self.db_path) c = conn.cursor() dt_str = datetime.datetime.fromtimestamp(int(timestamp)).isoformat(sep=' ', timespec='seconds') - c.execute(''' + _ = c.execute(''' INSERT OR IGNORE INTO countvotes (event_id, sender, timestamp, datetime) VALUES (?, ?, ?, ?) ''', (event_id, sender, int(timestamp), dt_str)) @@ -411,7 +413,7 @@ class Room: conn = dbutil.connect(self.db_path) c = conn.cursor() dt_str = datetime.datetime.fromtimestamp(int(timestamp)).isoformat(sep=' ', timespec='seconds') - c.execute(''' + _ = c.execute(''' INSERT OR IGNORE INTO voteurl (event_id, sender, timestamp, datetime) VALUES (?, ?, ?, ?) ''', (event_id, sender, int(timestamp), dt_str)) @@ -427,6 +429,7 @@ class Room: next_batch = None suggestion_votes = {} all_events = [] + assert self.client is not None # First pass: collect all recent events (both messages and reactions) for _ in range(15): # Look back further to catch more events @@ -434,10 +437,10 @@ class Room: self.config.room_id, start=next_batch, limit=batch_size, - direction="b" + direction=MessageDirection.back ) - if not hasattr(response, "chunk") or not response.chunk: + if not isinstance(response, RoomMessagesResponse) or not response.chunk: break all_events.extend(response.chunk) @@ -522,6 +525,7 @@ class Room: if since_ms is None: since_ms = int(_time.time() * 1000) + assert self.client is not None while True: sync_response = await self.client.sync(timeout=30000) if not isinstance(sync_response, SyncResponse): @@ -585,6 +589,7 @@ class Room: return result == "gettitles" async def alert_post(self, post): + assert self.client is not None content = { "msgtype": "m.text", "body": post @@ -628,7 +633,7 @@ class Room: if conditions: query += " WHERE " + " AND ".join(conditions) query += " ORDER BY timestamp ASC" - c.execute(query, tuple(params)) + _ = c.execute(query, tuple(params)) rows = c.fetchall() conn.close() self.posts = [Post(poster=row[0], display_name=row[1], content=row[2], time=row[3], age=row[4], is_posted=bool(row[5])) for row in rows] @@ -636,18 +641,18 @@ class Room: def get_last_post(self): conn = dbutil.connect("last_post.db") c = conn.cursor() - c.execute(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS last_post ( room_id TEXT PRIMARY KEY, timestamp INTEGER, datetime TEXT ) ''') - c.execute("PRAGMA table_info(last_post)") + _ = c.execute("PRAGMA table_info(last_post)") cols = [row[1] for row in c.fetchall()] if 'datetime' not in cols: - c.execute('ALTER TABLE last_post ADD COLUMN datetime TEXT') - c.execute('SELECT timestamp FROM last_post WHERE room_id = ?', (self.config.room_id,)) + _ = c.execute('ALTER TABLE last_post ADD COLUMN datetime TEXT') + _ = c.execute('SELECT timestamp FROM last_post WHERE room_id = ?', (self.config.room_id,)) row = c.fetchone() conn.close() if row: @@ -657,29 +662,29 @@ class Room: def update_last_post(self): conn = dbutil.connect("last_post.db") c = conn.cursor() - c.execute(''' + _ = c.execute(''' CREATE TABLE IF NOT EXISTS last_post ( room_id TEXT PRIMARY KEY, timestamp INTEGER, datetime TEXT ) ''') - c.execute("PRAGMA table_info(last_post)") + _ = c.execute("PRAGMA table_info(last_post)") cols = [row[1] for row in c.fetchall()] if 'datetime' not in cols: - c.execute('ALTER TABLE last_post ADD COLUMN datetime TEXT') + _ = 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)) dbutil.commit(conn) conn.close() async def post_suggestions(self): + assert self.client is not None # 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 @@ -714,6 +719,7 @@ class Room: async def post_suggestions_individually(self): """Post each unposted suggestion as its own message with a 👍 reaction for voting.""" + assert self.client is not None 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.") @@ -791,7 +797,7 @@ class Room: conn = dbutil.connect(self.db_path) c = conn.cursor() for post in posts: - c.execute( + _ = c.execute( "UPDATE suggestions SET is_posted = 1 WHERE user = ? AND message = ? AND timestamp = ?", (post.poster, post.content, post.time) ) @@ -804,6 +810,7 @@ class Room: async def acknowledge_new_suggestions(self): """React ✅ to newly recorded suggestions to confirm they were saved.""" + assert self.client is not None if not self.newly_inserted_posts: return for post in self.newly_inserted_posts: diff --git a/scraper.py b/scraper.py index 80937cb..d1dca2d 100755 --- a/scraper.py +++ b/scraper.py @@ -21,6 +21,7 @@ async def main(): # await room.alert_post("SCRAPER LOGGED IN") # Initial sync + assert room.client is not None await room.client.sync(timeout=3000) room.write_room_slugs()