Groups #

A group or clan brings together a bunch of users into a small community or team.

A group is made up of a superadmin, admins, and members. It can be public or private, which determines whether anyone can join the group. Private groups (open=false) are similar to how WhatsApp groups work, a user can only be added when they’re invited to join by one of the group’s admins.

A group also has a maximum member count. This is set to 100 by default if the group is created by the client, or can be overridden if the group is created by the code runtime.

A group user has four states:

CodePurpose
0SuperadminThere must at least be 1 superadmin in any group. The superadmin has all the privileges of the admin and can additionally delete the group and promote admin members.
1AdminThere can be one of more admins. Admins can update groups as well as accept, kick, promote, demote, ban or add members.
2MemberRegular group member. They cannot accept join requests from new users.
3Join requestA new join request from a new user. This does not count towards the maximum group member count.

List and filter groups #

Groups can be listed using a number of optional filters: name, lang_tag, open and (number of) members. If all filters are omitted, the operation will list all existing groups.

The name filter is case insensitive and mutually exclusive to the remainder filters. It can be useful to help the user look for a specific group by name, and it supports the % wildcard for partial matches as a suffix. As an example, looking for a group that is prefixed with the “Persian” word would be written as persian% name filter.

The remainder filters can be combined or omitted in any way, for instance, we could use the open and members filters to list all open groups with at most the specified amount of members.

Client
1
2
3
4
5
6
7
// Filter for group names which start with "heroes"
const string nameFilter = "heroes%";
var result = await client.ListGroupsAsync(session, nameFilter, 20);
foreach (var g in result.Groups)
{
    System.Console.WriteLine("Group name '{0}' count '{1}'", g.Name, g.EdgeCount);
}

The message response for a list of groups contains a cursor. The cursor can be used to quickly retrieve the next set of results.

Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Filter for group names which start with "heroes"
const string nameFilter = "heroes%";
var result = await client.ListGroupsAsync(session, nameFilter, 20);

// If there are more results get next page.
if (result.Cursor != null)
{
    result = await client.ListGroupsAsync(session, nameFilter, 20, result.Cursor);

    foreach (var g in result.Groups)
    {
        System.Console.WriteLine("Group name '{0}' count '{1}'", g.Name, g.EdgeCount);
    }
}

Join groups #

When a user has found a group to join they can request to become a member. A public group can be joined without any need for permission while a private group requires a superadmin or an admin to accept the user.

When a user joins or leaves a group event messages are added to chat history. This makes it easy for members to see what’s changed in the group.

A user who’s part of a group can join group chat and access it’s message history.

Client
1
2
3
const string groupId = "<group id>";
await client.JoinGroupAsync(session, groupId);
System.Console.WriteLine("Sent group join request '{0}'", groupId);

The user will receive an in-app notification when they’ve been added to the group. In a private group an admin or superadmin will receive a notification when a user has requested to join.

List a user’s groups #

Each user can list groups they’ve joined as a member or an admin or a superadmin. The list also contains groups which they’ve requested to join but not been accepted into yet.

Client
1
2
3
4
5
6
7
8
const string userId = "<user id>";
var result = await client.ListUserGroupsAsync(session, userId);

foreach (var ug in result.UserGroups)
{
    var g = ug.Group;
    System.Console.WriteLine("Group '{0}' role '{1}'", g.Id, ug.State);
}

List group members #

A user can list all members who’re part of their group. These include other users who’ve requested to join the private group but not been accepted into yet.

Client
1
2
3
4
5
6
7
const string groupId = "<group id>";
var result = await client.ListGroupUsersAsync(session, groupId);
foreach (var ug in result.UserGroups)
{
    var g = ug.Group;
    System.Console.WriteLine("group '{0}' role '{1}'", g.Id, ug.State);
}

Create a group #

A group can be created with a name and other optional fields. These optional fields are used when a user lists and filter groups. The user who creates the group becomes the owner and a superadmin for it.

Client
1
2
3
4
const string name = "pizza-lovers";
const string desc = "pizza lovers, pineapple haters";
var group = await client.CreateGroupAsync(session, name, desc);
System.Console.WriteLine("New group: {0}", group);

You can also create a group with server-side code. This can be useful when the group must be created together with some other record or feature.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
let userId = 'dcb891ea-a311-4681-9213-6741351c9994';
let creatorId = 'dcb891ea-a311-4681-9213-6741351c9994';
let name = 'Some unique group name';
let description = 'My awesome group.';
let lang = 'en';
let open = true;
let avatarURL = 'url://somelink';
let metadata = { custom_field: 'some_value' };
let maxCount = 100;

let group = {} as nkruntime.Group;

