群组 #

群组或家族将一群用户聚集在一起,形成一个小社区或团队。

群组由超级管理员、管理员和成员组成。群组可以是公开的,也可以是私人的,这决定了当用户列出群组时,它是否出现在结果中。私人群组类似于WhatsApp群组的运作方式。只有当群组的一位管理员邀请用户加入时,才能添加用户。

群组也有最大成员数。如果群组是由客户端创建的,则默认设置为100;如果群组是通过代码运行时程序创建的,那么可以超过此值。

群组用户有四种状态:

代码用途
0超级管理员任何群组都必须拥有至少一位超级管理员。超级管理员拥有管理员的所有权限,另外还可以删除群组和升级管理员成员。
1管理员可以有一个或多个管理员。管理员可以更新群组,也可以接受、踢出、升级、降级、封禁或添加成员。
2成员群组常规成员。无法接受新用户的加入请求。
3加入请求新用户发来的新的加入请求。这不会被计入群组成员的最大数量。

列出和过滤群组 #

可以使用许多可选过滤器列出群组:name, lang_tag, open 和(值为) members。如果省略所有过滤器,操作将列出所有现有群组。

过滤器name不区分大小写,与其余过滤器相互排斥。它可以帮助用户按名称查找特定的群组,并支持使用%通配符匹配后缀部分。例如,查找前缀为“Persian”的群组将被写为persian%名称过滤器。

剩余的过滤器可以以任何方式组合或省略,例如,我们可以使用openmembers过滤器最多列出所有拥有指定数量成员的开放群组。

Client
1
2
const groups = await client.listGroups(session, "heroes%", 20); // fetch first 20 groups
console.info("List of groups:", groups);
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);
}
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
auto successCallback = [this](NGroupListPtr list)
{
    for (auto& group : list->groups)
    {
        std::cout << "Group name '" << group.name << "' count " << group.edgeCount << std::endl;
    }
};

// Filter for group names which start with "heroes"
// fetch first 20 groups
client->listGroups(session, "heroes%", 20, "", successCallback);
Client
1
2
3
4
5
6
7
// Filter for group names which start with "heroes"
String nameFilter = "heroes%";
GroupList groups = client.listGroups(session, nameFilter, 20).get();

for (Group group : groups.getGroupsList()) {
    System.out.format("Group name %s count %s", group.getName(), group.getEdgeCount());
}
Client
1
2
3
4
5
6
7
8
9
var list : NakamaAPI.ApiGroupList = yield(client.list_groups_async(session, "heroes*", 20), "completed")

if list.is_exception():
    print("An error occurred: %s" % list)
    return

for g in list.groups:
    var group = g as NakamaAPI.ApiGroup
    print("Group: name %s, id %s", [group.name, group.id])
Client
1
2
curl "http://127.0.0.1:7350/v2/group?limit=20&name=heroes%25" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
3
4
5
GET /v2/group?limit=20&name=heroes%&cursor=<cursor>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
1
2
3
4
5
local result = client.list_groups("heroes*", "<cursor>", 20)

for _,group in ipairs(result.groups) do
  pprint(groups)
end

群组列表的消息响应包含一个游标。游标可用于快速检索下一组结果。

Client
1
2
curl -X GET "http://127.0.0.1:7350/v2/group?limit=20&name=heroes%25&cursor=somecursor" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
const groups = await client.listGroups(session, "heroes%", 20, cursor);
console.info("List of groups:", groups);
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);
    }
}
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void YourClass::processGroupList(NGroupListPtr list)
{
    for (auto& group : list->groups)
    {
        std::cout << "Group name '" << group.name << "' count " << group.edgeCount << std::endl;
    }

    if (!list->cursor.empty())
    {
        // request next page
        requestHeroes(list->cursor);
    }
}

