Skip to content

Commit

Permalink
User correct Matrix accounts when redacting events (#780)
Browse files Browse the repository at this point in the history
* Use correct Slack ghosts to redact messages sent from Slack

This makes us the original sender's intent to redact messages,
so that we do not require an elevated PL to handle message deletion.

* Use our Matrix bot to redact Matrix messages deleted on Slack

* Don't delete all bot message with our main account

* Refactor message deletion; fallback to using our bot if we can't determine a more fitting user

* Changelog
  • Loading branch information
tadzik committed Apr 29, 2024
1 parent 0b553be commit e46e46a
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog.d/766.bugfix
@@ -0,0 +1 @@
User correct Matrix accounts when redacting events.
1 change: 1 addition & 0 deletions src/BaseSlackHandler.ts
Expand Up @@ -50,6 +50,7 @@ export interface ISlackEventMessageAttachment {
}

export interface ISlackMessageEvent extends ISlackEvent {
team?: string;
team_domain?: string;
team_id?: string;
user?: string;
Expand Down
46 changes: 41 additions & 5 deletions src/SlackEventHandler.ts
Expand Up @@ -18,6 +18,7 @@ import { BaseSlackHandler, ISlackEvent, ISlackMessageEvent, ISlackUser } from ".
import { BridgedRoom } from "./BridgedRoom";
import { Main, METRIC_RECEIVED_MESSAGE } from "./Main";
import { Logger } from "matrix-appservice-bridge";
import { TeamEntry } from "./datastore/Models";
const log = new Logger("SlackEventHandler");

/**
Expand Down Expand Up @@ -289,11 +290,10 @@ export class SlackEventHandler extends BaseSlackHandler {
}
}
} else if (msg.subtype === "message_deleted" && msg.deleted_ts) {
const originalEvent = await this.main.datastore.getEventBySlackId(msg.channel, msg.deleted_ts);
if (originalEvent) {
const botClient = this.main.botIntent.matrixClient;
await botClient.redactEvent(originalEvent.roomId, originalEvent.eventId);
return;
try {
await this.deleteMessage(msg, team);
} catch (err) {
log.error(err);
}
// If we don't have the event
throw Error("unknown_message");
Expand All @@ -315,6 +315,42 @@ export class SlackEventHandler extends BaseSlackHandler {
return room.onSlackMessage(msg);
}

private async deleteMessage(msg: ISlackMessageEvent, team: TeamEntry): Promise<void> {
const originalEvent = await this.main.datastore.getEventBySlackId(msg.channel, msg.deleted_ts!);
if (originalEvent) {
const previousMessage = msg.previous_message;
if (!previousMessage) {
throw new Error(`Cannot delete message with no previous_message: ${JSON.stringify(msg)}`);
}

// Try to determine the Matrix user responsible for deleting the message, fallback to our main bot if all else fails
if (!previousMessage.user) {
log.warn("We don't know the original sender of", previousMessage, "will try to remove with our bot");
}

const isOurMessage = previousMessage.subtype === 'bot_message' && (previousMessage.bot_id === team.bot_id);

if (previousMessage.user && !isOurMessage) {
try {
const ghost = await this.main.ghostStore.get(previousMessage.user, previousMessage.team_domain, previousMessage.team);
await ghost.redactEvent(originalEvent.roomId, originalEvent.eventId);
return;
} catch (err) {
log.warn(`Failed to remove message on behalf of ${previousMessage.user}, falling back to our bot`);
}
}

try {
const botClient = this.main.botIntent.matrixClient;
await botClient.redactEvent(originalEvent.roomId, originalEvent.eventId, "Deleted on Slack");
} catch (err) {
throw new Error(
`Failed to remove message ${JSON.stringify(previousMessage)} with our Matrix bot. insufficient power level? Error: ${err}`
);
}
}
}

private async handleReaction(event: ISlackEventReaction, teamId: string) {
// Reactions store the channel in the item
const channel = event.item.channel;
Expand Down
7 changes: 7 additions & 0 deletions src/SlackGhost.ts
Expand Up @@ -321,6 +321,13 @@ export class SlackGhost {
return Slackdown.parse(body);
}

public async redactEvent(roomId: string, eventId: string) {
if (!this._intent) {
throw Error('No intent associated with ghost');
}
await this._intent.matrixClient.redactEvent(roomId, eventId);
}

public async sendInThread(roomId: string, text: string, slackRoomId: string,
slackEventTs: string, replyEvent: IMatrixReplyEvent): Promise<void> {
const content = {
Expand Down

0 comments on commit e46e46a

Please sign in to comment.