try {
    group = nk.groupCreate(userId, name, creatorId, lang, description, avatarURL, open, metadata, maxCount);
} catch (error) {
    // Handle error
}

Group metadata #

You can store additional fields for a group in group.metadata. This is useful to share data you want to be publicly available to users, and providing additional details usable for listing and filtering groups.

Metadata can also be used to create new functionality on top of existing groups features.

Metadata is limited to 16KB per group and can only be set via the script runtime.

The following example shows how you might use group metadata to extend Nakama’s group member permissions with added roles (as described in the best practices guide). Specifically, we will introduce the concept of a bouncer role which a group member must have in order to kick another member from the group.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Assuming a group metadata structure as follows
const metadata = {
  roles: {
    '000d8152-3258-457b-905b-05a9223c5c8c': ['bouncer'],
    '2c0c8e80-fcbc-4b61-901a-dace129f45f5': ['bouncer', 'vip'],
  }
}

// Add a before hook to only allow bouncers to kick group users
let BeforeKickGroupUser: nkruntime.BeforeHookFunction<KickGroupUsersRequest> = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, data: nkruntime.KickGroupUsersRequest): nkruntime.KickGroupUsersRequest | void {
  const groups = nk.groupsGetId([data.groupId]);
  if (groups.length == 0) {
    logger.warn('Invalid group Id');
    return null;
  }
  
  // Only continue with the Kick request if the actioning user has the bouncer role
  const roles = groups[0].metadata.roles;
  if (roles && roles[ctx.userId] && roles[ctx.userId].includes('bouncer')) {
    return data;
  }

  logger.warn("you must be a bouncer to kick group members")
  return null;
};

// Register inside InitModule
initializer.registerBeforeKickGroupUsers(BeforeKickGroupUser);

Update a group #

When a group has been created it’s admins can update optional fields.

Client
1
2
3
4
const string groupId = "<group id>";
const string desc = "Better than Marvel Heroes!";
var group = await client.UpdateGroupAsync(session, groupId, null, desc);
System.Console.WriteLine("Updated group: {0}", group);

Updating group size #

Updating a group’s size is an action that can only be performed authoritatively via the server.

Server
1
2
let newMaxSize = 50
nk.groupUpdate("<GroupId>", "", null, null, null, null, null, null, null, newMaxSize);

Leave a group #

A user can leave a group and will no longer be able to join group chat or read message history. If the user is a superadmin they will only be able to leave when at least one other superadmin exists in the group.

Any user who leaves the group will generate an event message in group chat which other members can read.

Client
1
2
const string groupId = "<group id>";
await client.LeaveGroupAsync(session, groupId);

Manage groups #

Each group is managed by one or more superadmins or admins. These users are members with permission to make changes to optional fields, accept or reject new members, remove members or other admins, and promote other members as admins.

A group must have at least one superadmin. The last superadmin has to promote another member before they can leave.

Accept new members #

When a user joins a private group it will create a join request until an admin accepts or rejects the user. The superadmin or admin can accept the user into the group.

Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.AddGroupUsersAsync(session, groupId, userIds);

The user will receive an in-app notification when they’ve been added to the group. In a private group an admin will receive a notification about the join request.

To reject the user from joining the group you should kick them.

Promote a member #

An admin can promote another member of the group as an admin. This grants the member the same privileges to manage the group. A group can have one or more admins.

Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.PromoteGroupUsersAsync(session, groupId, userIds);

Demote a member #

An admin can demote another member of the group down a role. This revokes the member of his current privileges to and assigns member the privileges available in the demoted role. Members who are already at the lowest role in their group will not be affected by a demotion.

Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.DemoteGroupUsersAsync(session, groupId, userIds);

Kick a member #

An admin or superadmin can kick a member from the group. The user is removed but can re-join again later unless the user is banned or the group is private in which case an admin must accept the re-join request. This can also be used to remove pending requests for groups that require acceptance.

If a user is removed from a group it does not prevent them from joining other groups. Sometimes a bad user needs to be kicked from the group and banned from re-joining either the group or the whole server. This will prevent the user from being able to connect to the server and interact at all.

Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.KickGroupUsersAsync(session, groupId, userIds);

Ban a group member #

An admin or superadmin can ban a member from the group. The user is kicked from the group and prevented from re-joining or even requesting to re-join.

The user can be unbanned either via the Nakama Console or a runtime code function.

Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.BanGroupUsersAsync(session, groupId, userIds);

Remove a group #

A group can only be removed by one of the superadmins which will disband all members. When a group is removed it’s name can be re-used to create a new group.

Client
1
2
const string groupId = "<group id>";
await client.DeleteGroupAsync(session, groupId);

Related Pages