void YourClass::requestHeroes(const string& cursor)
{
    // Filter for group names which start with "heroes"
    // fetch first 20 groups
    client->listGroups(session, "heroes%", 20, cursor, std::bind(&YourClass::processGroupList, this, std::placeholders::_1));
}
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Filter for group names which start with "heroes"
String nameFilter = "heroes%";
GroupList groups = client.listGroups(session, nameFilter, 20).get();

if (groups.getCursor() != null) {
    groups = client.listGroups(session, nameFilter, 20, groups.getCursor()).get();

    for (Group group : groups.getGroupsList()) {
        System.out.format("Group name %s count %s", group.getName(), group.getEdgeCount());
    }
}
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var list : NakamaAPI.ApiGroupList = yield(client.list_groups_async(session, "heroes*", 20), "completed")

if list.is_exception():
    print("An error occurred: %s" % list)
    return

for g in list.groups:
    var group = g as NakamaAPI.ApiGroup
    print("Group: name %s, id %s", [group.name, group.id])

var cursor = list.cursor

while cursor: # While there are more results get next page.
    list = yield(client.list_groups_async(session, "heroes*", 20, cursor), "completed")

    if list.is_exception():
        print("An error occurred: %s" % list)
        return

    for g in list.groups:
        var group = g as NakamaAPI.ApiGroup
        print("Group: name %s, id %s", [group.name, group.id])

    cursor = list.cursor
Client
1
2
3
4
5
GET /v2/group?limit=20&name=heroes%&cursor=somecursor
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
local cursor = nil

repeat
  local result = client.list_groups("heroes*", cursor, 20)

  if result.error then
    print(result.message)
    break
  end

  cursor = result.cursor

  for _,group in ipairs(result.groups) do
    pprint(groups)
  end
until not cursor

加入群组 #

用户找到想要加入的群组时,可以请求成为成员。公开群组不需要任何就可以加入,而私人群组需要超级管理员或管理员接受用户才可加入。

当用户加入或退出群组时,事件消息会被添加到聊天历史记录中。成员可以轻松查看群组内的变动。

群组中的用户可以加入群组聊天并查看群组聊天的历史消息

Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/join" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
3
const group_id = "<group id>";
await client.joinGroup(session, group_id);
console.info("Sent group join request", group_id);
Client
1
2
3
const string groupId = "<group id>";
await client.JoinGroupAsync(session, groupId);
System.Console.WriteLine("Sent group join request '{0}'", groupId);
Client
1
2
3
4
5
6
7
auto successCallback = []()
{
    std::cout << "Sent group join request" << std::endl;
};

string group_id = "<group id>";
client->joinGroup(session, group_id, successCallback);
Client
1
2
3
String groupid = "<group id>";
client.joinGroup(session, groupid).get();
System.out.format("Sent group join request %s", groupid);
Client
1
2
3
4
5
6
7
8
var group_id = "<group id>"
var join : NakamaAsyncResult = yield(client.join_group_async(session, group_id), "completed")

if join.is_exception():
    print("An error occurred: %s" % join)
    return

print("Sent group join request %s" % group_id)
Client
1
2
3
4
5
POST /v2/group/<group id>/join
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>

Client
1
2
3
4
5
6
7
8
9
local group_id = "<group_id>"
local result = client.join_group(group_id)

if result.error then
  print(result.message)
  return
end

print("Sent group join request", group_id)
用户在被添加到群组时将会在应用程序内收到通知。在私人群组中,当用户请求加入时,管理员或超级管理员将收到通知。

列出用户群组 #

每位用户都可以列出自己作为成员或作为管理员或超级管理员加入的群组。另外,还可以列出自己已经发送加入请求但尚未被接受的群组。

Client
1
2
curl -X GET "http://127.0.0.1:7350/v2/user/<user id>/group" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
3
4
5
6
7
8
const userId = "<user id>";
const groups = await client.listUserGroups(session, userid);

