Skip to main content

Read mentions

Extract mention data from messages your code receives to identify referenced users, roles, and channels.

Message properties

When you receive a message, three properties contain mention-related information:

PropertyTypeDescription
messageContentstringRaw message text with root:// URIs in Markdown link format
messageUrisMessageUri[]Extracted URIs from the message (what was referenced)
referenceMapsMessageReferenceMapsCurrent display names for all mentioned entities

What the data looks like

Given a message with this content:

Hey [@Alice](root://user/abc123), check out [#Announcements](root://channel/def456)!

The messageUris array contains each extracted URI:

[
{ uri: "root://user/abc123" },
{ uri: "root://channel/def456" }
]

The referenceMaps contains the current names for those entities:

{
users: [{ userId: "abc123", name: "Alice" }],
channels: [{ channelId: "def456", name: "Announcements" }]
}

If Alice changed her display name to "Alicia", the messageContent would still show [@Alice], but referenceMaps.users would show { userId: "abc123", name: "Alicia" }.

Why referenceMaps exists

The messageUris array tells you what was mentioned (via IDs in the URIs), but not the current names. The messageContent has display text, but it's frozen from when the message was sent. If you need to show Alice's current name but she renamed herself to "Alicia", you'd have to make an API call to look it up.

The referenceMaps property saves you that work. When the server delivers a message to your code, it resolves each mention URI to the entity's current display name. This lets you:

  • Show current names in logs or responses
  • Look up mentioned users without extra API calls
  • Match mentions against current usernames

The referenceMaps structure

type MessageReferenceMaps = {
users?: MessageReferenceMapUser[]; // Mentioned users
channels?: MessageReferenceMapChannel[]; // Mentioned channels
roles?: MessageReferenceMapCommunityRole[]; // Mentioned roles (including @All/@Here)
imageAssets?: { [uri: string]: AssetImage }; // Image metadata
assets?: { [uri: string]: AssetInformation }; // File attachment metadata
}

Each array contains entries with IDs and current names:

// User entry
{ userId: "abc123", name: "Alicia" }

// Channel entry
{ channelId: "def456", name: "General" }

// Role entry
{ communityRoleId: "ghi789", name: "Moderators" }
// For @All: { communityRoleId: "All", name: "All" }
// For @Here: { communityRoleId: "Here", name: "Here" }

Examples

Find mentioned users

import {
rootServer,
ChannelMessageEvent,
ChannelMessageCreatedEvent,
} from "@rootsdk/server-bot";

function handleMessage(evt: ChannelMessageCreatedEvent): void {
if (!evt.referenceMaps?.users?.length) {
return;
}

for (const user of evt.referenceMaps.users) {
console.log(`Mentioned user: ${user.name} (${user.userId})`);
}
}

rootServer.community.channelMessages.on(
ChannelMessageEvent.ChannelMessageCreated,
handleMessage
);

Find mentioned channels

function handleMessage(evt: ChannelMessageCreatedEvent): void {
if (!evt.referenceMaps?.channels?.length) {
return;
}

for (const channel of evt.referenceMaps.channels) {
console.log(`Mentioned channel: #${channel.name} (${channel.channelId})`);
}
}

Find mentioned roles

function handleMessage(evt: ChannelMessageCreatedEvent): void {
if (!evt.referenceMaps?.roles?.length) {
return;
}

for (const role of evt.referenceMaps.roles) {
console.log(`Mentioned role: @${role.name} (${role.communityRoleId})`);
}
}

Use messageUris to check for content types that aren't in referenceMaps, like external URLs:

function hasExternalLinks(evt: ChannelMessageCreatedEvent): boolean {
return evt.messageUris?.some(
(uri) => uri.uri.startsWith("https://") || uri.uri.startsWith("http://")
) ?? false;
}

Check for @All or @Here

function hasAllMention(evt: ChannelMessageCreatedEvent): boolean {
return evt.messageUris?.some(
(uri) => uri.uri.toLowerCase() === "root://role/all"
) ?? false;
}

function hasHereMention(evt: ChannelMessageCreatedEvent): boolean {
return evt.messageUris?.some(
(uri) => uri.uri.toLowerCase() === "root://role/here"
) ?? false;
}

Check if a specific user is mentioned

function isMentioned(
evt: ChannelMessageCreatedEvent,
targetUserId: UserGuid
): boolean {
if (!evt.referenceMaps?.users?.length) {
return false;
}

return evt.referenceMaps.users.some(
(user) => user.userId === targetUserId
);
}