|
|
|
@@ -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:
|
|
|
|
|