groups.user_groups.forEach(function(userGroup){
  console.log("Group: name '%o' id '%o'.", userGroup.group.name, userGroup.group.id);
  // group.State is one of: SuperAdmin, Admin, Member, or Join.
  console.log("Group's state is %o.", userGroup.state);
});
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);
}
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
auto successCallback = [](NUserGroupListPtr list)
{
    for (auto& userGroup : list->userGroups)
    {
        std::cout << "Group name " << userGroup.group.name << std::endl;
    }
};

string userId = "<user id>";
client->listUserGroups(session, userId, {}, {}, {}, successCallback);
Client
1
2
3
4
5
6
String userid = "<user id>";
UserGroupList userGroups = client.listUserGroups(session, userid).get();

for (UserGroupList.UserGroup userGroup : userGroups.getUserGroupsList()) {
    System.out.format("Group name %s role %d", userGroup.getGroup().getName(), userGroup.getState());
}
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var user_id = "<user id>"
var result : NakamaAPI.ApiUserGroupList = yield(client.list_user_groups_async(session, user_id), "completed")

if result.is_exception():
    print("An error occurred: %s" % result)
    return

for ug in result.user_groups:
    var g = ug.group as NakamaAPI.ApiGroup
    print("Group %s role %s", g.id, ug.state)
Client
1
2
3
4
5
GET /v2/user/<user id>/group
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
local user_id = "<user>"
local result = client.list_user_groups(user_id)

if result.error then
  print(result.message)
  return
end

for _,user_group in ipairs(result.user_groups) do
  pprint(user_group)
end

列出群组成员 #

用户可以列出所在群组的所有成员。其中包括已请求加入私人群组但尚未被接受的其他用户。

Client
1
2
curl -X GET "http://127.0.0.1:7350/v2/group/<group id>/user" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
3
const group_id = "<group id>";
const users = await client.listGroupUsers(session, group_id);
console.info("Users in group:", users);
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);
}
Client
1
2
3
4
5
6
7
auto successCallback = [](NGroupUserListPtr list)
{
    std::cout << "Users in group: " << list->groupUsers << std::endl;
};

string group_id = "<group id>";
client->listGroupUsers(session, group_id, {}, {}, {}, successCallback);
Client
1
2
3
4
5
6
String groupid = "<group id>";
GroupUserList groupUsers = client.listGroupUsers(session, groupid).get();

for (GroupUserList.GroupUser groupUser : groupUsers.getGroupUsersList()) {
    System.out.format("Username %s role %d", groupUser.getUser().getUsername(), groupUser.getState());
}
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var group_id = "<group id>"
var member_list : NakamaAPI.ApiGroupUserList = yield(client.list_group_users_async(session, group_id), "completed")

if member_list.is_exception():
    print("An error occurred: %s" % member_list)
    return

for ug in member_list.group_users:
    var u = ug.user as NakamaAPI.ApiUser
    print("User %s role %s" % [u.id, ug.state])
Client
1
2
3
4
5
GET /v2/group/<group id>/user
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
local group_id = "<group_id>"
local result = client.list_group_users(group_id)

if result.error then
  print(result.message)
  return
end

for _,user in ipairs(result.group_users) do
  pprint(user)
end

创建群组 #

可以使用名称和其他可选字段创建群组。这些可选字段在用户列出和过滤群组时使用。创建群组的用户是群组的所有者和超级管理员。

Client
1
2
3
4
5
6
7
8
curl -X POST "http://127.0.0.1:7350/v2/group" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "name": "pizza-lovers",
    "description": "pizza lovers, pineapple haters",
    "lang_tag": "en_US",
    "open": true
  }'
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const group_name = "pizza-lovers";
const description = "pizza lovers, pineapple haters";

const group = await client.createGroup(session, {
  name: group_name,
  description: description,
  lang_tag: "en_US",
  open: true
});

console.info("New group:", group);
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);
Client
1
2
3
4
5
6
7
8
auto successCallback = [](const NGroup& group)
{
    std::cout << "New group ID: " << group.id << std::endl;
};

