Skip to main content

Community access

The community access section of the Root API is a set of methods and events that let your server code interact with the community in which you're running.

What does community access enable?

The community API lets you build a wide range of experiences. Here are a few common categories with some ideas.

Member management

Track member activity and automatically promote users when they hit milestones. Set up a smooth onboarding flow with a checklist, starter role, and welcome message. Add an XP system that rewards participation and unlocks new roles or badges as members level up.

Events and coordination

Post reminders for upcoming events and tag the right roles automatically. Let members RSVP with reactions or commands, and log attendance for future rewards. Keep engagement high by posting daily challenges or prompts.

Messaging and content

Create temporary channels on a schedule and clean them up when they’re no longer needed. Pin popular messages based on reactions or keywords, and unpin older ones. Enable quick polls in chat and use emoji reactions to track results.

Moderation and safety

Auto-delete rule-breaking messages and issue warnings or kicks based on filters. Track repeat offenses and auto-ban users after a set number of strikes. Flag suspicious join activity to catch bot raids or alt floods.

Examples

Here are two examples that show the core usage patterns of the community-access API: subscribe to community events and update community state.

Example: echo every message

Let's start with an unrealistic, but simple, example: echo back every message posted in the community. Here's the full code to achieve this; the key lines are highlighted.

Echo every community message
import {
rootServer,
ChannelMessageEvent,
ChannelMessageCreatedEvent,
ChannelMessageCreateRequest,
} from "@rootsdk/server-app";

// At startup, subscribe to be notified when members post messages in any community channel
(async () => {
rootServer.community.channelMessages.on(ChannelMessageEvent.ChannelMessageCreated, onMessage);
await rootServer.lifecycle.start(initialize);
})();

async function onMessage(evt: ChannelMessageCreatedEvent): Promise<void> {
// Retrieve the incoming message from the event object
const reply: string = evt.messageContent;

// Send that same message back to the channel where it was posted
const request: ChannelMessageCreateRequest = { channelId: evt.channelId, content: reply };
await rootServer.community.channelMessages.create(request);
}

Example: new member joined message

Suppose you're a community admin that wants to see who's recently joined the community. You could use the community-access API to listen for that event and write a message into your community's system-message channel (every community can have one of these channels, it's settable in the community options). Here's the full code to achieve this; the key lines are highlighted.

Add a message to the community's system channel when a new member joins
import {
rootServer,
Community,
CommunityMember,
CommunityEvent,
CommunityJoinedEvent,
CommunityMemberGetRequest,
ChannelMessageCreateRequest,
} from "@rootsdk/server-app";

// At startup, subscribe to be notified when new members join the community
(async () => {
rootServer.community.communities.on(CommunityEvent.CommunityJoined, onJoined);
await rootServer.lifecycle.start();
})();

async function onJoined(evt: CommunityJoinedEvent): Promise<void> {
// Retrieve information about the current community
const community: Community = await rootServer.community.communities.get();

// Verify the community has set a system-message channel (called default channel in code)
if (!community.defaultChannelId)
return;

// Get the nickname of the new member
const memberRequest: CommunityMemberGetRequest = { userId: evt.userId };
const member: CommunityMember = await rootServer.community.communityMembers.get(memberRequest);
const nickname: string = member.nickname;

// Write a message to the community's system-message channel
const messageRequest: ChannelMessageCreateRequest = { channelId: community.defaultChannelId, content: nickname + " joined"};
await rootServer.community.channelMessages.create(messageRequest);
}

Which operations are available?

Think of your server like a community member. Your code can do almost everything a human can in the community.

What your code can do

  • Create channels and channel groups
  • Post messages in channels
  • Upload files to channels
  • Pin messages
  • Kick or ban members
  • Create new roles
  • Assign roles to members
  • And more...

Events your code can watch for

  • Messages are created, edited, deleted, or pinned
  • Members join or leave the community
  • Roles are created, updated, or deleted
  • The community name or picture changes
  • And more...

What your code can’t do

  • Cannot send notifications
  • Cannot respond to community join invites
  • Cannot interact with Friends lists

API structure

The community-access part of the Root API is encapsulated within rootServer:

import
{
rootServer
}
from "@rootsdk/server-app";

rootServer has a community property that lets you access different aspects of the community. Here's a snippet of the RootServer source code that shows all the community options.

export type RootServer =
{
community:
{
accessRules : AccessRuleClient,
channels : ChannelClient,
channelGroups : ChannelGroupClient,
channelMessages : ChannelMessageClient,
communities : CommunityClient,
communityMemberBans : CommunityMemberBanClient,
communityMemberInvites: CommunityMemberInviteClient,
communityRoles : CommunityRoleClient,
communityMembers : CommunityMemberClient,
communityMemberRoles : CommunityMemberRoleClient,
channelDirectories : ChannelDirectoryClient,
channelFiles : ChannelFileClient,
channelWebRtcs : ChannelWebRtcClient,
};
// ...
}

