群组
群组(下文简称群)有固定的成员,适合同事群、家长群、兴趣群等场景。用户可以在客户端建群、邀请他人、申请加入公开群、通过入群链接加入;群主和管理员可以修改群资料、管理成员、审批入群申请。
Flutter SDK 把“我的群”和读取过的群成员列表保存在本地数据库中,以可订阅的列表(LiveList)提供:登录、重连、回到前台后自动同步,其他成员和其他设备上的变化实时反映到列表中,App 重新启动后不必重新获取。入群申请、邀请这类只需要翻页查看的数据按页查询。
本页介绍群本身和成员的操作。发送群消息见消息(目标写成 SendTarget.group(groupId)),群文件见文件与图片。群的概念(类型、角色、设置、状态)与服务端相同,完整的规则见服务端的群组管理。
基本概念
群类型
GroupType | 说明 |
|---|---|
private | 私有群,默认。只能由成员邀请、通过入群链接加入或由你的服务端添加;非成员看不到这个群,对它的任何操作都得到 not_found |
public | 公开群。用户可以搜索到它,看到名称、头像、简介和人数,并申请加入 |
角色与权限
| 操作 | 群主 owner | 管理员 admin | 普通成员 member |
|---|---|---|---|
| 修改群资料和设置 | 可以 | 可以(群类型除外) | 不可以 |
| 邀请他人 | 可以 | 可以 | 按群设置 member_invite |
| 移出、禁言成员,管理群黑名单 | 可以 | 只能处理普通成员 | 不可以 |
| 开启全员禁言,审批入群申请,生成入群链接 | 可以 | 可以 | 不可以 |
| 设置管理员、转让群主、解散群 | 可以 | 不可以 | 不可以 |
| 修改自己的群昵称 | 可以 | 可以 | 可以 |
角色在 Dart 中是枚举 GroupRole(owner、admin、member)。每个群最多 20 个管理员。群主不能被移出、禁言或加入群黑名单;群主要退出,须先转让群主。
群设置
| 设置 | Dart 枚举 | 取值 | 说明 |
|---|---|---|---|
member_invite | MemberInvite | free(默认)、approval、disabled | 普通成员能否邀请他人:直接邀请、邀请后要群主或管理员审批、不能邀请 |
join_mode | JoinMode | approval(默认)、free | 公开群的申请方式:需要审批、申请即加入。私有群为 null,不能设置 |
mute_all | — | true、false | 全员禁言,开启后只有群主和管理员可以发言 |
max_members | — | 只读 | 群的人数上限,只能由你的服务端或控制台设置 |
读取群信息时这些设置是字符串(如 group.memberInvite == 'approval'),修改时传枚举。
建群
用户在客户端建群时自己是群主,可以同时邀请初始成员(最多 100 人)。初始成员按邀请的规则处理:对方的入群设置为需要确认时,他收到一条邀请,接受后才入群。
import 'package:deeprespond_im/deeprespond_im.dart';
final group = await im.groups.create(
name: '产品研发群',
type: GroupType.private,
description: '产品和研发日常沟通',
members: ['lisi', 'wangwu', 'zhaoliu'],
message: '拉你进项目群', // 给需要确认的被邀请人看的附言
);
for (final r in group.results) {
if (!r.ok) {
print('${r.username} 没有加入:${r.code}');
} else if (r.status == 'invited') {
print('${r.username} 需要确认后入群');
}
}- 返回值是
CreateGroupResult:新群的全部信息,另加results,按请求的顺序列出每个初始成员的结果。初始成员加入失败不影响建群。 - 新群随即出现在“我的群”列表中。
- 应用在运行策略中关闭了客户端建群时,以
permission_denied拒绝。可以先读取im.config?.clientGroupCreateEnabled决定是否显示建群入口。 - 本人加入的群数已满时以
limit_exceeded(details['reason']为user_group_limit)拒绝,不建群;应用的群数已达套餐的上限时为app_group_limit。 - 群名称、简介、公告、头像要经过内容安全检查,不通过时以
content_rejected拒绝,details['field']为字段名。
建群不会自动重试
网络错误或超时后,SDK 先刷新本人的群列表:找到这次新建的群(群主是本人、创建时间在这次调用之后)的按成功返回,这时 results 为空列表,可以用 members() 查看实际入群的人;找不到的以 timeout 拒绝,由用户决定是否再建。遇到 rate_limited 时请提示用户,不要自动重试。
我的群
im.groups.list() 返回本人加入的全部群的可订阅列表,每项为 GroupView,按群名排序。登录后 SDK 自动同步;建群、入群、退群、被移出、群解散,以及群资料的变化都会反映到列表中。列表不再显示时调用 dispose()。
import 'package:deeprespond_im/deeprespond_im.dart';
import 'package:deeprespond_im_flutter/deeprespond_im_flutter.dart';
import 'package:flutter/material.dart';
class MyGroupsPage extends StatefulWidget {
const MyGroupsPage({super.key, required this.im});
final DRClient im;
@override
State<MyGroupsPage> createState() => _MyGroupsPageState();
}
class _MyGroupsPageState extends State<MyGroupsPage> {
late final LiveList<GroupView> _groups = widget.im.groups.list();
@override
void dispose() {
_groups.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => LiveListBuilder<GroupView>(
list: _groups,
builder: (context, snapshot) => ListView(
children: [
for (final g in snapshot.items)
ListTile(
title: Text(g.group.name.isNotEmpty ? g.group.name : '群聊'),
trailing: Text('${g.group.memberCount}'),
),
],
),
);
}本人在群里的角色、群昵称和禁言状态在 group.self 中:
final view = await im.groups.get('1840012345678901');
final self = view?.group.self;
final canManage = self?.role == GroupRole.owner || self?.role == GroupRole.admin;im.groups.get(groupId)先读本地,本地有就直接返回;本地没有(如别人分享的公开群)或refresh: true时向服务端查询。看不到的群(不存在、本人不是成员的私有群)返回null。- 已离开的群
inMyGroups为false,group为离开前最后一次的信息,会话列表仍可以用它显示群名和头像。 - 一次查询多个群用
im.groups.lookup(groupIds)(最多 100 个),本人不是成员的公开群只返回公开的字段,看不到的群不在结果中。
群资料和我的群列表的变化以 GroupsChanged 事件通知,upserted 为新增或变化的群,removed 为离开了“我的群”的群:
im.on<GroupsChanged>().listen((e) {
print('变化的群:${e.upserted},离开的群:${e.removed}');
});修改群资料和设置
群主和管理员可以修改群资料和设置,只提交传入的参数。群类型只有群主能改。修改后群的在线成员收到变化,本地的群信息随之更新。
await im.groups.update(
'1840012345678901',
name: '产品研发群(2 期)',
announcement: '本周五发布 2.3 版本',
attributes: {'project_id': 'P-1024', 'old_key': null}, // 按项合并,null 删除这个键
memberInvite: MemberInvite.approval,
);- 公开群改为私有群时,尚未处理的入群申请全部失效;私有群改为公开群时,申请方式默认为需要审批。
- 为私有群设置
joinMode以invalid_argument拒绝。 - 自定义属性全体成员可见,按项合并;
clearAttributes: true清空全部。 - 多人可能同时修改时,带上读取到的
group.infoVersion作为version,群资料已被别人修改时以version_conflict拒绝,不做任何修改。 - 修改的名称、简介、公告、头像要经过内容安全检查,不通过时以
content_rejected拒绝。 - 群被封禁时以
group_disabled拒绝。
设置群头像
群头像要先以用途 group_avatar 上传,得到公开地址后再写入群资料(建群前就可以上传):
Future<void> changeGroupAvatar(String groupId, String path) async {
final info = await im.files.upload(DRFile(path), purpose: UploadPurpose.groupAvatar).result;
await im.groups.update(groupId, avatarUrl: info.url);
}成员列表
im.groups.members(groupId) 返回群成员的可订阅列表(LiveList<GroupMember>),按群主、管理员、普通成员排列,同一角色按入群时间排列。只有成员可以查看。
final members = im.groups.members(groupId);
final sub = members.stream.listen((s) {
if (s.error != null) {
showToast('成员列表加载失败:${s.error!.code}');
} else if (!s.loading) {
print(s.items.map((m) => m.groupNickname.isNotEmpty ? m.groupNickname : (m.nickname.isNotEmpty ? m.nickname : m.username)));
}
});
// 只看管理员、只看被禁言的成员
final admins = im.groups.members(groupId, role: GroupRole.admin);
final muted = im.groups.members(groupId, muted: true);
// 页面关闭时
await sub.cancel();
members.dispose();
admins.dispose();
muted.dispose();- 第一次打开某个群的成员列表时,SDK 获取全部成员并保存在本地;之后再打开时只检查是否有变化,App 重新启动后同样有效。筛选在本地进行。
- 成员的加入、离开,角色、群昵称、禁言的变化实时反映到列表中,同时发出
GroupMembersChanged。 - 列表不再显示时调用
dispose()。在 Widget 中用LiveListBuilder显示,写法同我的群。
只需要显示少数人的群昵称(如消息发送者)时,不必取整个成员列表,用 lookupMembers(最多 100 人,不是成员的不在结果中):
final found = await im.groups.lookupMembers(groupId, ['lisi', 'wangwu']);显示名字时可以用 package:deeprespond_im/render.dart 中的 displayName(im, username, groupId: groupId),它按好友备注、群昵称、昵称、用户名的顺序取第一个非空的,见用户、好友与在线状态。
群的在线人数(需要应用开启在线状态且 presence_scope 为 all,否则在本地以 permission_denied 拒绝):
final count = await im.groups.onlineCount(groupId);
print('${count.onlineCount} 人在线');同一个群 30 秒内重复查询直接返回上一次的结果。
邀请成员
一次最多邀请 100 人,可以带附言。结果按请求的顺序逐个给出,某个人失败不影响其他人:
final results = await im.groups.invite(groupId, ['sunqi', 'zhouba'], message: '欢迎加入');
for (final r in results) {
if (!r.ok) showToast('${r.username}:${r.code}');
}成功的 status:
status | 说明 |
|---|---|
joined | 已入群 |
invited | 对方需要确认,已发出邀请,见被邀请时是否需要确认 |
pending_review | 本人是普通成员且群设置为邀请需要审批,等待群主或管理员审批 |
already_member | 原本就是成员 |
失败的项 ok 为 false,带 code,常见的有:permission_denied(对方在群黑名单中,details['reason'] 为 group_blacklisted;或被加群前回调拒绝)、limit_exceeded(群已满 group_member_limit,或对方的群数已满 user_group_limit)、user_blocked(对方把本人拉黑了)、not_friend(应用只允许邀请好友)、rate_limited(declined_cooldown:对方 24 小时内拒绝过这个群的邀请或主动退出了这个群;request_rate、group_join_rate:邀请或入群过于频繁)、not_found(用户不存在)。
整个请求失败的情况:普通成员在 member_invite 为 disabled 的群里邀请(permission_denied),群被封禁(group_disabled)。
移出成员
群主和管理员可以移出成员(管理员只能移出普通成员),一次最多 100 人。结果中 removed 为已移出,not_member 为原本就不是成员。不能用它移出自己,请用退出群。
await im.groups.removeMembers(groupId, ['zhouba']);设置管理员
群主可以把成员设为管理员或取消管理员,每个群最多 20 个管理员(超出以 limit_exceeded 拒绝,details['reason'] 为 admin_limit)。
await im.groups.setRole(groupId, 'lisi', GroupRole.admin);
await im.groups.setRole(groupId, 'lisi', GroupRole.member); // 取消管理员群昵称与成员属性
每个成员可以修改自己的群昵称;群主和管理员也可以修改级别比自己低的成员的群昵称和成员属性。成员属性按项合并,值为 null 的键被删除,clearAttributes: true 清空全部。
final me = im.auth.currentUser!.username;
await im.groups.updateMember(groupId, me, groupNickname: '李四-研发');
await im.groups.updateMember(groupId, 'wangwu', attributes: {'title': '后端'});群昵称最长 64 个字符,传空字符串表示清除;要经过内容安全检查。
禁言
禁言成员
群主和管理员可以禁言成员(管理员只能禁言普通成员),省略时长为永久禁言。被禁言的成员发送群消息时得到 group_muted(details['reason'] 为 member)。
await im.groups.mute(groupId, 'zhouba', durationSeconds: 3600); // 禁言 1 小时
await im.groups.unmute(groupId, 'zhouba');时长为 1 到 315360000 秒(10 年)。本人是否被禁言在 group.self 的 muted 和 mutedUntil 中,可以据此禁用输入框。
全员禁言
开启后只有群主和管理员可以发言,普通成员发送群消息时得到 group_muted(details['reason'] 为 all)。被单独禁言的管理员仍然不能发言。
await im.groups.setMuteAll(groupId, true);
await im.groups.setMuteAll(groupId, false);群黑名单
被加入群黑名单的用户被移出群(如果在群里),之后不能再申请、被邀请或通过入群链接加入这个群。群主和管理员可以管理群黑名单;管理员只能移出由管理员加入的记录。每个群最多 1000 人。
await im.groups.blacklist.add(groupId, 'spammer', reason: '发广告');
final page = await im.groups.blacklist.list(groupId, limit: 50);
for (final e in page.items) {
print('${e.username}:${e.reason ?? ''}');
}
await im.groups.blacklist.remove(groupId, 'spammer');列表的每项为 GroupBlacklistEntry。群黑名单只影响本群,与用户之间的黑名单无关。
公开群与入群申请
搜索公开群
im.groups.publicGroups() 按创建时间从新到旧列出公开群,可以按名称前缀搜索。每项只有公开的字段(groupId、type、name、avatarUrl、description、memberCount、joinMode、status),self 为本人在群里的信息,不是成员时为 null。
final page = await im.groups.publicGroups(namePrefix: '摄影', limit: 20);
for (final g in page.items) {
print('${g.name}(${g.memberCount} 人)${g.self != null ? ' 已加入' : ''}');
}
// 下一页:im.groups.publicGroups(namePrefix: '摄影', cursor: page.nextCursor)申请加入
final status = await im.groups.applications.apply(groupId, message: '我也喜欢摄影');
if (status == 'joined') {
showToast('已加入');
} else if (status == 'pending') {
showToast('已提交申请,等待群主或管理员审批');
}群的申请方式为 free 时直接入群;为 approval 时产生一条待审批的申请,任意一位群主或管理员同意后入群。申请理由最长 256 个字符。申请被拒绝后 24 小时内不能再申请这个群(rate_limited,details['reason'] 为 declined_cooldown),同一个人对同一个群每天最多申请 5 次(pair_daily_limit)。私有群不能申请,得到 not_found;本人在群黑名单中得到 permission_denied(group_blacklisted)。
撤回待处理的申请,查看本人发出的申请:
await im.groups.applications.withdraw(groupId);
final mine = await im.groups.applications.mine();申请已被处理时 withdraw 以 version_conflict 拒绝,details['status'] 为申请当前的状态。
审批申请和成员的邀请
群主和管理员用 im.groups.requests 查看和处理本群的请求。请求按“群 + 类型 + 用户名”定位,没有单独的 ID。请求的类型 GroupRequestKind:
GroupRequestKind | 服务端的取值 | 说明 | 由谁处理 |
|---|---|---|---|
application | application | 用户申请加入公开群 | 群主或管理员 |
inviteReview | invite_review | 普通成员在需要审批的群里发出的邀请 | 群主或管理员 |
invitation | invitation | 等待被邀请人确认的邀请 | 被邀请人,见下一节 |
final page = await im.groups.requests.list(groupId, kind: GroupRequestKind.application, status: GroupRequestStatus.pending);
for (final r in page.items) {
print('${r.nickname.isNotEmpty ? r.nickname : r.username} 申请加入:${r.message ?? ''}');
}
final result = await im.groups.requests.approve(groupId, GroupRequestKind.application, 'sunqi');
await im.groups.requests.decline(groupId, GroupRequestKind.application, 'zhouba', reason: '仅限本校学生');approve的返回值:joined已入群;invited成员的邀请审批通过、被邀请人需要确认,已向他发出邀请;canceled被邀请人已不能接收这个群的邀请,请求失效。- 处理时会重新检查入群条件(群正常、不在群黑名单中、人数和群数未满),不满足时以对应的错误拒绝,请求保持待处理,条件满足后可以再次处理。
- 请求已被别人处理时以
version_conflict拒绝,details['status']为请求当前的状态;已过期或不存在时以not_found拒绝。 - 拒绝的理由最长 256 个字符,展示给申请人或邀请人。拒绝申请后,申请人 24 小时内不能再申请。
- 普通成员调用
requests.list()只能看到自己发出的邀请。邀请人、群主和管理员可以用requests.withdrawInvite(groupId, kind, username)撤回待处理的邀请(kind为inviteReview或invitation)。
新的请求到达、请求被处理时发出 GroupRequestsChanged(list 为 pending,groupId 为这个群),可以据此刷新审批页面或显示红点:
im.on<GroupRequestsChanged>().listen((e) {
if (e.list == 'pending') print('群 ${e.groupId} 有新的待审批请求');
});待处理的请求在最近一次发起后 7 天未处理即过期。完整规则见服务端的入群申请与邀请。
被邀请时是否需要确认
每个用户可以设置别人邀请自己入群时怎样处理:InviteMode.allowAny(allow_any)直接入群,InviteMode.needConfirm(need_confirm)收到一条邀请、接受后才入群。没有设置过时使用应用运行策略中的默认值(默认 allow_any)。
final settings = await im.groups.settings.get();
print(settings.inviteMode == null ? '跟随应用默认(${settings.effectiveInviteMode?.wire})' : settings.inviteMode!.wire);
await im.groups.settings.set(InviteMode.needConfirm);
await im.groups.settings.set(null); // 清除本人的设置,改用应用的默认值处理收到的邀请
final page = await im.groups.invitations.received(status: GroupRequestStatus.pending);
for (final inv in page.items) {
final inviter = inv.inviter?.nickname ?? '有人';
print('$inviter 邀请你加入 ${inv.group?.name ?? '群聊'}');
}
final group = await im.groups.invitations.accept('1840012345678901');
await im.groups.invitations.decline('1840099999999999', reason: '暂时不参加');invitations.received()和applications.mine()不带cursor和status时返回 SDK 内存中的第一页,不发请求;SDK 在登录、重连和收到相关通知时刷新这一页,刷新后发出GroupRequestsChanged(list为invitations或applications)。- 接受后入群,返回群信息,群随即出现在“我的群”中。本人已经通过其他途径入群的同样返回群信息。
- 拒绝后 24 小时内这个群不能再邀请本人。
入群链接
群主和管理员可以生成入群链接,私有群也可以。用户打开链接后确认即可直接入群,不需要审批。每个群同时只有一个有效链接,重新生成或撤销后旧链接立即失效;生成链接的人不再是群主或管理员时,链接也随之失效。链接可以展示为二维码。
final created = await im.groups.joinLink.create(groupId, expiresInSeconds: 86400);
print(created.link);
final current = await im.groups.joinLink.get(groupId); // 没有有效链接时三项都为 null
await im.groups.joinLink.revoke(groupId);有效期为 86400 到 604800 秒(1 到 7 天),默认 7 天。
打开链接的一方(如扫码后)先预览,再确认加入:
final preview = await im.groups.joinLink.preview(link);
if (preview.isMember) {
showToast('你已在群中');
} else {
final group = await im.groups.joinLink.join(link);
print(group.name);
}链接无效、过期、已撤销,或群已封禁、已解散时,preview 和 join 都以 not_found 拒绝。已是成员时 join 直接返回群信息。预览和加入合计按用户每分钟 20 次限流。
退出与解散
await im.groups.leave(groupId); // 退出群
await im.groups.dismiss(groupId); // 解散群,只有群主可以- 退出:退出后群从“我的群”中移除,本地的成员列表随之删除。群主退出以
owner_transfer_required拒绝,请先转让群主;群里只剩群主一人时请解散。主动退出后 24 小时内,这个群不能再邀请本人。 - 解散:不能恢复。群的全部成员收到通知,群从各自的“我的群”中移除;群文件全部被删除,本地这个群的会话免打扰设置也一并删除。被平台封禁的群不能解散(
group_disabled)。
被移出群或群被解散时,本人的群列表同样自动更新,原因可以从会话中的群提示消息得知(见消息)。
转让群主
群主可以把群主转让给群里的另一个成员。转让后原群主变为普通成员,原群主生成的入群链接失效。
final info = await im.groups.transferOwner(groupId, 'wangwu');
print(info.owner); // wangwu接口参考
各方法的 groupId 参数都是群 ID 字符串。私有群对非成员不可见,对它的任何操作都以 not_found 拒绝;本人不是公开群的成员时,只有成员才能做的操作以 not_group_member 拒绝;角色不够时以 permission_denied 拒绝;群被封禁时,被禁止的操作以 group_disabled 拒绝;未登录时以 not_signed_in 拒绝。下文不再逐个列出这几个错误。
im.groups.list()
返回本人加入的群的可订阅列表(LiveList<GroupView>),按群名排序;快照的 hasMore 总是 false。不再使用时调用 dispose()。
im.groups.get()
读取一个群。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
refresh | bool | 否 | 命名参数。为 true 时不读本地,向服务端查询;默认 false |
返回值:Future<GroupView?>,看不到的群为 null。
im.groups.lookup()
批量查询群信息。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupIds | List<String> | 是 | 1 到 100 个群 ID |
返回值:Future<List<GroupInfo>>。看不到的群不在结果中;本人不是成员的公开群只有公开的字段。
可能的错误:local_validation(为空或超过 100 个)。
im.groups.publicGroups()
列出公开群,按创建时间从新到旧,不含已封禁和已解散的群。参数都是命名参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
namePrefix | String? | 否 | 按群名称的前缀搜索 |
cursor | String? | 否 | 下一页的游标 |
limit | int? | 否 | 每页条数,默认 20,最大 100 |
返回值:Future<DRPage<GroupInfo>>。
im.groups.create()
建群,本人为群主。不自动重试,见建群。参数都是命名参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | String | 是 | 群名称,最长 64 个字符 |
type | GroupType? | 否 | 群类型,默认 private |
avatarUrl | String? | 否 | 群头像地址 |
description | String? | 否 | 群简介,最长 512 个字符 |
announcement | String? | 否 | 群公告,最长 2048 个字符 |
attributes | Map<String, String?>? | 否 | 自定义属性,全体成员可见;最多 32 项,合计不超过 4 KB |
memberInvite | MemberInvite? | 否 | 普通成员能否邀请他人,默认 free |
joinMode | JoinMode? | 否 | 公开群的申请方式,默认 approval;私有群不能设置 |
members | List<String>? | 否 | 初始成员的用户名,最多 100 个 |
message | String? | 否 | 给初始成员的邀请附言 |
返回值:Future<CreateGroupResult>。
可能的错误:invalid_argument、permission_denied(应用关闭了客户端建群)、limit_exceeded(user_group_limit、app_group_limit)、content_rejected、rate_limited、app_unavailable(应用只读)、timeout。
im.groups.update()
修改群资料和设置(群主和管理员,群类型只有群主能改),只提交传入的参数。除 groupId 外都是命名参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
type、name、avatarUrl、description、announcement、memberInvite、joinMode | 同 create | 否 | 要修改的字段,至少一项 |
attributes | Map<String, String?>? | 否 | 自定义属性,按项合并,值为 null 的键被删除 |
clearAttributes | bool | 否 | 为 true 时清空全部自定义属性,默认 false |
version | int? | 否 | 读取到的 infoVersion,不一致时不修改 |
返回值:Future<GroupInfo>,修改后的群信息。
可能的错误:invalid_argument(为私有群设置 joinMode 等)、content_rejected、version_conflict。
im.groups.transferOwner()
转让群主(群主)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
username | String | 是 | 新群主,必须是本群成员 |
返回值:Future<GroupInfo>。
可能的错误:not_group_member(对方不是成员)。
im.groups.setMuteAll()
开启或关闭全员禁言(群主和管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
on | bool | 是 | true 开启,false 关闭 |
返回值:Future<GroupInfo>。
im.groups.leave()
退出群。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
返回值:Future<void>。
可能的错误:owner_transfer_required(本人是群主)。
im.groups.dismiss()
解散群(群主),不能恢复。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
返回值:Future<void>。
im.groups.onlineCount()
查询群的在线人数。同一个群 30 秒内重复调用返回上一次的结果。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
返回值:Future<GroupOnlineCount>。
可能的错误:permission_denied(details['reason'] 为 presence_disabled:应用没有开启在线状态;presence_scope:在线状态的可见范围不是 all。这两种在本地判断)、rate_limited。
im.groups.members()
返回群成员的可订阅列表(LiveList<GroupMember>)。只有成员可以查看。不再使用时调用 dispose()。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
role | GroupRole? | 否 | 命名参数。只列出这种角色的成员 |
muted | bool? | 否 | 命名参数。true 只列出被禁言的成员,false 只列出没有被禁言的 |
获取失败时,快照的 error 为对应的 DRException。未登录时抛出 not_signed_in。
im.groups.lookupMembers()
按用户名查询成员信息(成员才能调用)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
usernames | List<String> | 是 | 1 到 100 个用户名 |
返回值:Future<List<GroupMember>>,不是成员的用户不在结果中。
可能的错误:local_validation(为空或超过 100 个)。
im.groups.invite()
邀请他人入群。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
usernames | List<String> | 是 | 1 到 100 个用户名 |
message | String? | 否 | 命名参数。邀请附言 |
返回值:Future<List<GroupMemberResult>>,每人的结果见邀请成员。
可能的错误:local_validation、permission_denied(普通成员在不允许邀请的群里邀请)、content_rejected(附言)。
im.groups.removeMembers()
移出成员(群主和管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
usernames | List<String> | 是 | 1 到 100 个用户名 |
返回值:Future<List<GroupMemberResult>>,成功的 status 为 removed 或 not_member。
可能的错误:local_validation、invalid_argument(移出自己)。
im.groups.updateMember()
修改成员的群昵称和成员属性:本人,或级别更高的群主、管理员。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
username | String | 是 | 成员的用户名 |
groupNickname | String? | 否 | 命名参数。群昵称,最长 64 个字符;空字符串表示清除 |
attributes | Map<String, String?>? | 否 | 命名参数。成员属性,按项合并,值为 null 的键被删除。最多 16 项,合计不超过 1 KB |
clearAttributes | bool | 否 | 命名参数。为 true 时清空全部成员属性,默认 false |
返回值:Future<GroupMember>。
可能的错误:content_rejected(群昵称)、not_group_member(对方不是成员)。
im.groups.setRole()
设置或取消管理员(群主)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
username | String | 是 | 成员的用户名 |
role | GroupRole | 是 | 新角色:admin 或 member |
返回值:Future<GroupMember>。
可能的错误:limit_exceeded(admin_limit)、invalid_argument(对群主设置)。
im.groups.mute()
禁言成员(群主和管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
username | String | 是 | 成员的用户名 |
durationSeconds | int? | 否 | 命名参数。禁言时长,1 到 315360000 秒;省略时永久禁言 |
返回值:Future<GroupMember>。
im.groups.unmute()
解除成员的禁言(群主和管理员)。不能给自己解除。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
username | String | 是 | 成员的用户名 |
返回值:Future<GroupMember>。
im.groups.blacklist.list()
查询群黑名单(群主和管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
cursor | String? | 否 | 命名参数。下一页的游标 |
limit | int? | 否 | 命名参数。每页条数,默认 20,最大 100 |
返回值:Future<DRPage<GroupBlacklistEntry>>。
im.groups.blacklist.add()
把用户加入群黑名单(群主和管理员);他在群里时同时被移出。已在群黑名单中时同样成功。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
username | String | 是 | 用户名 |
reason | String? | 否 | 命名参数。原因 |
返回值:Future<void>。
可能的错误:limit_exceeded(group_blacklist_limit)、invalid_argument(拉黑自己)。
im.groups.blacklist.remove()
把用户移出群黑名单。管理员只能移出由管理员加入的记录。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
username | String | 是 | 用户名 |
返回值:Future<void>。
im.groups.joinLink.get()
查询当前有效的入群链接(群主和管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
返回值:Future<JoinLink>;没有有效链接时三个字段都为 null。
im.groups.joinLink.create()
生成或重新生成入群链接(群主和管理员),旧链接立即失效。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
expiresInSeconds | int? | 否 | 命名参数。有效期,86400 到 604800 秒,默认 604800 |
返回值:Future<JoinLink>。
im.groups.joinLink.revoke()
撤销入群链接(群主和管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
返回值:Future<void>。
im.groups.joinLink.preview()
预览入群链接。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
link | String | 是 | 入群链接 |
返回值:Future<JoinLinkPreview>。
可能的错误:not_found(链接无效、过期、已撤销,或群已封禁、已解散)、rate_limited。
im.groups.joinLink.join()
通过入群链接加入。已是成员时直接返回群信息。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
link | String | 是 | 入群链接 |
返回值:Future<GroupInfo>。
可能的错误:not_found、permission_denied(在群黑名单中,或被加群前回调拒绝)、limit_exceeded(群已满或本人的群数已满)、rate_limited。
im.groups.applications.apply()
申请加入公开群。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
message | String? | 否 | 命名参数。申请理由,最长 256 个字符 |
返回值:Future<String>:joined、pending 或 already_member。
可能的错误:not_found(私有群)、permission_denied(group_blacklisted,或被加群前回调拒绝)、limit_exceeded(user_group_limit、group_member_limit)、rate_limited(declined_cooldown、pair_daily_limit、request_rate、group_join_rate)、content_rejected。
im.groups.applications.withdraw()
撤回本人待处理的申请。已撤回过同样成功。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
返回值:Future<void>。
可能的错误:version_conflict(申请已被处理,details['status'] 为当前状态)。
im.groups.applications.mine()
查询本人发出的入群申请,按发起时间从新到旧。参数都是命名参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
status | GroupRequestStatus? | 否 | 只列出这种状态的申请 |
cursor | String? | 否 | 下一页的游标 |
limit | int? | 否 | 每页条数 |
返回值:Future<DRPage<GroupRequest>>。不带 cursor 和 status 时返回 SDK 内存中的第一页。
im.groups.invitations.received()
查询本人收到的入群邀请,按发起时间从新到旧。参数同 applications.mine()。
返回值:Future<DRPage<GroupRequest>>。不带 cursor 和 status 时返回 SDK 内存中的第一页。
im.groups.invitations.accept()
接受入群邀请。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
返回值:Future<GroupInfo>。
可能的错误:not_found(邀请不存在或已过期)、permission_denied(在群黑名单中)、limit_exceeded、rate_limited(group_join_rate)、version_conflict(邀请已被撤回等)。
im.groups.invitations.decline()
拒绝入群邀请,之后 24 小时内这个群不能再邀请本人。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
reason | String? | 否 | 命名参数。拒绝理由,最长 256 个字符 |
返回值:Future<void>。
im.groups.requests.list()
查询本群的请求,按发起时间从新到旧。群主和管理员可以看到全部类型,普通成员只能看到自己发出的邀请。每次都向服务端查询。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
kind | GroupRequestKind? | 否 | 命名参数。application、inviteReview 或 invitation |
status | GroupRequestStatus? | 否 | 命名参数。pending、accepted、declined、canceled 或 expired |
cursor | String? | 否 | 命名参数。下一页的游标 |
limit | int? | 否 | 命名参数。每页条数 |
返回值:Future<DRPage<GroupRequest>>。
im.groups.requests.approve()
通过申请或成员发起的邀请(群主和管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
kind | GroupRequestKind | 是 | application 或 inviteReview |
username | String | 是 | 申请人或被邀请人 |
返回值:Future<String>:joined、invited 或 canceled。
可能的错误:not_found(请求不存在或已过期)、version_conflict(已被别人处理)、permission_denied(group_blacklisted)、limit_exceeded、rate_limited(group_join_rate)。
im.groups.requests.decline()
拒绝申请或成员发起的邀请(群主和管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
kind | GroupRequestKind | 是 | application 或 inviteReview |
username | String | 是 | 申请人或被邀请人 |
reason | String? | 否 | 命名参数。拒绝理由,最长 256 个字符 |
返回值:Future<void>。
可能的错误:not_found、version_conflict、content_rejected(理由)。
im.groups.requests.withdrawInvite()
撤回待处理的邀请:邀请人本人,或群主、管理员。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
kind | GroupRequestKind | 是 | inviteReview 或 invitation |
username | String | 是 | 被邀请人 |
返回值:Future<void>。
可能的错误:not_found、version_conflict。
im.groups.settings.get()
查询本人被邀请时的处理方式。
返回值:Future<GroupInviteSettings>。inviteMode 为本人的设置,没有设置过时为 null;effectiveInviteMode 为实际生效的方式。
im.groups.settings.set()
设置本人被邀请时的处理方式。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
mode | InviteMode? | 是 | allowAny 或 needConfirm;null 清除设置,改用应用的默认值 |
返回值:Future<void>。
相关事件
用 im.on<T>() 按类型订阅,见事件与错误处理。
| 事件 | 字段 | 说明 |
|---|---|---|
GroupsChanged | upserted、removed(群 ID) | 群信息变化(upserted),或群离开了“我的群”(removed) |
GroupMembersChanged | groupId、usernames(List<String>?) | 本地保存的成员列表变化;usernames 为 null 表示整体变化 |
GroupRequestsChanged | list、groupId(String?) | list 为 pending:这个群的待审批请求有变化;invitations、applications:本人收到的邀请、发出的申请的第一页已刷新 |
数据结构
服务端对象(继承 DRObject)的常用字段以 getter 提供;时间为 UTC 的 DateTime。服务端返回的全部字段都在 raw(Map<String, Object?>)中,可以直接读取没有 getter 的字段。
GroupView
| 字段 | 类型 | 说明 |
|---|---|---|
groupId | String | 群 ID |
group | GroupInfo | 群信息;已离开的群为离开前最后一次的信息 |
inMyGroups | bool | 是否在“我的群”中 |
GroupInfo
本人是成员时有全部字段;本人不是成员的公开群只有 groupId、type、name、avatarUrl、description、memberCount、joinMode、status 和 self,其余为 null(或空值)。
| 字段 | 类型 | 说明 |
|---|---|---|
groupId | String | 群 ID |
type | String | 群类型:private 或 public |
name | String | 群名称,没有时为空字符串 |
avatarUrl | String | 群头像地址,没有时为空字符串 |
description | String? | 群简介 |
announcement | String? | 群公告 |
announcementUpdatedAt | DateTime? | 公告最近一次修改的时间 |
announcementUpdatedBy | String? | 最近一次在客户端修改公告的用户名 |
attributes | Map<String, String>? | 自定义属性 |
owner | String? | 群主的用户名 |
memberInvite | String? | 普通成员能否邀请他人:free、approval、disabled |
joinMode | String? | 公开群的申请方式:approval、free;私有群为 null |
muteAll | bool | 是否开启了全员禁言 |
maxMembers | int? | 人数上限 |
memberCount | int | 当前人数,含群主 |
status | String | active 正常、disabled 已封禁、dismissed 已解散 |
infoVersion | int? | 群资料和设置的版本号,可用于 update 的 version |
memberVersion | int? | 成员列表的版本号 |
createdAt | DateTime? | 创建时间 |
self | GroupSelf? | 本人在群里的信息;不是成员时为 null |
raw['disabled_by'] 为封禁方:tenant 应用、platform 平台;未封禁时为 null。
GroupSelf
| 字段 | 类型 | 说明 |
|---|---|---|
role | GroupRole? | 本人的角色 |
groupNickname | String | 本人的群昵称,没有时为空字符串 |
muted | bool | 本人是否被禁言 |
mutedUntil | DateTime? | 禁言的到期时间,永久禁言为 null |
joinedAt | DateTime? | 入群时间 |
CreateGroupResult
继承 GroupInfo,另有:
| 字段 | 类型 | 说明 |
|---|---|---|
results | List<GroupMemberResult> | 每个初始成员的结果,按请求的顺序;超时后查询确认已建成的群为空列表 |
GroupMember
| 字段 | 类型 | 说明 |
|---|---|---|
username | String | 用户名 |
nickname | String | 用户资料中的昵称 |
avatarUrl | String | 用户资料中的头像 |
role | GroupRole? | owner、admin 或 member |
groupNickname | String | 本群的群昵称,没有时为空字符串 |
attributes | Map<String, String>? | 成员属性 |
muted | bool | 是否被禁言 |
mutedUntil | DateTime? | 禁言的到期时间,永久禁言为 null |
joinedVia | String | 入群方式:create 建群时、server 服务端添加、invite 被邀请、apply 申请、link 入群链接 |
inviter | String? | 邀请人的用户名 |
joinedAt | DateTime? | 入群时间 |
GroupMemberResult
批量操作中一个用户的结果:成功的有 status,失败的有 code。
| 字段 | 类型 | 说明 |
|---|---|---|
username | String | 用户名 |
ok | bool | 是否成功 |
status | String? | 成功时:joined、invited、pending_review、already_member(邀请、建群),或 removed、not_member(移出);失败时为 null |
code | String? | 失败时的错误码 |
message | String? | 失败时的说明 |
details | Map<String, Object?>? | 失败的详细原因,details['reason'] 说明原因 |
GroupOnlineCount
| 字段 | 类型 | 说明 |
|---|---|---|
onlineCount | int | 在线人数 |
countedAt | DateTime? | 统计的时间 |
GroupRequest
入群申请或邀请,requests.list()、applications.mine()、invitations.received() 的每项。
| 字段 | 类型 | 说明 |
|---|---|---|
groupId | String | 群 ID |
kind | String | application、invite_review 或 invitation |
username | String | 请求的目标用户:申请人或被邀请人 |
nickname | String | 目标用户的昵称 |
avatarUrl | String | 目标用户的头像 |
inviter | GroupRequestUser? | 邀请人的 username、nickname、avatarUrl;申请为 null |
message | String? | 申请理由或邀请附言 |
reason | String? | 拒绝理由 |
status | String | pending、accepted、declined、canceled 或 expired |
cancelCause | String? | 失效的原因:withdrawn、joined、blacklisted、group_private、not_eligible、dismissed |
requestedAt | DateTime? | 最近一次发起的时间 |
expiresAt | DateTime? | 过期时间,只有待处理的有值 |
handledAt | DateTime? | 处理时间 |
handledBy | String? | 处理人的用户名 |
group | GroupBrief? | 只在本人收到的邀请和发出的申请中:群的 groupId、name、avatarUrl、memberCount。群是私有群且请求已处理时为 null |
群被封禁期间,待处理的请求显示为 canceled、cancelCause 为 null,解封后恢复。
GroupBlacklistEntry
| 字段 | 类型 | 说明 |
|---|---|---|
username | String | 用户名 |
nickname | String | 昵称 |
avatarUrl | String | 头像 |
reason | String? | 原因 |
createdBy | String? | 执行操作的群主或管理员;你的服务端和控制台加入的为 null |
createdByRole | String? | 加入时操作人的身份:owner、admin 或 server |
createdAt | DateTime? | 加入的时间 |
JoinLink
| 字段 | 类型 | 说明 |
|---|---|---|
link | String? | 入群链接 |
expiresAt | DateTime? | 过期时间 |
createdBy | String? | 生成链接的人 |
JoinLinkPreview
| 字段 | 类型 | 说明 |
|---|---|---|
groupId | String | 群 ID |
name | String | 群名称 |
avatarUrl | String | 群头像 |
memberCount | int | 人数 |
isMember | bool | 本人是否已是成员 |
GroupInviteSettings
| 字段 | 类型 | 说明 |
|---|---|---|
inviteMode | InviteMode? | 本人的设置,没有设置过为 null |
effectiveInviteMode | InviteMode? | 实际生效的方式 |
枚举
| 枚举 | 取值 |
|---|---|
GroupRole | owner、admin、member |
GroupType | private、public |
MemberInvite | free、approval、disabled |
JoinMode | approval、free |
GroupRequestKind | application、inviteReview(invite_review)、invitation |
GroupRequestStatus | pending、accepted、declined、canceled、expired |
InviteMode | allowAny(allow_any)、needConfirm(need_confirm) |
每个枚举的 wire 为服务端的取值。