string group_name = "pizza-lovers";
string description = "pizza lovers, pineapple haters";
client->createGroup(session, group_name, description, "", "en_US", true, {}, successCallback);
Client
1
2
3
4
String name = "pizza-lovers";
String desc = "pizza lovers, pineapple haters";
Group group = client.createGroup(session, name, desc).get();
System.out.format("New group %s", group.getId());
Client
1
2
3
4
5
6
7
8
9
var group_name = "pizza-lovers"
var group_desc = "pizza lovers, pineapple haters"
var group : NakamaAPI.ApiGroup = yield(client.create_group_async(session, group_name, group_desc), "completed")

if group.is_exception():
    print("An error occurred: %s" % group)
    return

print("New group: %s" % group)
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /v2/group
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "name": "pizza-lovers",
  "description": "pizza lovers, pineapple haters",
  "lang_tag": "en_US",
  "open": true
}
Client
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
local avatar_url = ""
local description = "pizza lovers, pineapple haters"
local lang_tag = "en-gb"
local max_count = 10
local name = "pizza-lovers"
local open = true

local result = client.create_group(avatar_url, description, lang_tag, max_count, name, open)

if result.error then
  print(result.message)
  return
end

您还可以使用服务器端代码创建群组。创建群组时,若必须有其他一些记录或特征,这可能很有用。

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
local nk = require("nakama")

local metadata = { -- Add whatever custom fields you want.
  my_custom_field = "some value"
}

local user_id = "<user id>"
local name = "pizza-lovers"
local creator_id = user_id
local lang = "en_US"
local description = "pizza lovers, pineapple haters"
local avatar_url = "url://somelink"
local open = true
local maxMemberCount = 100

local success, err = pcall(nk.group_create, user_id, name, creator_id, lang, description, avatar_url, open, metadata, maxMemberCount)

if (not success) then
  nk.logger_error(("Could not create group: %q"):format(err))
end
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
metadata := map[string]interface{}{
    "my_custom_field": "some value", // Add whatever custom fields you want.
}

userID := "<user id>"
creatorID := userID
name := "pizza-lovers"
description := "pizza lovers, pineapple haters"
langTag := "en"
open := true
avatarURL := "url://somelink"
maxCount := 100

if _, err := nk.GroupCreate(ctx, userID, name, creatorID, langTag, description, avatarURL, open, metadata, maxCount); err != nil {
    logger.Error("Could not create group: %s", err.Error())
}
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中存储群组的其他字段。这有助于共享您希望公开给用户的数据,并提供可用于列出和过滤群组的其他详细信息。

元数据还可用于在现有群组功能的基础上创建新功能

每个群组的元数据限制在16KB,并且只能通过脚本运行进行设置。

以下示例显示了您如何使用群组元数据来扩展Nakama中群组成员的权限和增加角色(如最佳实践指南中所述)。具体来说,我们将引入保镖角色的概念,群组成员必须拥有该角色才能踢出群组中的另一名成员。

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);
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Assuming a group metadata structure as follows
type GroupMetadata struct {
	Roles map[string][]string `json:"roles"`
}

