forked from nolan/titlebot-ng-ng
Not much cleanup
This commit is contained in:
@@ -2,3 +2,4 @@ config.yaml
|
||||
*.db
|
||||
__pycache__
|
||||
.env
|
||||
.env/
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
# titbot
|
||||
Title-bot for Jupiter Broadcasting's "The Launch" chat room. Why didn't I just use the old bot?
|
||||
|
||||
## Usage
|
||||
- Single entry point: `python3 titlebot.py`
|
||||
- Configuration:
|
||||
- config.yaml (ignored by git) or environment variables:
|
||||
- TITLEBOT_USERNAME, TITLEBOT_PASSWORD, TITLEBOT_ROOM_ID, TITLEBOT_HOMESERVER
|
||||
- TITLEBOT_LOOKBACK_LIMIT, TITLEBOT_BATCH_SIZE, TITLEBOT_DB_NAME
|
||||
- dbName is optional; if unset, a per-room DB name is derived automatically.
|
||||
|
||||
## TODO:
|
||||
## Notes
|
||||
- scraper.py and post_results.py are deprecated wrappers around titlebot.py.
|
||||
- db.py is deprecated; DB logic lives in room.Room.
|
||||
- Do not commit virtualenvs. Remove the tracked .env folder:
|
||||
- git rm -r --cached .env
|
||||
- git commit -m "Remove tracked venv"
|
||||
- .gitignore already ignores `.env/`
|
||||
|
||||
## TODO:
|
||||
- Re-write in Rust
|
||||
- Re-write in Go
|
||||
- Re-write in Zig
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
import re
|
||||
import sqlite3
|
||||
import time
|
||||
import datetime
|
||||
|
||||
def safe_db_name(room_id):
|
||||
safe = re.sub(r'[^a-zA-Z0-9_-]', '_', room_id)
|
||||
return f"suggestions_{safe}.db"
|
||||
|
||||
def init_db(db_path):
|
||||
conn = sqlite3.connect(db_path)
|
||||
c = conn.cursor()
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS suggestions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user TEXT,
|
||||
display_name TEXT,
|
||||
message TEXT,
|
||||
timestamp INTEGER,
|
||||
age INTEGER,
|
||||
is_posted INTEGER DEFAULT 0,
|
||||
UNIQUE(user, message, timestamp)
|
||||
)
|
||||
''')
|
||||
# Add is_posted column if it doesn't exist
|
||||
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')
|
||||
|
||||
# New: table for logging !gettitles detections
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS gettitles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
event_id TEXT,
|
||||
sender TEXT,
|
||||
timestamp INTEGER,
|
||||
datetime TEXT,
|
||||
UNIQUE(event_id),
|
||||
UNIQUE(sender, timestamp)
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def write_to_db(posts, db_path):
|
||||
conn = sqlite3.connect(db_path)
|
||||
c = conn.cursor()
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS suggestions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user TEXT,
|
||||
display_name TEXT,
|
||||
message TEXT,
|
||||
timestamp INTEGER,
|
||||
age INTEGER,
|
||||
is_posted INTEGER DEFAULT 0,
|
||||
UNIQUE(user, message, timestamp)
|
||||
)
|
||||
''')
|
||||
# Add is_posted column if it doesn't exist
|
||||
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')
|
||||
|
||||
for post in posts:
|
||||
try:
|
||||
c.execute(
|
||||
"INSERT INTO suggestions (user, display_name, message, timestamp, age, is_posted) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
post.as_tuple()
|
||||
)
|
||||
except sqlite3.IntegrityError:
|
||||
continue
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def update_last_run(room_id):
|
||||
conn = sqlite3.connect("last_run.db")
|
||||
c = conn.cursor()
|
||||
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)")
|
||||
cols = [row[1] for row in c.fetchall()]
|
||||
if 'datetime' not in cols:
|
||||
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))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def get_last_run(room_id):
|
||||
conn = sqlite3.connect("last_run.db")
|
||||
c = conn.cursor()
|
||||
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)")
|
||||
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,))
|
||||
row = c.fetchone()
|
||||
conn.close()
|
||||
if row and row[0] is not None:
|
||||
try:
|
||||
return int(row[0])
|
||||
except Exception:
|
||||
return 0
|
||||
return 0
|
||||
+23
-16
@@ -15,25 +15,32 @@ async def main():
|
||||
return
|
||||
|
||||
# Initial sync establishes the next-batch; after this, listen from "now"
|
||||
await room.client.sync(timeout=3000)
|
||||
listen_since_ms = int(time.time() * 1000)
|
||||
await room.client.sync(timeout=300)
|
||||
|
||||
room.get_last_run()
|
||||
last_post = getattr(room, "last_run", None)
|
||||
if last_post:
|
||||
print(f"Last run for this room was at UNIX timestamp {last_post}")
|
||||
try:
|
||||
while True:
|
||||
listen_since_ms = int(time.time() * 1000)
|
||||
|
||||
# Wait for !gettitles before posting
|
||||
await asyncio.wait_for(room.wait_for_gettitles(since_ms=listen_since_ms), timeout=None)
|
||||
|
||||
# Then read and post
|
||||
room.read_db(since_timestamp=last_post, only_unposted=True)
|
||||
await room.post_suggestions()
|
||||
await room.close()
|
||||
room.get_last_run()
|
||||
last_post = getattr(room, "last_run", None)
|
||||
if last_post:
|
||||
print(f"Last run for this room was at UNIX timestamp {last_post}")
|
||||
|
||||
# Wait for !gettitles before posting
|
||||
await room.wait_for_gettitles(since_ms=listen_since_ms)
|
||||
|
||||
# Then read and post
|
||||
room.read_db(since_timestamp=last_post, only_unposted=True)
|
||||
if room.posts:
|
||||
await room.post_suggestions()
|
||||
room.update_last_post()
|
||||
else:
|
||||
await room.alert_post("No new unposted suggestions found.")
|
||||
await asyncio.sleep(1)
|
||||
finally:
|
||||
await room.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
asyncio.run(main())
|
||||
time.sleep(2)
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ class Room:
|
||||
|
||||
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)
|
||||
@@ -89,7 +90,15 @@ class Room:
|
||||
conn.close()
|
||||
|
||||
async def login_to_matrix(self):
|
||||
self.client = AsyncClient(self.config.homeserver, self.config.username)
|
||||
# Initialize client if needed
|
||||
if self.client is None:
|
||||
self.client = AsyncClient(self.config.homeserver, self.config.username)
|
||||
# Reuse existing login if present
|
||||
if getattr(self.client, "access_token", None):
|
||||
print("Reusing existing login.")
|
||||
self.user_id = self.client.user_id
|
||||
return True
|
||||
|
||||
resp = await self.client.login(self.config.password)
|
||||
if isinstance(resp, LoginResponse):
|
||||
print("Logged in!")
|
||||
@@ -416,5 +425,5 @@ class Room:
|
||||
async def close(self):
|
||||
if self.client:
|
||||
await self.client.close()
|
||||
|
||||
|
||||
|
||||
|
||||
+20
-17
@@ -7,7 +7,6 @@ import time
|
||||
from post import Post
|
||||
from room import Room
|
||||
|
||||
|
||||
async def main():
|
||||
room = Room()
|
||||
|
||||
@@ -16,24 +15,28 @@ async def main():
|
||||
print("Failed to login")
|
||||
return
|
||||
|
||||
|
||||
await room.alert_post("SCRAPER LOGGED IN")
|
||||
|
||||
# Initial sync
|
||||
await room.client.sync(timeout=3000)
|
||||
|
||||
# Get last run timestamp
|
||||
room.get_last_run()
|
||||
if room.last_run:
|
||||
print(f"Last run for this room was at UNIX timestamp {room.last_run}")
|
||||
|
||||
# Initial scrape and write to database
|
||||
await room.get_convos()
|
||||
room.write_to_db()
|
||||
room.update_last_run()
|
||||
|
||||
# Close connection
|
||||
await room.close()
|
||||
try:
|
||||
while True:
|
||||
# Get last run timestamp
|
||||
room.get_last_run()
|
||||
if room.last_run:
|
||||
print(f"Last run for this room was at UNIX timestamp {room.last_run}")
|
||||
|
||||
# Scrape and write to database
|
||||
await room.get_convos()
|
||||
room.write_to_db()
|
||||
room.update_last_run()
|
||||
print(f"Stored {len(room.posts)} suggestions in database.")
|
||||
await asyncio.sleep(0.5)
|
||||
finally:
|
||||
# Close connection
|
||||
await room.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
while True:
|
||||
asyncio.run(main())
|
||||
time.sleep(5)
|
||||
print("Loop completed, waiting before next iteration...")
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user