Add consent-gated media exchange library

This commit is contained in:
jjbrunton committed 2026-08-09 19:24:38 +01:00
1 parent b8e4a1bb88
commit bf474dfe10
42 files changed
+2533 -9

No files matched your search

+43 -2
View File
@@ -582,7 +582,7 @@ export class ChatService {
* `https://chat.reddit.com/room/<roomId>` URL (web UI compatibility) or
* a raw Matrix room ID like `!abc:reddit.com`.
*/
async sendChatMessage(chatUrlOrRoomId: string, message: string): Promise<boolean> {
async sendChatMessage(chatUrlOrRoomId: string, message: string, txnId?: string): Promise<boolean> {
if (!this.accountAuth?.isFullyAuthenticated()) return false;
const active = this.getInvitationCooldown();
if (active && this.blocksExistingRoomSends(active)) {
@@ -599,7 +599,7 @@ export class ChatService {
this.logger.error(`[chat] cannot extract room ID from "${chatUrlOrRoomId}"`);
return false;
}
const eventId = await this.withAuth(() => this.matrix.sendTextMessage(roomId, message));
const eventId = await this.withAuth(() => this.matrix.sendTextMessage(roomId, message, txnId));
this.logger.info(`[chat] sent message to ${roomId} (event_id=${eventId})`);
const chat = this.cache.get().find((c) => c.roomId === roomId);
if (chat) {
@@ -619,6 +619,47 @@ export class ChatService {
});
}
async sendChatImage(
chatUrlOrRoomId: string,
image: { bytes: Buffer; mimeType: string; fileName: string; width: number; height: number },
txnId: string,
existingMxcUri?: string | null,
): Promise<{ eventId: string; mxcUri: string } | null> {
if (!this.accountAuth?.isFullyAuthenticated()) return null;
const active = this.getInvitationCooldown();
if (active && this.blocksExistingRoomSends(active)) return null;
return this.enqueue(async () => {
try {
const roomId = ChatService.extractRoomId(chatUrlOrRoomId);
if (!roomId) return null;
const mxcUri = existingMxcUri
?? await this.withAuth(() => this.matrix.uploadImage(image.bytes, image.mimeType, image.fileName));
const eventId = await this.withAuth(() => this.matrix.sendImageMessage(
roomId,
image.fileName,
mxcUri,
{ mimeType: image.mimeType, size: image.bytes.length, width: image.width, height: image.height },
txnId,
));
this.logger.info(`[chat] sent image to ${roomId} (event_id=${eventId})`);
const chat = this.cache.get().find((item) => item.roomId === roomId);
if (chat) {
chat.lastMessageSnippet = '[Image]';
chat.lastMessageTime = Date.now();
chat.isFromUs = true;
}
return { eventId, mxcUri };
} catch (error) {
if (error instanceof MatrixRateLimitError) {
this.setInvitationCooldown(error, 'all-sends');
return null;
}
this.logger.error('[chat] sendChatImage failed:', error);
return null;
}
});
}
/**
* Start a brand-new DM with the given Reddit user and send the opening
* line. Returns the new chat (or null on failure).