metadata := &GroupMetadata {
  Roles: map[string][]string{
    "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
if err := initializer.RegisterBeforeKickGroupUsers(func (ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, in *api.KickGroupUsersRequest) (*api.KickGroupUsersRequest, error) {
  userId, ok := ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)
  if !ok {
    logger.Error("invalid user")
    return nil, runtime.NewError("invalid user", 13)
  }

  groups, err := nk.GroupsGetId(ctx, []string { in.GroupId })
  if err != nil || len(groups) == 0 {
    logger.Error("group not found")
    return nil, runtime.NewError("group not found", 5)
  }

  // Only continue with the Kick request if the actioning user has the bouncer role
  var metadata GroupMetadata
  if err := json.Unmarshal([]byte(groups[0].GetMetadata()), &metadata); err != nil {
    logger.Error("error deserializing metadata")
    return nil, runtime.NewError("error deserializing metadata", 13)
  }

  for _, role := range metadata.Roles[userId] {
    if role == "bouncer" {
      return in, nil
    }
  }

  return nil, runtime.NewError("you must be a bouncer to kick group members", 7)
}); err != nil {
  logger.Error("unable to register before kick group users hook: %v", err)
  return err
}
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
29
30
31
32
33
-- Assuming a group metadata structure as follows
local 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
local function before_kick_group_users(context, payload)
    local groups = nk.groups_get_id({ payload.group_id })
    if #groups == 0 then
        nk.logger_error("group not found")
        return nil
    end

    local roles = groups[1].metadata["roles"]
    if roles == nil or roles[context.user_id] == nil then
        nk.logger_error("no roles configured for user")
        return nil
    end

    for _, role in ipairs(roles[context.user_id]) do
        if role == "bouncer" then
            return payload
        end
    end

    nk.logger_error("you must be a bouncer to kick group members")
    return nil
end

nk.register_req_before(before_kick_group_users, "KickGroupUsers")

更新群组 #

创建群组后,群组管理员可以更新可选字段。

Client
1
2
3
4
5
curl -X PUT "http://127.0.0.1:7350/v2/group/<group id>" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "description": "Better than Marvel Heroes!",
  }'
Client
1
2
3
4
const group_id = "<group id>";
const description = "Better than Marvel Heroes!";
const group = await client.updateGroup(session, group_id, { description: description });
console.info("Updated group:", group);
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);
Client
1
2
3
4
5
6
7
8
auto successCallback = []()
{
    std::cout << "Updated group" << std::endl;
};

string group_id = "<group id>";
string description = "Better than Marvel Heroes!";
client->updateGroup(session, group_id, opt::nullopt, description, opt::nullopt, opt::nullopt, opt::nullopt, successCallback);
Client
1
2
3
4
String groupid = "<group id>";
String desc = "Better than Marvel Heroes!";
client.updateGroup(session, groupid, null, desc).get();
System.out.format("Updated group %s", groupid);
Client
1
2
3
4
5
6
7
8
9
var group_id = "<group id>"
var description = "Better than Marvel Heroes!"
var update : NakamaAsyncResult = yield(client.update_group_async(session, group_id, null, description), "completed")

if update.is_exception():
    print("An error occurred: %s" % update)
    return

print("Updated group")
Client
1
2
3
4
5
6
7
8
PUT /v2/group/<group id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "description": "I was only kidding. Basil sauce ftw!",
}
Client
1
2
3
4
5
6
7
8
local group_id = "<group id>"
local avatar_url = "http://example.com/example.jpg"

local result = client.update_group(group_id, avatar_url)

if result.error then
  print(result.message)
end

更新群组规模 #

更新群组规模操作只能通过服务器授权执行。

Server
1
2
3
4
5
6
7
8
local nk = require("nakama")

local new_max_size = 50
local success, err = pcall(nk.group_update("<GroupId>", nil, "", "", "", "", "", nil, nil, new_max_size))

if (not success) then
  nk.logger_error(("Could not update group: %q"):format(err))
end
Server
1
2
3
4
5
6
newMaxSize := 50
open := true

if err := nk.GroupUpdate(ctx, "<GroupId>", "", "", "", "", "", open, nil, newMaxSize); err != nil {
    return err
}
Server
1
2
let newMaxSize = 50
nk.groupUpdate("<GroupId>", "", null, null, null, null, null, null, null, newMaxSize);

退出群组 #

用户可以退出群组,退出后无法再加入群组聊天或读取消息历史记录。如果用户为超级管理员,只有当群组中除此位用户外至少有一位超级管理员时才可以退出群组。

