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:
| Property | Type | Description |
|---|---|---|
messageContent | string | Raw message text with root:// URIs in Markdown link format |
messageUris | MessageUri[] | Extracted URIs from the message (what was referenced) |
referenceMaps | MessageReferenceMaps | Current 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-app";
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})`);
}
}
Check for external links
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
);
}