feat(guild): translate <@user.name:NAME> -> <@userId>

Before persist/parse, resolve <@user.name:NAME> (outside backticks) via
Center and rewrite to <@userId>; unresolved tokens left as-is. Translated
ids then flow into the existing mention/wakeup/sub-frame logic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-15 15:47:01 +01:00
parent 02b7c72e70
commit 22fd834ed0
3 changed files with 92 additions and 5 deletions

View File

@@ -18,7 +18,8 @@ import { Message } from '../entities/message.entity';
import { IdempotencyRecord } from '../entities/idempotency-record.entity';
import { WakeMapping } from '../entities/wake-mapping.entity';
import { parseSlashCommand } from '../channels/slash-commands';
import { parseMentions } from '../channels/mentions';
import { parseMentions, extractNameMentions, replaceNameMentions } from '../channels/mentions';
import { resolveUserNames } from '../common/center-auth';
import { TurnService } from '../channels/turn.service';
import { EventsService } from '../events/events.service';
import { clampLimit, computeNextExpectedSeq } from './pagination.util';
@@ -146,8 +147,17 @@ export class MessagingController {
const isRotating = xType === 'discuss' || xType === 'work';
const authorUserId = String(body.authorUserId ?? 'anonymous');
// ---- translate <@user.name:NAME> -> <@userId> (outside backticks) via
// Center before anything else persists/parses the content
let content = body.content ?? '';
const names = extractNameMentions(content);
if (names.length) {
const map = await resolveUserNames(names);
content = replaceNameMentions(content, map);
}
// ---- command interception: registered slash commands are never delivered
const cmd = parseSlashCommand(body.content ?? '');
const cmd = parseSlashCommand(content);
if (cmd) {
if (isRotating && cmd.name === 'no-reply') {
const { ack } = await this.turn.onNoReply(channelId, authorUserId);
@@ -163,7 +173,7 @@ export class MessagingController {
// ---- normal message
const message = await this.persistMessage(channelId, {
authorUserId,
content: body.content,
content,
clientMessageId: body.clientMessageId,
replyToMessageId: body.replyToMessageId,
mentions: body.mentions,
@@ -180,8 +190,8 @@ export class MessagingController {
data: responseBody,
});
// mentions: <@id> outside backtick spans
const mentionIds = parseMentions(body.content ?? '');
// mentions: <@id> outside backtick spans (post name-translation)
const mentionIds = parseMentions(content);
if (isRotating) {
// discuss/work: rotation (incl. mention sub-frames) picks the target