Each of the client types inside community gives you two things: methods and events.

API methods

The community-access methods let you manipulate your community. Generally, they follow the Create/Read/Update/Delete (CRUD) pattern, although there are some exceptions. Here's a snippet of the source code for the ChannelMessageClient type.

export type ChannelMessageClient = TypedEventEmitter<ChannelMessageEvents> &
{
create(request: ChannelMessageCreateRequest): Promise<ChannelMessage>;
delete(request: ChannelMessageDeleteRequest): Promise<void>;
edit (request: ChannelMessageEditRequest ): Promise<ChannelMessage>;
get (request: ChannelMessageGetRequest ): Promise<ChannelMessage>;
list (request: ChannelMessageListRequest ): Promise<ChannelMessageListResponse>;

pinCreate(request: ChannelMessagePinCreateRequest): Promise<void>;
pinDelete(request: ChannelMessagePinDeleteRequest): Promise<void>;
pinList (request: ChannelMessagePinListRequest ): Promise<ChannelMessagePinListResponse>;

reactionCreate(request: ChannelMessageReactionCreateRequest): Promise<ChannelMessageReaction>;
reactionDelete(request: ChannelMessageReactionDeleteRequest): Promise<void>;

setTypingIndicator(request: ChannelMessageSetTypingIndicatorRequest): Promise<void>;

setViewTime(request: ChannelMessageSetViewTimeRequest): Promise<void>;
};

API events

Each client type is also an event emitter that publishes events to notify you when things change in your community. Here's part of the ChannelMessageClient source code. Notice the TypedEventEmitter<ChannelMessageEvents> part.

export type ChannelMessageClient = TypedEventEmitter<ChannelMessageEvents> & {
...
}

The ChannelMessageEvents type tells you all the events this client publishes:

export type ChannelMessageEvents =
{
'channelMessageReaction.created' : (evt: ChannelMessageReactionCreatedEvent ) => void,
'channelMessageReaction.deleted' : (evt: ChannelMessageReactionDeletedEvent ) => void,
'channelMessagePin.created' : (evt: ChannelMessagePinCreatedEvent ) => void,
'channelMessagePin.deleted' : (evt: ChannelMessagePinDeletedEvent ) => void,
'channelMessage.created' : (evt: ChannelMessageCreatedEvent ) => void,
'channelMessage.edited' : (evt: ChannelMessageEditedEvent ) => void,
'channelMessage.deleted' : (evt: ChannelMessageDeletedEvent ) => void,
'channelMessage.set.typingIndicator': (evt: ChannelMessageSetTypingIndicatorEvent) => void
}

For convenience, each Events type has a corresponding enum that provides symbolic constants for those event strings. For example, the ChannelMessageEvents type (note the 's' on the end) has a corresponding enum type named ChannelMessageEvent (there's no 's' on the end) that looks like this:

export enum ChannelMessageEvent
{
ChannelMessageReactionCreated = 'channelMessageReaction.created',
ChannelMessageReactionDeleted = 'channelMessageReaction.deleted',
ChannelMessagePinCreated = 'channelMessagePin.created',
ChannelMessagePinDeleted = 'channelMessagePin.deleted',
ChannelMessageCreated = 'channelMessage.created',
ChannelMessageEdited = 'channelMessage.edited',
ChannelMessageDeleted = 'channelMessage.deleted',
ChannelMessageSetTypingIndicator = 'channelMessage.set.typingIndicator'
}

You should use the enum value when subscribing to an event rather than the raw string.

//
// Yes: use the enum
//
rootServer.community.channelMessages.on(ChannelMessageEvent.ChannelMessageCreated, onCreated);

//
// No: don't use the raw string, it's error prone
//
rootServer.community.channelMessages.on('channelMessage.created', onCreated);

How do you determine success/failure?

Root API calls throw RootApiException on failure. You can look at the errorCode inside the exception object to determine what went wrong. The ErrorCodeType is an enum with values for many common issues.

async function onCreated(evt: ChannelMessageCreatedEvent): Promise<void>
{
// ...
try
{
const cm: ChannelMessage = await rootServer.community.channelMessages.create(createMessageRequest);
}
catch (xcpt: unknown)
{
if (xcpt instanceof RootApiException)
{
if (xcpt.errorCode === ErrorCodeType.NoPermissionToCreate)
{
// ...
}
}
else if (xcpt instanceof Error)
{
// ...
}
else
{
// ...
}
}
}

Your code needs permission

Just like a human member, your code is subject to the community's permissions. For example, to successfully create a new channel using the API, your code will need the Manage Channel permission.