任何用户退出群组时都会在群组聊天中生成事件消息,其他成员可以读取该消息。

Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/leave" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
const group_id = "<group id>";
await client.leaveGroup(session, group_id);
Client
1
2
const string groupId = "<group id>";
await client.LeaveGroupAsync(session, groupId);
Client
1
2
string groupId = "<group id>";
client->leaveGroup(session, groupId);
Client
1
2
String groupId = "<group id>";
client.leaveGroup(session, groupId).get();
Client
1
2
3
4
5
6
7
8
var group_id = "<group id>"
var leave : NakamaAsyncResult = yield(client.leave_group_async(session, group_id), "completed")

if leave.is_exception():
    print("An error occurred: %s" % leave)
    return

print("Group left")
Client
1
2
3
4
5
POST /v2/group/<group id>/leave
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
1
2
3
4
5
6
local group_id = "<group id>"
local result = client.leave_group(group_id)

if result.error then
  print(result.message)
end

管理群组 #

每个群组由一位或多位超级管理员或管理员管理。这些用户是具有更改可选字段、接受或拒绝新成员、移除成员或其他管理员、将其他成员升级为管理员权限的成员。

每个群组必须拥有至少一位超级管理员。群组的最后一位超级管理员必须要将另一位成员升级为超级管理员才可以退出群组。

接受新成员 #

用户加入私人群组时会创建加入请求,等待管理员接受或拒绝用户。超级管理员或管理员可以接受用户加入群组。

Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/add?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
Client
1
2
3
const group_id = "<group id>";
const user_id = "<user id>";
await client.addGroupUsers(session, group_id, [user_id]);
Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.AddGroupUsersAsync(session, groupId, userIds);
Client
1
2
3
4
5
6
7
8
auto successCallback = []()
{
    std::cout << "added user to group" << std::endl;
};

string group_id = "<group id>";
string user_id = "<user id>";
client->addGroupUsers(session, group_id, { user_id }, successCallback);
Client
1
2
3
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.addGroupUsers(session, groupid, userIds).get();
Client
1
2
3
4
5
6
7
8
9
var group_id = "<group id>"
var user_ids = ["<user id>"]
var accept : NakamaAsyncResult = yield(client.add_group_users_async(session, group_id, user_ids), "completed")

if accept.is_exception():
    print("An error occurred: %s" % accept)
    return

print("User added")
Client
1
2
3
4
5
POST /v2/group/<group id>/add?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
1
2
3
4
5
6
7
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.add_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end

用户在被添加到群组时将会在应用程序内收到通知。在私人群组中,管理员将收到加入请求通知。

要拒绝用户加入群组,您应该将他们踢出

升级成员 #

一位管理员可以将群组的另一位成员升级为管理员。这将同样赋予此成员管理群组的权限。一个群组可以有一个或多个管理员。

Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/promote?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
Client
1
2
3
const group_id = "<group id>";
const user_id = "<user id>";
await client.promoteGroupUsers(session, group_id, [user_id]);
Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.PromoteGroupUsersAsync(session, groupId, userIds);
Client
1
2
3
4
5
6
7
8
auto successCallback = []()
{
    std::cout << "user has been promoted" << std::endl;
};

string group_id = "<group id>";
string user_id = "<user id>";
client->promoteGroupUsers(session, group_id, { user_id }, successCallback);
Client
1
2
3
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.promoteGroupUsers(session, groupid, userIds).get();
Client
1
2
3
4
5
6
7
8
9
var group_id = "<group id>"
var user_ids = ["<user id>"]
var promote : NakamaAsyncResult = yield(client.promote_group_users_async(session, group_id, user_ids), "completed")

if promote.is_exception():
    print("An error occurred: %s" % promote)
    return

print("User promoted")
Client
1
2
3
4
5
POST /v2/group/<group id>/promote?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
1
2
3
4
5
6
7
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.promote_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end

降级成员 #

一位管理员可以将群组的另一位成员降一个等级。这将撤消成员的当前权限,并为成员分配降级后可用的权限。降级不会影响群组中已处于最低等级的成员。

