fix(#709): route Discord threads and approvals
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
This commit is contained in:
@@ -639,9 +639,36 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
|
|||||||
* Creates it if absent — safe to call concurrently since a duplicate insert
|
* Creates it if absent — safe to call concurrently since a duplicate insert
|
||||||
* would fail on the PK constraint and be caught here.
|
* would fail on the PK constraint and be caught here.
|
||||||
*/
|
*/
|
||||||
|
@SubscribeMessage('discord:approve')
|
||||||
|
async handleDiscordApproval(
|
||||||
|
@ConnectedSocket() client: Socket,
|
||||||
|
@MessageBody() envelope: DiscordIngressEnvelope,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!client.data.discordService) return;
|
||||||
|
const ingress = this.resolveDiscordIngress(client, envelope, 'approve');
|
||||||
|
const command = ingress?.content.match(/^\/approve\s+([a-z][\w-]*)$/i)?.[1];
|
||||||
|
const serviceUserId = process.env['DISCORD_SERVICE_USER_ID'];
|
||||||
|
if (!ingress || !command || !serviceUserId) return;
|
||||||
|
const approval = await this.commandExecutor.createApproval(
|
||||||
|
{ command, conversationId: ingress.conversationId },
|
||||||
|
{
|
||||||
|
userId: serviceUserId,
|
||||||
|
tenantId: process.env['DISCORD_SERVICE_TENANT_ID'] ?? serviceUserId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
client.emit('discord:approval', {
|
||||||
|
correlationId: ingress.correlationId,
|
||||||
|
command,
|
||||||
|
success: approval !== null,
|
||||||
|
approvalId: approval?.approvalId,
|
||||||
|
expiresAt: approval?.expiresAt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private resolveDiscordIngress(
|
private resolveDiscordIngress(
|
||||||
client: Socket,
|
client: Socket,
|
||||||
envelope: DiscordIngressEnvelope,
|
envelope: DiscordIngressEnvelope,
|
||||||
|
operation: 'send' | 'approve' = 'send',
|
||||||
): DiscordIngressPayload | null {
|
): DiscordIngressPayload | null {
|
||||||
const payload = verifyDiscordIngressEnvelope(
|
const payload = verifyDiscordIngressEnvelope(
|
||||||
envelope,
|
envelope,
|
||||||
@@ -662,7 +689,7 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
|
|||||||
payload.guildId,
|
payload.guildId,
|
||||||
payload.channelId,
|
payload.channelId,
|
||||||
payload.userId,
|
payload.userId,
|
||||||
'send',
|
operation,
|
||||||
);
|
);
|
||||||
if (!binding) {
|
if (!binding) {
|
||||||
this.logger.warn(`Rejected unpaired Discord ingress from ${client.id}`);
|
this.logger.warn(`Rejected unpaired Discord ingress from ${client.id}`);
|
||||||
|
|||||||
@@ -327,15 +327,18 @@ export class DiscordPlugin {
|
|||||||
},
|
},
|
||||||
this.config.serviceToken,
|
this.config.serviceToken,
|
||||||
);
|
);
|
||||||
this.socket.emit('message', envelope);
|
this.socket.emit(content.startsWith('/approve ') ? 'discord:approve' : 'message', envelope);
|
||||||
}
|
}
|
||||||
|
|
||||||
private isAllowedMessage(message: DiscordMessage): boolean {
|
private isAllowedMessage(message: DiscordMessage): boolean {
|
||||||
const guildId = message.guildId;
|
const guildId = message.guildId;
|
||||||
|
const parentChannelId = 'parentId' in message.channel ? message.channel.parentId : null;
|
||||||
|
// Threads inherit their authorization boundary from their configured parent.
|
||||||
|
const authorizationChannelId = parentChannelId ?? message.channelId;
|
||||||
return (
|
return (
|
||||||
guildId !== null &&
|
guildId !== null &&
|
||||||
includesId(this.config.allowedGuildIds, guildId) &&
|
includesId(this.config.allowedGuildIds, guildId) &&
|
||||||
includesId(this.config.allowedChannelIds, message.channelId) &&
|
includesId(this.config.allowedChannelIds, authorizationChannelId) &&
|
||||||
includesId(this.config.allowedUserIds, message.author.id)
|
includesId(this.config.allowedUserIds, message.author.id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user