Persist t2→username mappings in reddit_users table
Adds a durable mapping so the UI never has to show raw t2_xxx IDs or MXIDs just because the in-memory cache is cold after a restart. ChatService seeds from the table on construction and writes through every resolution path; cached chats whose username is still t2_xxx get upgraded from the DB on loadCache. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
160172403b
commit
1b6dfd67e3
7 files changed
+407
-18
No files matched your search
@@ -66,6 +66,15 @@ export function runMigrations(db: Database.Database): void {
|
||||
image_url TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS reddit_users (
|
||||
t2_id TEXT PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
mxid TEXT,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_reddit_users_username
|
||||
ON reddit_users(username);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_posts_subreddit
|
||||
ON processed_posts(subreddit);
|
||||
CREATE INDEX IF NOT EXISTS idx_posts_processed_at
|
||||
|
||||
@@ -566,3 +566,83 @@ export class SuccessfulMessagesRepository {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface RedditUserRecord {
|
||||
t2Id: string;
|
||||
username: string;
|
||||
mxid: string | null;
|
||||
updatedAt: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Durable mapping of Reddit account IDs (`t2_xxx`) to current usernames.
|
||||
* Populated as the chat sync resolves IDs via Reddit's alfred/user_data
|
||||
* endpoints; read back on startup so we never have to show raw `t2_xxx`
|
||||
* or MXIDs in the UI just because the in-memory cache is cold.
|
||||
*/
|
||||
export class RedditUsersRepository {
|
||||
private upsertStmt: Database.Statement;
|
||||
private getByT2Stmt: Database.Statement;
|
||||
private getByUsernameStmt: Database.Statement;
|
||||
private getAllStmt: Database.Statement;
|
||||
|
||||
constructor(private db: Database.Database) {
|
||||
this.upsertStmt = db.prepare(`
|
||||
INSERT INTO reddit_users (t2_id, username, mxid, updated_at)
|
||||
VALUES (@t2Id, @username, @mxid, @updatedAt)
|
||||
ON CONFLICT(t2_id) DO UPDATE SET
|
||||
username = excluded.username,
|
||||
mxid = COALESCE(excluded.mxid, reddit_users.mxid),
|
||||
updated_at = excluded.updated_at
|
||||
WHERE excluded.username != ''
|
||||
`);
|
||||
this.getByT2Stmt = db.prepare(`SELECT * FROM reddit_users WHERE t2_id = ?`);
|
||||
this.getByUsernameStmt = db.prepare(`SELECT * FROM reddit_users WHERE username = ? COLLATE NOCASE`);
|
||||
this.getAllStmt = db.prepare(`SELECT * FROM reddit_users`);
|
||||
}
|
||||
|
||||
upsert(record: { t2Id: string; username: string; mxid?: string | null }): void {
|
||||
if (!record.t2Id || !record.username) return;
|
||||
if (record.username.startsWith('t2_') || record.username.startsWith('@')) return;
|
||||
const t2Id = record.t2Id.startsWith('t2_') ? record.t2Id : `t2_${record.t2Id}`;
|
||||
this.upsertStmt.run({
|
||||
t2Id,
|
||||
username: record.username,
|
||||
mxid: record.mxid ?? null,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
upsertMany(records: Array<{ t2Id: string; username: string; mxid?: string | null }>): void {
|
||||
const tx = this.db.transaction((items: typeof records) => {
|
||||
for (const r of items) this.upsert(r);
|
||||
});
|
||||
tx(records);
|
||||
}
|
||||
|
||||
getByT2(t2Id: string): RedditUserRecord | null {
|
||||
const normalized = t2Id.startsWith('t2_') ? t2Id : `t2_${t2Id}`;
|
||||
const row = this.getByT2Stmt.get(normalized) as Record<string, unknown> | undefined;
|
||||
return row ? this.mapRow(row) : null;
|
||||
}
|
||||
|
||||
getByUsername(username: string): RedditUserRecord | null {
|
||||
const row = this.getByUsernameStmt.get(username) as Record<string, unknown> | undefined;
|
||||
return row ? this.mapRow(row) : null;
|
||||
}
|
||||
|
||||
/** Bulk load — used to seed the in-memory cache on startup. */
|
||||
getAll(): RedditUserRecord[] {
|
||||
const rows = this.getAllStmt.all() as Record<string, unknown>[];
|
||||
return rows.map((r) => this.mapRow(r));
|
||||
}
|
||||
|
||||
private mapRow(row: Record<string, unknown>): RedditUserRecord {
|
||||
return {
|
||||
t2Id: row.t2_id as string,
|
||||
username: row.username as string,
|
||||
mxid: (row.mxid as string | null) ?? null,
|
||||
updatedAt: row.updated_at as number,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user