Code snippet for this language C++/Unreal/Cocos2d-x has not been found. Please choose another language to show equivalent examples.
Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/demote?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
Client
1
2
3
const group_id = "<group id>";
const user_id = "<user id>";
await client.demoteGroupUsers(session, group_id, [user_id]);
Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.DemoteGroupUsersAsync(session, groupId, userIds);
Client
1
2
3
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.demoteGroupUsers(session, groupid, userIds).get();
Client
1
2
3
4
5
POST /v2/group/<group id>/demote?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
1
2
3
4
5
6
7
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.demote_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end

踢出成员 #

管理员或超级管理员可以将成员踢出群组。被移除的用户可以重新加入,除非用户被封禁,或者群组是私人群组,必须由管理员接受重新加入请求。

用户被从一个群组中移除不会对用户加入其它群组造成影响。有时,需要将恶意用户踢出群组,并被禁止重新加入群组整个服务器。这将使用户无法连接到服务器进行任何互动。

Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/kick?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
Client
1
2
3
const group_id = "<group id>";
const user_id = "<user id>";
await client.kickGroupUsers(session, group_id, [user_id]);
Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.KickGroupUsersAsync(session, groupId, userIds);
Client
1
2
3
4
5
6
7
8
auto successCallback = []()
{
    std::cout << "user has been kicked" << std::endl;
};

string group_id = "<group id>";
string user_id = "<user id>";
client->kickGroupUsers(session, group_id, { user_id }, successCallback);
Client
1
2
3
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.kickGroupUsers(session, groupid, userIds).get();
Client
1
2
3
4
5
6
7
8
9
var group_id = "<group id>"
var user_ids = ["<user id>"]
var kick : NakamaAsyncResult = yield(client.kick_group_users_async(session, group_id, user_ids), "completed")

if kick.is_exception():
    print("An error occurred: %s" % kick)
    return

print("User kicked")
Client
1
2
3
4
5
POST /v2/group/<group id>/kick?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
1
2
3
4
5
6
7
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.kick_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end

封禁群组成员 #

管理员或超级管理员可以封禁成员加入群组。用户被踢出群组后,无法重新加入,甚至无法请求重新加入。

可以通过Nakama控制台或运行时代码功能解除对用户的封禁。

Client
1
2
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/ban?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
Client
1
2
3
const group_id = "<group id>";
const user_id = "<user id>";
await client.banGroupUsers(session, group_id, [user_id]);
Client
1
2
3
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.BanGroupUsersAsync(session, groupId, userIds);
Client
1
2
3
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.banGroupUsers(session, groupid, userIds).get();
Client
1
2
3
4
5
POST /v2/group/<group id>/ban?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
1
2
3
4
5
6
7
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.ban_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end

移除群组 #

群组只能由超级管理员移除,移除群组将解散所有成员。群组被移除后,可以重新使用这个群组的名称创建新的群组

Client
1
2
curl -X DELETE "http://127.0.0.1:7350/v2/group/<group id>" \
  -H 'Authorization: Bearer <session token>'
Client
1
2
const group_id = "<group id>";
await client.deleteGroup(session, group_id);
Client
1
2
const string groupId = "<group id>";
await client.DeleteGroupAsync(session, groupId);
Client
1
2
3
4
5
6
7
auto successCallback = []()
{
    std::cout << "group deleted" << std::endl;
};

string group_id = "<group id>";
client->deleteGroup(session, group_id, successCallback);
Client
1
2
String groupid = "<group id>";
client.deleteGroup(session, groupid).get();
Client
1
2
3
4
5
6
7
8
var group_id = "<group id>"
var remove : NakamaAsyncResult = yield(client.delete_group_async(session, group_id), "completed")

if remove.is_exception():
    print("An error occurred: %s" % remove)
    return

print("Group removed")
Client
1
2
3
4
5
DELETE /v2/group/<group id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
Client
1
2
3
4
5
6
local group_id = "<group id>"
local result = client.delete_group(group_id)

if result.error then
  print(result.message)
end

Related Pages