그룹은 총괄 관리자, 관리자 및 구성원으로 이뤄집니다. 그룹은 사용자가 그룹을 나열할 때 결과에 표시할지 여부의 결정에 따라 공개 또는 비공개일 수 있습니다. 비공개 그룹은 WhatsApp 그룹이 작동하는 방식과 비슷합니다. 그룹 관리자 중 한 명이 가입 초대를 한 사용자만 추가할 수 있습니다.
그룹에는 최대 구성원 수도 설정되어 있습니다. 그룹을 클라이언트에서 생성한 경우 기본적으로 100으로 설정되며 그룹을 코드 런타임에서 생성한 경우에는 재정의할 수 있습니다.
그룹 사용자의 상태는 네 가지입니다.
코드
목적
0
관리자
모든 그룹에는 최소 1명의 관리자가 있어야 합니다. 총괄 관리자는 모든 관리자 권한이 부여되며, 그룹을 삭제하고 관리자 구성원을 승격시킬 수 있습니다.
1
관리자
관리자는 2명 이상일 수 있습니다. 관리자는 그룹을 업데이트하거나 구성원을 수락, 추방, 승격, 강등, 금지, 추가할 수 있습니다.
2
구성원
일반 그룹 구성원 새로운 사용자의 가입 요청을 수락할 수 없습니다.
3
가입 요청
새로운 사용자로부터 가입 요청이 있습니다. 이것은 그룹의 최대 구성원 숫자에 포함되지 않습니다.
name, lang_tag, open 및 (다수의) members과(와) 같은 여러 선택적 필터를 사용하여 나열할 수 있습니다. 어떤 필터도 사용하지 않으면 이 작업은 기존의 모든 그룹을 나열합니다.
name 필터는 대소문자를 구분하지 않으며 나머지 필터와 상호 배타적입니다. 이를 통해 사용자는 이름으로 특정 그룹을 찾을 수 있으며 부분 일치를 위한 % 와일드카드를 접미사로 지원합니다. 예를 들어, “Persian” 단어가 접두사로 붙은 그룹을 찾는 것은 persian% 이름 필터로 작성할 수 있습니다.
나머지 필터는 어떤 방식으로든 결합하거나 생략할 수 있습니다. 예를 들어, open 및 members 필터를 사용하여 지정된 최대 수의 구성원이 있는 모든 공개 그룹을 나열할 수 있습니다.
Client
1
2
constgroups=awaitclient.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"conststringnameFilter="heroes%";varresult=awaitclient.ListGroupsAsync(session,nameFilter,20);foreach(varginresult.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
autosuccessCallback=[this](NGroupListPtrlist){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"StringnameFilter="heroes%";GroupListgroups=client.listGroups(session,nameFilter,20).get();for(Groupgroup:groups.getGroupsList()){System.out.format("Group name %s count %s",group.getName(),group.getEdgeCount());}
Client
1
2
3
4
5
6
7
8
9
varlist:NakamaAPI.ApiGroupList=yield(client.list_groups_async(session,"heroes*",20),"completed")iflist.is_exception():print("An error occurred: %s"%list)returnforginlist.groups:vargroup=gasNakamaAPI.ApiGroupprint("Group: name %s, id %s",[group.name,group.id])
그룹 목록에 대한 메시지 응답에 커서가 있습니다. 커서를 사용하여 다음 결과 세트를 빠르게 검색할 수 있습니다.
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
constgroups=awaitclient.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"conststringnameFilter="heroes%";varresult=awaitclient.ListGroupsAsync(session,nameFilter,20);// If there are more results get next page.if(result.Cursor!=null){result=awaitclient.ListGroupsAsync(session,nameFilter,20,result.Cursor);foreach(varginresult.Groups){System.Console.WriteLine("Group name '{0}' count '{1}'",g.Name,g.EdgeCount);}}
voidYourClass::processGroupList(NGroupListPtrlist){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);}}voidYourClass::requestHeroes(conststring&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"StringnameFilter="heroes%";GroupListgroups=client.listGroups(session,nameFilter,20).get();if(groups.getCursor()!=null){groups=client.listGroups(session,nameFilter,20,groups.getCursor()).get();for(Groupgroup:groups.getGroupsList()){System.out.format("Group name %s count %s",group.getName(),group.getEdgeCount());}}
varlist:NakamaAPI.ApiGroupList=yield(client.list_groups_async(session,"heroes*",20),"completed")iflist.is_exception():print("An error occurred: %s"%list)returnforginlist.groups:vargroup=gasNakamaAPI.ApiGroupprint("Group: name %s, id %s",[group.name,group.id])varcursor=list.cursorwhilecursor:# While there are more results get next page.list=yield(client.list_groups_async(session,"heroes*",20,cursor),"completed")iflist.is_exception():print("An error occurred: %s"%list)returnforginlist.groups:vargroup=gasNakamaAPI.ApiGroupprint("Group: name %s, id %s",[group.name,group.id])cursor=list.cursor
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/join"\
-H 'Authorization: Bearer <session token>'
Client
1
2
3
constgroup_id="<group id>";awaitclient.joinGroup(session,group_id);console.info("Sent group join request",group_id);
Client
1
2
3
conststringgroupId="<group id>";awaitclient.JoinGroupAsync(session,groupId);System.Console.WriteLine("Sent group join request '{0}'",groupId);
Client
1
2
3
4
5
6
7
autosuccessCallback=[](){std::cout<<"Sent group join request"<<std::endl;};stringgroup_id="<group id>";client->joinGroup(session,group_id,successCallback);
Client
1
2
3
Stringgroupid="<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
vargroup_id="<group id>"varjoin:NakamaAsyncResult=yield(client.join_group_async(session,group_id),"completed")ifjoin.is_exception():print("An error occurred: %s"%join)returnprint("Sent group join request %s"%group_id)
localgroup_id="<group_id>"localresult=client.join_group(group_id)ifresult.errorthenprint(result.message)returnendprint("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
constuserId="<user id>";constgroups=awaitclient.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
conststringuserId="<user id>";varresult=awaitclient.ListUserGroupsAsync(session,userId);foreach(varuginresult.UserGroups){varg=ug.Group;System.Console.WriteLine("Group '{0}' role '{1}'",g.Id,ug.State);}
Client
1
2
3
4
5
6
7
8
9
10
autosuccessCallback=[](NUserGroupListPtrlist){for(auto&userGroup:list->userGroups){std::cout<<"Group name "<<userGroup.group.name<<std::endl;}};stringuserId="<user id>";client->listUserGroups(session,userId,{},{},{},successCallback);
Client
1
2
3
4
5
6
Stringuserid="<user id>";UserGroupListuserGroups=client.listUserGroups(session,userid).get();for(UserGroupList.UserGroupuserGroup: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
varuser_id="<user id>"varresult:NakamaAPI.ApiUserGroupList=yield(client.list_user_groups_async(session,user_id),"completed")ifresult.is_exception():print("An error occurred: %s"%result)returnforuginresult.user_groups:varg=ug.groupasNakamaAPI.ApiGroupprint("Group %s role %s",g.id,ug.state)
사용자는 그룹에 속한 모든 구성원을 나열할 수 있습니다. 여기에는 비공개 그룹 가입을 요청했지만 아직 수락되지 않은 다른 사용자가 포함됩니다.
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
constgroup_id="<group id>";constusers=awaitclient.listGroupUsers(session,group_id);console.info("Users in group:",users);
Client
1
2
3
4
5
6
7
conststringgroupId="<group id>";varresult=awaitclient.ListGroupUsersAsync(session,groupId);foreach(varuginresult.UserGroups){varg=ug.Group;System.Console.WriteLine("group '{0}' role '{1}'",g.Id,ug.State);}
Client
1
2
3
4
5
6
7
autosuccessCallback=[](NGroupUserListPtrlist){std::cout<<"Users in group: "<<list->groupUsers<<std::endl;};stringgroup_id="<group id>";client->listGroupUsers(session,group_id,{},{},{},successCallback);
Client
1
2
3
4
5
6
Stringgroupid="<group id>";GroupUserListgroupUsers=client.listGroupUsers(session,groupid).get();for(GroupUserList.GroupUsergroupUser: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
vargroup_id="<group id>"varmember_list:NakamaAPI.ApiGroupUserList=yield(client.list_group_users_async(session,group_id),"completed")ifmember_list.is_exception():print("An error occurred: %s"%member_list)returnforuginmember_list.group_users:varu=ug.userasNakamaAPI.ApiUserprint("User %s role %s"%[u.id,ug.state])
// Assuming a group metadata structure as follows
constmetadata={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
letBeforeKickGroupUser: nkruntime.BeforeHookFunction<KickGroupUsersRequest>=function(ctx: nkruntime.Context,logger: nkruntime.Logger,nk: nkruntime.Nakama,data: nkruntime.KickGroupUsersRequest):nkruntime.KickGroupUsersRequest|void{constgroups=nk.groupsGetId([data.groupId]);if(groups.length==0){logger.warn('Invalid group Id');returnnull;}// Only continue with the Kick request if the actioning user has the bouncer role
constroles=groups[0].metadata.roles;if(roles&&roles[ctx.userId]&&roles[ctx.userId].includes('bouncer')){returndata;}logger.warn("you must be a bouncer to kick group members")returnnull;};// Register inside InitModule
initializer.registerBeforeKickGroupUsers(BeforeKickGroupUser);
// Assuming a group metadata structure as follows
typeGroupMetadatastruct{Rolesmap[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
iferr:=initializer.RegisterBeforeKickGroupUsers(func(ctxcontext.Context,loggerruntime.Logger,db*sql.DB,nkruntime.NakamaModule,in*api.KickGroupUsersRequest)(*api.KickGroupUsersRequest,error){userId,ok:=ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)if!ok{logger.Error("invalid user")returnnil,runtime.NewError("invalid user",13)}groups,err:=nk.GroupsGetId(ctx,[]string{in.GroupId})iferr!=nil||len(groups)==0{logger.Error("group not found")returnnil,runtime.NewError("group not found",5)}// Only continue with the Kick request if the actioning user has the bouncer role
varmetadataGroupMetadataiferr:=json.Unmarshal([]byte(groups[0].GetMetadata()),&metadata);err!=nil{logger.Error("error deserializing metadata")returnnil,runtime.NewError("error deserializing metadata",13)}for_,role:=rangemetadata.Roles[userId]{ifrole=="bouncer"{returnin,nil}}returnnil,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)returnerr}
-- Assuming a group metadata structure as followslocalmetadata={["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 userslocalfunctionbefore_kick_group_users(context,payload)localgroups=nk.groups_get_id({payload.group_id})if#groups==0thennk.logger_error("group not found")returnnilendlocalroles=groups[1].metadata["roles"]ifroles==nilorroles[context.user_id]==nilthennk.logger_error("no roles configured for user")returnnilendfor_,roleinipairs(roles[context.user_id])doifrole=="bouncer"thenreturnpayloadendendnk.logger_error("you must be a bouncer to kick group members")returnnilendnk.register_req_before(before_kick_group_users,"KickGroupUsers")
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
constgroup_id="<group id>";constdescription="Better than Marvel Heroes!";constgroup=awaitclient.updateGroup(session,group_id,{description:description});console.info("Updated group:",group);
Client
1
2
3
4
conststringgroupId="<group id>";conststringdesc="Better than Marvel Heroes!";vargroup=awaitclient.UpdateGroupAsync(session,groupId,null,desc);System.Console.WriteLine("Updated group: {0}",group);
Client
1
2
3
4
5
6
7
8
autosuccessCallback=[](){std::cout<<"Updated group"<<std::endl;};stringgroup_id="<group id>";stringdescription="Better than Marvel Heroes!";client->updateGroup(session,group_id,opt::nullopt,description,opt::nullopt,opt::nullopt,opt::nullopt,successCallback);
Client
1
2
3
4
Stringgroupid="<group id>";Stringdesc="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
vargroup_id="<group id>"vardescription="Better than Marvel Heroes!"varupdate:NakamaAsyncResult=yield(client.update_group_async(session,group_id,null,description),"completed")ifupdate.is_exception():print("An error occurred: %s"%update)returnprint("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!",
}
localnk=require("nakama")localnew_max_size=50localsuccess,err=pcall(nk.group_update("<GroupId>",nil,"","","","","",nil,nil,new_max_size))if(notsuccess)thennk.logger_error(("Could not update group: %q"):format(err))end
autosuccessCallback=[](){std::cout<<"added user to group"<<std::endl;};stringgroup_id="<group id>";stringuser_id="<user id>";client->addGroupUsers(session,group_id,{user_id},successCallback);
autosuccessCallback=[](){std::cout<<"user has been promoted"<<std::endl;};stringgroup_id="<group id>";stringuser_id="<user id>";client->promoteGroupUsers(session,group_id,{user_id},successCallback);
autosuccessCallback=[](){std::cout<<"user has been kicked"<<std::endl;};stringgroup_id="<group id>";stringuser_id="<user id>";client->kickGroupUsers(session,group_id,{user_id},successCallback);