聊天室
聊天室适合直播间、语聊房、活动互动区:没有固定成员,用户打开页面时进入、离开页面时离开,一个聊天室可以有几十万人同时在线。消息只推给此刻在聊天室里的用户,不进入会话列表,不计未读数,也没有离线推送;刚进入的用户可以看到最近的几十条消息。
典型的用法是:你的服务端在直播开始时创建聊天室,把聊天室 ID 交给 App;直播间页面调用 im.chatrooms.enter() 进入,得到一个 ChatroomHandle,用它读取聊天室信息、显示消息、发送消息;离开页面时调用 leave()。
import 'package:deeprespond_im/deeprespond_im.dart';
final room = await im.chatrooms.enter('100310941534519296');
room.stream.listen((s) {
print('${s.info.name}(${s.info.memberCount} 人)共 ${s.messages.length} 条消息');
});
await room.send(const OutgoingContent.text('主播晚上好'));聊天室的完整规则(人数上限、身份、消息的优先级与限流、最近的消息、属性)见服务端文档聊天室管理,本页只说明在 Flutter App 中怎么用。聊天室的消息和状态只在内存中,不写入本地数据库。
查找聊天室
im.chatrooms.list() 列出应用中对客户端公开的聊天室(服务端创建时 listed 为 true、状态正常的),可以按名称前缀搜索;不公开的聊天室只能按 ID 进入。im.chatrooms.get() 查询一个聊天室的信息,不需要先进入。
final page = await im.chatrooms.list(namePrefix: '周末', limit: 20);
for (final item in page.items) {
print('${item.roomId} ${item.name} ${item.memberCount}');
}
// 还有下一页时:im.chatrooms.list(namePrefix: '周末', cursor: page.nextCursor)在列表页上同时显示多个聊天室的在线人数,用 onlineCounts() 一次查询,最多 100 个:
final counts = await im.chatrooms.onlineCounts(['100310941534519296', '100311027031212032']);
for (final c in counts) {
print('${c.roomId}:${c.memberCount} 人在线'); // 不存在和已解散的不在结果中
}在线人数按用户计算(同一用户多台设备只算一次),最多缓存 5 秒,是近似值,适合“1.2 万人在线”这样的展示。
进入聊天室
enter() 在进入成功后兑现,返回这个聊天室的句柄。进入时可以带一段最长 512 字节的 ext(如“VIP3”),随进入的通知发给其他成员。
try {
final room = await im.chatrooms.enter('100310941534519296', ext: 'VIP3');
print(room.snapshot.state); // in
} on DRException catch (e) {
if (e.reason == 'chatroom_banned') {
showToast('你已被禁止进入该直播间');
} else if (e.code == 'not_found') {
showToast('直播已结束');
} else {
rethrow;
}
}进入的条件:聊天室存在且正常;你没有被这个聊天室封禁;聊天室没有满员(所有者和管理员不受人数上限的限制)。
- 多次进入同一个聊天室:对同一个聊天室多次调用
enter(),得到的是同一个句柄(ext、leaveOthers只在第一次生效),SDK 记下进入的次数,leave()调用同样的次数后才真正离开。App 中的几个页面或组件可以各自进入和离开同一个聊天室,互不影响。 - 进入的数量:同一台设备同时进入的聊天室数有上限(默认 10 个),超出时
enter()以limit_exceeded(connection_room_limit)拒绝。 - 进入太频繁:同一时刻大量用户进入同一个聊天室时,服务端会让一部分人稍后再进入,SDK 自动等待并重试,
enter()可能需要几秒才兑现。 - 还没连上长连接:进入必须经过长连接,长连接没有就绪时
enter()等待,最长 15 秒,超时以not_connected拒绝。
切换直播间
在直播间之间切换(如上下滑动切换)时,进入新的聊天室时带上 leaveOthers: true,会同时离开本 App 进入的其他聊天室,旧的句柄的 state 变为 left:
final next = await im.chatrooms.enter('100311027031212032', leaveOthers: true);离开聊天室
final room = await im.chatrooms.enter('100310941534519296');
// ……
await room.leave();leave()立即离开,其他成员会收到离开的通知(人数不多的聊天室)。没有调用leave()而连接断开的,断开 15 秒后服务端判定你已离开。- 直播间页面关闭(
State.dispose)时请调用leave(),见下面的在页面中使用。 - 离开之后句柄就结束了:快照的
state为left,不再变化,再调用send()等方法以invalid_state拒绝。要再次进入,重新调用enter(),会得到新的句柄。 im.chatrooms.entered是本 App 当前进入的聊天室(聊天室 ID 到句柄)。- 退出登录时,全部句柄结束,
state为left。
在页面中使用
句柄的 stream 在订阅时先发出当前快照,之后每次变化发出新快照,可以直接交给 StreamBuilder。快照不可修改,没有变化的消息沿用原来的对象。下面是一个直播间页面的骨架:进入、显示、发送、点赞,页面关闭时离开。
import 'dart:async';
import 'package:deeprespond_im/deeprespond_im.dart';
import 'package:flutter/material.dart';
class LiveRoomPage extends StatefulWidget {
const LiveRoomPage({super.key, required this.im, required this.roomId});
final DRClient im;
final String roomId;
@override
State<LiveRoomPage> createState() => _LiveRoomPageState();
}
class _LiveRoomPageState extends State<LiveRoomPage> {
late final Future<ChatroomHandle> _room = widget.im.chatrooms.enter(widget.roomId);
@override
void dispose() {
// 进入失败的不需要离开
unawaited(_room.then((room) => room.leave(), onError: (Object _) {}));
super.dispose();
}
@override
Widget build(BuildContext context) => FutureBuilder<ChatroomHandle>(
future: _room,
builder: (context, entered) {
if (entered.hasError) return const Center(child: Text('进入失败'));
final room = entered.data;
if (room == null) return const Center(child: Text('正在进入…'));
return StreamBuilder<ChatroomSnapshot>(
stream: room.stream,
initialData: room.snapshot,
builder: (context, s) {
final snap = s.data!;
return Column(
children: [
Text('${snap.info.name}(${snap.info.memberCount} 人在线)'),
if (snap.state == 'reentering') const Text('正在重新连接…'),
Expanded(
child: ListView(
children: [
for (final m in snap.messages)
ListTile(
title: Text(m.senderNickname ?? m.sender ?? '系统'),
subtitle: Text(m.type == 'text' ? '${m.body?['text']}' : '[消息]'),
),
],
),
),
Row(
children: [
TextButton(onPressed: () => room.send(const OutgoingContent.text('666')), child: const Text('发送')),
IconButton(onPressed: room.like, icon: const Icon(Icons.favorite)),
],
),
],
);
},
);
},
);
}切到后台与断线后的重新进入
聊天室属于长连接:进入的状态保存在服务端这条连接上。
- 网络断开又恢复、或在 Wi-Fi 与移动网络之间切换:SDK 立即建立新的长连接,并自动重新进入断开前所在的聊天室,句柄不变,你不需要做任何处理。重新进入期间快照的
state为reentering,可以显示“正在重新连接”;成功后回到in,并重新获取聊天室信息、属性和最近的消息。在 15 秒内重新进入的,其他成员看不到你离开再进入。 - 切到后台:长连接保持,你仍在聊天室中,照常收到消息,
state保持in。 - 后台被系统断开连接:系统挂起 App 或断开网络后连接关闭,SDK 在后台不重连;15 秒后服务端判定你已离开,其他成员收到离开的通知,你设置的
autoDelete属性(如麦位)被删除。回到前台后 SDK 重新连接并自动重新进入,state先为reentering再回到in,其他成员看到你重新进入。
断开期间的消息不会补发:聊天室消息只推给此刻在线的连接。重新进入后获取的最近消息会与已有的消息合并、去重。
断开期间如果你被移出、被封禁,或聊天室已解散、已被封禁,重新进入不会成功,句柄按被移出处理。
直播类 App 如果不希望用户在后台时继续占着聊天室的名额(或麦位),可以在切到后台时自己调用 leave(),回到前台时重新 enter():
import 'package:flutter/widgets.dart';
class RoomLifecycle {
RoomLifecycle(this.roomId) {
_listener = AppLifecycleListener(onHide: _onHide, onShow: _onShow);
}
final String roomId;
late final AppLifecycleListener _listener;
ChatroomHandle? room;
Future<void> _onHide() async {
await room?.leave();
room = null;
}
Future<void> _onShow() async {
room ??= await im.chatrooms.enter(roomId);
}
void dispose() => _listener.dispose();
}被移出聊天室
你被所有者、管理员或你的服务端移出、封禁,或者聊天室被解散、被封禁时,句柄发出 ChatroomRemovedEvent 事件,快照的 state 变为 removed,removed 中是原因。SDK 不会自动重新进入。
final room = await im.chatrooms.enter('100310941534519296');
room.on<ChatroomRemovedEvent>().listen((e) {
final r = e.removed;
final until = r.expiresAt;
final text = switch (r.reason) {
'kicked' => '你已被移出直播间',
'banned' => until != null ? '你已被禁止进入,至 ${until.toLocal()}' : '你已被永久禁止进入',
'dismissed' => '直播已结束',
'disabled' => '该直播间已被关闭',
_ => '你已离开直播间',
};
showToast(text);
});reason | 说明 |
|---|---|
kicked | 被移出,operator 为操作人的用户名(你的服务端操作时为 null)。可以再次进入 |
banned | 被封禁,expiresAt 为封禁的到期时间,永久封禁为 null。到期前不能再进入 |
dismissed | 聊天室已解散 |
disabled | 聊天室已被封禁 |
聊天室信息与在线人数
快照的 info 是聊天室的信息:名称、简介、封面、公告、所有者、管理员、在线人数、是否全员禁言等,见 ChatroomInfo。资料变化时(包括全员禁言、所有者和管理员的变化)SDK 自动更新快照:
final room = await im.chatrooms.enter('100310941534519296');
final sub = room.stream.listen((s) {
final info = s.info;
print('${info.name} ${info.avatarUrl} 在线 ${info.memberCount} 全员禁言 ${info.muteAll}');
});
// 不再需要时 await sub.cancel()info.self 是你在这个聊天室中的身份和状态(ChatroomSelf):
| 字段 | 说明 |
|---|---|
role | owner 所有者 / admin 管理员 / member 普通用户 |
muted、mutedUntil | 你是否被禁言、禁言的到期时间(永久禁言为 null)。禁言到期时 SDK 自动恢复为 false |
allowlisted | 你是否在白名单中:全员禁言时白名单中的用户仍然可以发言 |
在线人数 memberCount 在进入时取得;人数不多的聊天室(不超过运行策略 chatroom_member_notify_limit,默认 100 人)在有人进出时随通知更新。人数更多的聊天室不推送进出通知,需要定时刷新在线人数时,调用 refresh() 或 im.chatrooms.get():
final room = await im.chatrooms.enter('100310941534519296');
final timer = Timer.periodic(const Duration(seconds: 30), (_) {
unawaited(room.refresh().catchError((Object _) {}));
});
// 离开时 timer.cancel()接收消息
快照的 messages 是进入后获取的最近的消息,加上之后收到和发出的消息,按到达的顺序排列,最多保留 500 条(更早的自动丢弃)。通常直接按 messages 构建消息列表(见在页面中使用)。
需要对每条新消息做处理(如播放礼物特效)时,监听 ChatroomMessageReceived 事件:
final room = await im.chatrooms.enter('100310941534519296');
room.on<ChatroomMessageReceived>().listen((e) {
final m = e.message;
if (m.type == 'custom' && m.body?['gift'] != null) {
print('${m.senderNickname} 送出了 ${m.body!['gift']}');
}
});- 管理员和官方消息:
senderRole为发送时的身份(owner、admin、member),可以据此显示“房管”标识;local.trusted为true的消息由你的服务端、控制台或系统发出,不是用户在客户端发送的,可以显示为官方消息。不要根据ext中的内容判断,ext由发送者自己填写。 - 系统消息:
senderType为system的消息,sender、senderNickname、senderAvatarUrl、senderRole都为null。 - 消息过多时:热闹的聊天室中,服务端每秒推给每个连接的消息有上限,超出时先丢弃低优先级的,再丢弃普通的(高优先级的正常情况下不会丢弃)。有消息被丢弃时句柄发出
ChatroomDropped事件,可以提示“消息过多,已省略部分”:
room.on<ChatroomDropped>().listen((e) {
print('消息过多,已省略 ${e.count} 条');
});- 撤回:消息被撤回时,SDK 把它从
messages中去掉,并发出ChatroomMessageRecalled事件; - 不保证送达和顺序:网络异常时消息可能丢失,不同用户发出的消息到达的先后可能略有不同。需要可靠送达的内容(如礼物结算)请以高优先级发送,并由你的业务另行保证。
room.events 是句柄全部事件的流(Stream<ChatroomEvent>,ChatroomEvent 是密封类),可以用 switch 一起处理;room.on<T>() 只取一种。
最近的消息
进入聊天室后,SDK 自动获取最近的消息并放在 messages 的前面,你不需要另外调用。最近的消息只有普通和高优先级的,最多 chatroom_history_size 条(运行策略,默认 50 条),只保留 24 小时;运行策略设为 0 时不保存,进入后 messages 从空开始。最近的消息不能向前翻页。
收到服务端的提示、可能漏收了内容时,SDK 会自动重新获取聊天室信息、属性和最近的消息,并与已有的消息合并、去重。你也可以调用 refresh() 手动刷新。
发送消息
send() 发送一条消息,支持文本、位置、自定义消息,以及已上传的图片、语音、视频、文件,内容的格式与单聊、群聊消息相同,见消息格式。
final room = await im.chatrooms.enter('100310941534519296');
final sent = await room.send(const OutgoingContent.text('唱得太好了!'));
print('${sent.messageId} ${sent.local.status}'); // sent发送时,普通和高优先级的消息先以 sending 状态出现在 messages 中,成功后变为 sent,失败时变为 failed 并以错误拒绝。网络错误、超时时 SDK 自动重试,1 分钟内结束;被限流(rate_limited)时不自动重试,请提示用户“发送太频繁”。在进入完成之前调用的,等进入成功后再发送;长连接没有就绪时最多等待 15 秒。
优先级
每条聊天室消息有一个优先级(ChatroomPriority),决定消息过多时是否会被限流或丢弃:
priority | 用途 | 谁可以发送 | 发送时超出聊天室的额度 |
|---|---|---|---|
high | 系统通知、管理员公告、礼物结算等不能丢的消息 | 所有者、管理员 | 以 rate_limited 拒绝 |
normal | 普通聊天,默认 | 所有人 | 以 rate_limited 拒绝 |
low | 点赞、弹幕特效、进场特效等丢了无妨的高频消息 | 所有人 | 静默丢弃,返回的消息 local.status 为 dropped,不必提示用户 |
final room = await im.chatrooms.enter('100310941534519296');
// 进场特效:低优先级
await room.send(const OutgoingContent.custom({'effect': 'enter', 'level': 3}), priority: ChatroomPriority.low);
// 管理员公告:高优先级,普通用户发送以 permission_denied(priority_denied)拒绝
await room.send(const OutgoingContent.text('请文明发言'), priority: ChatroomPriority.high);低优先级的消息不先出现在 messages 中,也不自动重试;服务端推回来后才出现在 messages 中,被丢弃的不会出现。
每个聊天室每秒接受的普通和低优先级消息数由运行策略 chatroom_msg_per_second 决定(默认 40 条),见消息的优先级与下发。
大小上限与图片
一条聊天室消息(类型、内容和 ext 合计)不能超过运行策略 max_chatroom_message_bytes(默认 2 KB),超出时 SDK 在本地以 local_validation(too_large)拒绝。
发送图片等附件时,先上传文件,再用 OutgoingContent.uploaded 把地址写进消息。聊天室不支持直接传入文件,OutgoingContent.image(DRFile(...)) 这类内容以 unsupported(chatroom_attachment)拒绝:
Future<void> sendImage(ChatroomHandle room, String path) async {
final info = await im.files.upload(DRFile(path), purpose: UploadPurpose.attachment, kind: FileKind.image).result;
await room.send(OutgoingContent.uploaded('image', {
'url': info.url,
if (info.width != null) 'width': info.width,
if (info.height != null) 'height': info.height,
'size': info.size,
}));
}上传和显示附件见文件与图片。
禁言
你被禁言(info.self.muted 为 true),或聊天室开启了全员禁言(info.muteAll 为 true)而你不是所有者、管理员、也不在白名单中时,不能发送消息和设置属性,发送以 chatroom_muted 拒绝,details['reason'] 为 member(你被禁言)或 all(全员禁言)。请据快照禁用输入框:
bool canSpeak(ChatroomSnapshot s) {
final self = s.info.self;
final privileged = self?.role == 'owner' || self?.role == 'admin' || self?.allowlisted == true;
return self?.muted != true && (!s.info.muteAll || privileged);
}禁言、解除禁言、全员禁言的变化会实时更新快照;禁言到期时 SDK 在本地自动恢复。用户被全局禁言(聊天室场景)时,发送以 user_muted 拒绝,见用户、好友与在线状态。
点赞
点赞这类高频操作不要每点一次发一条消息。like() 把一秒内的多次点赞合并成一条低优先级的消息发送,不等待结果,也不抛出错误:
// 用户每点一次
room.like();
// 其他人的点赞(同一个人一秒内的合并为一次)
room.on<ChatroomLike>().listen((e) {
print('${e.username} 点了 ${e.count} 个赞');
});点赞不会出现在 messages 中,只以 ChatroomLike 事件交给你。点赞是低优先级的消息,聊天室消息过多时可能被丢弃。
撤回消息
final room = await im.chatrooms.enter('100310941534519296');
final sent = await room.send(const OutgoingContent.text('发错了'));
final id = sent.messageId;
if (id != null) {
final changed = await room.recall(id);
}- 普通用户可以在运行策略
recall_window_seconds(默认 120 秒)内撤回自己的消息; - 管理员可以撤回发送者现在是普通用户的消息,所有者可以撤回任何人的消息;
- 只能撤回还在最近的消息中的消息,已经不在的以
not_found拒绝。
撤回后,所有人的 messages 中这条消息被去掉,并发出 ChatroomMessageRecalled 事件:
room.on<ChatroomMessageRecalled>().listen((e) {
final by = e.recalledBy;
print(by != null ? '$by 撤回了一条消息' : '一条消息已被撤回');
});成员列表
members() 分页返回此刻在聊天室里的用户,按进入时间从早到晚排列,每页最多 100 个。翻页时 SDK 已按用户名去重(不带 cursor 从第一页开始时重新计)。
final room = await im.chatrooms.enter('100310941534519296');
var page = await room.members(limit: 50);
print(page.items.map((m) => '${m.nickname.isNotEmpty ? m.nickname : m.username}(${m.role})'));
final next = page.nextCursor;
if (next != null) {
page = await room.members(cursor: next, limit: 50);
}人数不多的聊天室(不超过运行策略 chatroom_member_notify_limit,默认 100 人),有人进入和离开时句柄发出 ChatroomMemberJoined、ChatroomMemberLeft 事件,同时更新快照中的在线人数。人数更多的聊天室不推送这两个事件;需要显示“某某进入直播间”时,请让进入的用户自己发一条低优先级的自定义消息。
room.on<ChatroomMemberJoined>().listen((e) {
final nickname = e.data['nickname'];
print('${nickname is String && nickname.isNotEmpty ? nickname : e.username} 进入了直播间,ext:${e.data['ext']}');
});
room.on<ChatroomMemberLeft>().listen((e) {
print('${e.username} 离开了直播间(${e.data['reason']})');
});两个事件都有 username,data 是通知的原始内容:
| 事件 | data 的字段 |
|---|---|
ChatroomMemberJoined | room_id、username、nickname、avatar_url、role、ext(进入时带的 ext)、joined_at、member_count |
ChatroomMemberLeft | room_id、username、reason(left 主动离开 / disconnected 断开 / kicked 被移出 / banned 被封禁)、member_count |
聊天室属性
属性是聊天室的自定义键值对,全体成员可见,常用于语聊房的麦位(如 seat_1 为 zhangsan)、直播间的玩法状态。快照的 attributes 是当前的全部属性(键到 ChatroomAttribute),变化时 SDK 自动更新快照。
final room = await im.chatrooms.enter('100310941534519296');
room.stream.listen((s) {
print('1 号麦:${s.attributes['seat_1']?.value ?? '空位'}');
});设置和删除属性
final room = await im.chatrooms.enter('100310941534519296');
// 上麦:离开聊天室(包括断线 15 秒后)时自动删除,麦位不会一直被占着
final result = await room.setAttributes({'seat_1': 'zhangsan'}, autoDelete: true);
if (result.failed.containsKey('seat_1')) {
showToast('这个麦位已经有人了');
}
// 下麦
await room.removeAttributes(['seat_1']);- 谁能修改:在聊天室里的用户可以设置和删除没人设置过的键和自己设置的键;别人设置的键在
failed中返回permission_denied(attribute_owned),其他键照常写入。所有者和管理员带上force: true可以覆盖和删除别人(包括你的服务端)设置的键,普通用户带force以permission_denied(role_required)拒绝。 - 禁言:被禁言的用户,以及全员禁言时不在白名单中的普通用户,不能设置属性(以
chatroom_muted拒绝),可以删除自己设置的属性。 - 格式:每个聊天室最多 100 个键。键为 1 到 128 个字符,只能包含字母、数字和
_、-、.、:;值为字符串,最长 4096 字节。一次最多设置或删除 20 个键,一次设置的键和值合计不超过 16 KB,超出时 SDK 在本地以local_validation拒绝。 - 频率:每个聊天室每秒最多 20 次属性写入。每秒变化多次的状态(如实时点赞数)不适合放在属性中,请用低优先级的自定义消息。
- 设置后,属性的值还会经过内容安全检查,违规的属性会被删除。
公告
公告是快照中的 info.announcement,每次修改都会更新 announcementUpdatedAt。要提示“公告已更新”,记下用户看过的公告时间(如保存在 shared_preferences 中),与它比较:
DateTime? seenAnnouncementAt; // 从持久化存储中读取
void checkAnnouncement(ChatroomSnapshot s, void Function(String text) show) {
final info = s.info;
final updatedAt = info.announcementUpdatedAt;
if (info.announcement.isNotEmpty && updatedAt != null && updatedAt != seenAnnouncementAt) {
show(info.announcement);
seenAnnouncementAt = updatedAt; // 同时写回持久化存储
}
}所有者和管理员可以修改公告:
await room.manage.update(const ChatroomPatch(announcement: '今晚 8 点开播,请文明发言'));管理聊天室
所有者和管理员可以在客户端管理聊天室,接口在句柄的 manage 下(需要先进入聊天室)。身份不满足时以 permission_denied(role_required,details['required_role'] 为需要的身份)拒绝。普通用户的界面中请隐藏这些入口,按 info.self?.role 判断。
| 操作 | 方法 | 所有者 | 管理员 |
|---|---|---|---|
| 修改名称、简介、封面、公告 | manage.update() | 可以 | 可以 |
| 修改人数上限、是否出现在列表中 | manage.update() | 可以 | 不可以 |
| 开启、关闭全员禁言 | manage.setMuteAll() | 可以 | 可以 |
| 禁言、解除禁言,查看禁言名单 | manage.mute()、unmute()、mutes() | 可以 | 只能处置普通用户 |
| 封禁、解除封禁,查看封禁名单 | manage.ban()、unban()、bans() | 可以 | 只能处置普通用户 |
| 移出 | manage.kick() | 可以 | 只能移出普通用户 |
| 管理白名单 | manage.allowlist | 可以 | 可以 |
| 设置、取消管理员 | manage.setAdmin() | 可以 | 不可以 |
| 转让所有者 | manage.transferOwner() | 可以 | 不可以 |
| 解散聊天室 | manage.dismiss() | 可以 | 不可以 |
| 发送高优先级的消息 | send() | 可以 | 可以 |
| 撤回别人的消息 | recall() | 任何人的 | 普通用户的 |
- 没有人能禁言、移出或封禁所有者(
owner_protected);管理员不能处置其他管理员(admin_protected)。 - 解除禁言和封禁时,管理员只能解除管理员设置的,所有者和你的服务端设置的只有所有者能解除(
set_by_higher_role)。 - 聊天室被封禁期间,客户端的管理操作都以
chatroom_disabled拒绝。 - 普通用户可以进入、发送普通和低优先级的消息、查看成员和最近的消息、设置属性、撤回自己的消息。
禁言、封禁与移出
禁言、封禁、移出和白名单都是批量操作,一次最多 100 人,逐个处理,某个用户失败不影响其他用户,返回每个用户的结果(ChatroomMemberResult):
final room = await im.chatrooms.enter('100310941534519296');
// 禁言 10 分钟;省略 durationSeconds 为永久禁言
final results = await room.manage.mute(['spammer01', 'spammer02'], durationSeconds: 600, reason: '刷屏');
for (final r in results) {
if (!r.ok) showToast('${r.username}:${r.code}'); // 如 permission_denied(owner_protected)
}
await room.manage.unmute(['spammer02']);
// 封禁:在聊天室里的会被立即移出,到期前不能再进入
await room.manage.ban(['troll01'], durationSeconds: 24 * 3600);
// 移出:之后可以再次进入
await room.manage.kick(['troll02']);- 禁言:不能发送消息和设置属性,仍然可以留在聊天室里、接收消息。时长 1 秒到 10 年,或永久,到期自动解除。
- 封禁:在聊天室里的被移出,封禁期间不能进入。封禁管理员时,他同时不再是管理员。
- 移出:只是让他离开,他可以再次进入;要阻止再次进入请用封禁。
- 三个名单(禁言、封禁、白名单)与用户是否在聊天室里无关,可以对此刻不在聊天室里的用户操作。
查看名单(只列出未到期的):
final mutes = await room.manage.mutes(limit: 20);
for (final entry in mutes.items) {
final name = entry.nickname.isNotEmpty ? entry.nickname : entry.username;
print('$name 禁言到 ${entry.mutedUntil?.toLocal() ?? '永久'}');
}全员禁言与白名单
final room = await im.chatrooms.enter('100310941534519296');
await room.manage.setMuteAll(true);
// 嘉宾在全员禁言时仍然可以发言
await room.manage.allowlist.add(['guest01']);
await room.manage.allowlist.remove(['guest01']);
await room.manage.setMuteAll(false);全员禁言时,所有者、管理员和白名单中的用户仍然可以发言;被单独禁言的用户即使在白名单中也不能发言。白名单最多 500 人。
修改资料
manage.update() 只修改 ChatroomPatch 中给出的字段,返回修改后的聊天室信息。可以带上读取到的 info.infoVersion 作为 version,防止覆盖别人同时做的修改,不一致时以 version_conflict 拒绝。
final room = await im.chatrooms.enter('100310941534519296');
final info = room.snapshot.info;
await room.manage.update(
const ChatroomPatch(name: '周末音乐会(返场)', description: '每周六晚八点'),
version: info.infoVersion,
);修改封面时,先以用途 chatroom_avatar 上传图片:
Future<void> changeCover(ChatroomHandle room, String path) async {
final uploaded = await im.files.upload(DRFile(path), purpose: UploadPurpose.chatroomAvatar).result;
await room.manage.update(ChatroomPatch(avatarUrl: uploaded.url));
}名称、简介和公告会经过内容安全检查,不通过的以 content_rejected 拒绝(details['field'] 为不通过的字段)。
管理员、所有者与解散
final room = await im.chatrooms.enter('100310941534519296');
await room.manage.setAdmin('lisi', true); // 设置管理员(最多 99 个)
await room.manage.setAdmin('lisi', false); // 取消管理员
await room.manage.transferOwner('wangwu'); // 转让后你成为普通用户所有者可以解散聊天室,解散后所有人(包括自己)被移出,ChatroomRemovedEvent 的原因为 dismissed,不能恢复:
await room.manage.dismiss();在客户端创建聊天室
默认只能由你的服务端或在控制台中创建聊天室。应用在运行策略中开启 client_chatroom_create_enabled 后,用户可以在客户端创建,创建者成为所有者。没有开启时 create() 以 permission_denied(client_create_disabled)拒绝,请按运行配置决定是否显示入口:
if (im.config?.clientChatroomCreateEnabled == true) {
final info = await im.chatrooms.create(name: '我的直播间', description: '欢迎来玩', listed: true);
final room = await im.chatrooms.enter(info.roomId);
}应用中的聊天室数有上限,达到时以 limit_exceeded(chatroom_limit)拒绝。
接口参考
im.chatrooms.list()
分页列出对客户端公开(listed 为 true)、状态正常的聊天室,按创建时间从新到旧排列;按名称前缀搜索时按名称排列。参数都是命名参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
namePrefix | String? | 否 | 按名称前缀搜索,1 到 128 个字符 |
limit | int? | 否 | 每页条数,默认 20,最多 100 |
cursor | String? | 否 | 上一页返回的 nextCursor |
返回值:Future<DRPage<ChatroomListItem>>,nextCursor 为下一页的游标,没有下一页时为 null。
可能的错误:invalid_argument(参数不合法)、not_signed_in。
im.chatrooms.get()
查询一个聊天室的信息,含当前在线人数和你在其中的身份 self,不需要先进入。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
roomId | String | 是 | 聊天室 ID |
返回值:Future<ChatroomInfo>。
可能的错误:not_found(聊天室不存在或已解散)、local_validation(roomId 为空)。
im.chatrooms.onlineCounts()
批量查询在线人数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
roomIds | List<String> | 是 | 聊天室 ID,1 到 100 个 |
返回值:Future<List<ChatroomOnlineCount>>,按请求的顺序;不存在和已解散的聊天室不在结果中,已封禁的为 0。
可能的错误:local_validation(roomIds 为空或超过 100 个)。
im.chatrooms.create()
在客户端创建聊天室,你成为所有者。需要应用开启 client_chatroom_create_enabled。参数都是命名参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | String | 是 | 名称,1 到 128 个字符 |
description | String? | 否 | 简介,最长 512 个字符 |
avatarUrl | String? | 否 | 封面地址,以用途 chatroom_avatar 上传后得到 |
announcement | String? | 否 | 公告,最长 2048 个字符 |
maxMembers | int? | 否 | 人数上限,1 到 500000;省略时跟随运行策略 |
listed | bool? | 否 | 是否出现在聊天室列表中,默认 true |
返回值:Future<ChatroomInfo>。创建后需要调用 enter() 才进入。
可能的错误:permission_denied(client_create_disabled,应用没有开启客户端创建,在本地判断)、limit_exceeded(chatroom_limit)、content_rejected(名称、简介或公告没有通过内容安全检查)、invalid_argument(如封面地址不被允许 url_not_allowed)、app_unavailable(应用只读)、local_validation(名称为空)。
im.chatrooms.enter()
进入聊天室,成功后兑现。对同一个聊天室多次调用返回同一个句柄,进入次数加一。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
roomId | String | 是 | 聊天室 ID |
ext | String? | 否 | 命名参数。这次进入的附加信息,最长 512 字节,随进入的通知发给其他成员。只在第一次进入时生效 |
leaveOthers | bool | 否 | 命名参数。为 true 时同时离开本 App 进入的其他聊天室,用于切换直播间;默认 false |
返回值:Future<ChatroomHandle>。
可能的错误:not_found(聊天室不存在或已解散)、chatroom_disabled(聊天室已被封禁)、permission_denied(chatroom_banned,你被封禁,details['expires_at'] 为到期时间,永久为 null)、limit_exceeded(chatroom_member_limit 聊天室已满;connection_room_limit 同时进入的聊天室已达上限)、rate_limited(进入太频繁,SDK 等待时间不超过 60 秒的会自动重试)、not_connected(长连接 15 秒内没有就绪)、local_validation(roomId 为空或 ext 超长)、not_signed_in。
im.chatrooms.entered
只读属性,Map<String, ChatroomHandle>(不可修改):本 App 当前进入的聊天室,聊天室 ID 到句柄。
ChatroomHandle.snapshot
只读属性:句柄当前的快照(不可修改,变化时整体替换),见 ChatroomSnapshot。
ChatroomHandle.stream
Stream<ChatroomSnapshot>:订阅时先发出当前快照,之后每次变化发出新快照。可以多处订阅;不再需要时取消订阅。
ChatroomHandle.events 与 on()
events 为句柄的全部事件(Stream<ChatroomEvent>);on<T extends ChatroomEvent>() 返回按类型过滤的 Stream<T>,如 room.on<ChatroomLike>()。
| 事件 | 字段 | 说明 |
|---|---|---|
ChatroomMessageReceived | message(ChatroomMessageView) | 收到新消息,或你发送的消息被服务端确认 |
ChatroomMessageRecalled | messageId、recalledBy(String?)、recalledAt(DateTime?) | 消息被撤回,recalledBy 为操作人,你的服务端撤回时为 null |
ChatroomMemberJoined | username、data | 有人进入(只在人数不多的聊天室推送),data 的字段见成员列表 |
ChatroomMemberLeft | username、data | 有人离开(只在人数不多的聊天室推送) |
ChatroomLike | username、count | 有人点赞 |
ChatroomDropped | count | 消息过多,服务端丢弃了 count 条 |
ChatroomRemovedEvent | removed(ChatroomRemoved) | 你被移出,见被移出聊天室 |
ChatroomHandle.send()
发送一条聊天室消息。在进入完成之前调用的,等进入成功后再发送。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
content | OutgoingContent | 是 | 消息内容:text、location、custom,或 uploaded(已上传的附件)。不支持直接传入文件 |
priority | ChatroomPriority | 否 | 命名参数。优先级,默认 normal。high 只有所有者和管理员能用 |
ext | Map<String, Object?>? | 否 | 命名参数。扩展字段 |
返回值:Future<ChatroomMessageView>,发送成功的消息;低优先级被丢弃时 local.status 为 dropped、messageId 为 null。
可能的错误:chatroom_muted(被禁言或全员禁言,details['reason'] 为 member 或 all,details['muted_until'] 为到期时间)、user_muted(被全局禁言)、permission_denied(priority_denied,普通用户发送高优先级的消息)、rate_limited(user_send_rate 你发送太频繁;room_send_rate 聊天室消息太多)、message_rejected(被内容安全或你的发送前回调拒绝)、payload_too_large、local_validation(内容不合法,如 too_large 超过 max_chatroom_message_bytes)、unsupported(chatroom_attachment,直接传入了文件)、not_chatroom_member、chatroom_disabled、not_found、app_unavailable、not_connected、invalid_state(句柄已结束)。
ChatroomHandle.like()
点赞。一秒内的多次点赞合并成一条低优先级的消息发送,不返回结果,也不抛出错误。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
count | int | 否 | 可选的位置参数。这次点赞的次数,正整数,默认 1 |
返回值:void。
ChatroomHandle.setAttributes()
设置聊天室属性。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
attributes | Map<String, String> | 是 | 键到值,1 到 20 个,键和值合计不超过 16 KB |
autoDelete | bool | 否 | 命名参数。为 true 时这些键在你离开聊天室时自动删除,默认 false |
force | bool | 否 | 命名参数。覆盖别人设置的键,只有所有者和管理员能用,默认 false |
返回值:Future<AttributeResult>。部分键失败时不抛出错误,失败的键在 failed 中。
可能的错误:chatroom_muted、permission_denied(role_required,普通用户带 force)、rate_limited(attribute_rate、room_attribute_rate)、invalid_argument(invalid_attribute_key、invalid_attribute_value)、local_validation(键数或大小超出)、not_chatroom_member、chatroom_disabled、app_unavailable、not_connected、invalid_state。
ChatroomHandle.removeAttributes()
删除聊天室属性。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
keys | List<String> | 是 | 要删除的键,1 到 20 个;不存在的键忽略 |
force | bool | 否 | 命名参数。删除别人设置的键,只有所有者和管理员能用,默认 false |
返回值:Future<AttributeResult>。
可能的错误:同 setAttributes(),被禁言时也可以删除自己设置的属性。
ChatroomHandle.recall()
撤回一条聊天室消息。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
messageId | String | 是 | 消息 ID |
返回值:Future<bool>,是否有变化;已经撤回过的为 false。
可能的错误:permission_denied(recall_window_expired 超过可撤回的时间;recall_denied 无权撤回这条消息)、not_found(消息已不在最近的消息中)、local_validation(messageId 为空)、invalid_state。
ChatroomHandle.members()
分页返回此刻在聊天室里的用户,按进入时间从早到晚排列,已按用户名去重。参数都是命名参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
limit | int? | 否 | 每页条数,最多 100 |
cursor | String? | 否 | 上一页返回的 nextCursor;不带时从第一页开始 |
返回值:Future<DRPage<ChatroomMember>>。
可能的错误:not_chatroom_member、invalid_state。
ChatroomHandle.refresh()
重新获取聊天室信息(含在线人数)、属性和最近的消息,更新快照。
返回值:Future<void>。
可能的错误:not_found、invalid_state。
ChatroomHandle.leave()
离开聊天室。进入次数减一,减到 0 时真正离开,之后句柄结束。进入还没完成时调用的,等进入完成后再处理。句柄已结束时调用没有作用。
返回值:Future<void>,不会失败。
ChatroomHandle.manage.update()
修改聊天室资料,只修改给出的字段。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
patch | ChatroomPatch | 是 | 要修改的字段,见 ChatroomPatch |
version | int? | 否 | 命名参数。读取到的 info.infoVersion,不一致时不修改 |
返回值:Future<ChatroomInfo>,修改后的信息。
可能的错误:permission_denied(role_required)、version_conflict、content_rejected、invalid_argument、chatroom_disabled、app_unavailable、invalid_state。
ChatroomHandle.manage.setMuteAll()
开启或关闭全员禁言(所有者、管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
on | bool | 是 | true 开启,false 关闭 |
返回值:Future<void>。
可能的错误:permission_denied(role_required)、chatroom_disabled、invalid_state。
ChatroomHandle.manage.setAdmin()
设置或取消管理员(所有者)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | String | 是 | 用户名,不要求他在聊天室里 |
admin | bool | 是 | true 设置,false 取消 |
返回值:Future<void>。
可能的错误:permission_denied(role_required;target_banned 对方在封禁中)、limit_exceeded(admin_limit,管理员已有 99 个)、not_found(用户不存在)、chatroom_disabled、invalid_state。
ChatroomHandle.manage.transferOwner()
把所有者转让给另一个用户(所有者),你成为普通用户。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | String | 是 | 新所有者的用户名,不要求他在聊天室里 |
返回值:Future<void>。
可能的错误:permission_denied(role_required)、not_found、chatroom_disabled、invalid_state。
ChatroomHandle.manage.mute()
禁言用户(所有者、管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | List<String> | 是 | 用户名,1 到 100 个 |
durationSeconds | int? | 否 | 命名参数。时长,1 到 315360000 秒(10 年);省略表示永久 |
reason | String? | 否 | 命名参数。原因 |
返回值:Future<List<ChatroomMemberResult>>,每个用户的结果:成功的 result 为 muted 或 unchanged,失败的带 code(如 permission_denied 的 owner_protected、admin_protected,not_found,limit_exceeded 的 mute_limit)。
可能的错误:permission_denied(role_required)、chatroom_disabled、invalid_argument、local_validation(usernames 为空)、invalid_state。
ChatroomHandle.manage.unmute()
解除禁言(所有者、管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | List<String> | 是 | 用户名,1 到 100 个 |
返回值:Future<List<ChatroomMemberResult>>,成功为 unmuted 或 unchanged(原来没有禁言),失败如 permission_denied(set_by_higher_role)。
可能的错误:同 mute()。
ChatroomHandle.manage.ban()
封禁用户(所有者、管理员),在聊天室里的同时被移出。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | List<String> | 是 | 用户名,1 到 100 个 |
durationSeconds | int? | 否 | 命名参数。时长,1 到 315360000 秒;省略表示永久 |
reason | String? | 否 | 命名参数。原因 |
返回值:Future<List<ChatroomMemberResult>>,成功为 banned 或 unchanged,失败如 owner_protected、admin_protected、ban_limit。
可能的错误:同 mute()。
ChatroomHandle.manage.unban()
解除封禁(所有者、管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | List<String> | 是 | 用户名,1 到 100 个 |
返回值:Future<List<ChatroomMemberResult>>,成功为 unbanned 或 unchanged,失败如 set_by_higher_role。
可能的错误:同 mute()。
ChatroomHandle.manage.kick()
把用户移出聊天室(所有者、管理员),他之后可以再次进入。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | List<String> | 是 | 用户名,1 到 100 个 |
返回值:Future<List<ChatroomMemberResult>>,成功为 removed 或 not_in_room(他不在聊天室里),失败如 owner_protected、admin_protected。
可能的错误:同 mute()。
ChatroomHandle.manage.allowlist.list()
分页查询白名单(所有者、管理员)。参数都是命名参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
limit | int? | 否 | 每页条数,默认 20,最多 100 |
cursor | String? | 否 | 上一页返回的 nextCursor |
返回值:Future<DRPage<ChatroomAllowlistEntry>>。
可能的错误:permission_denied(role_required)、invalid_state。
ChatroomHandle.manage.allowlist.add()
把用户加入白名单(所有者、管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | List<String> | 是 | 用户名,1 到 100 个 |
返回值:Future<List<ChatroomMemberResult>>,成功为 added 或 unchanged,失败如 limit_exceeded(allowlist_limit,白名单已有 500 人)。
可能的错误:同 mute(),另有 app_unavailable(应用只读)。
ChatroomHandle.manage.allowlist.remove()
把用户移出白名单(所有者、管理员)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | List<String> | 是 | 用户名,1 到 100 个 |
返回值:Future<List<ChatroomMemberResult>>,成功为 removed 或 unchanged。
可能的错误:同 mute()。
ChatroomHandle.manage.mutes()
分页查询未到期的禁言(所有者、管理员)。参数同 allowlist.list()。
返回值:Future<DRPage<ChatroomMuteEntry>>。
可能的错误:permission_denied(role_required)、invalid_state。
ChatroomHandle.manage.bans()
分页查询未到期的封禁(所有者、管理员)。参数同 allowlist.list()。
返回值:Future<DRPage<ChatroomBanEntry>>。
可能的错误:permission_denied(role_required)、invalid_state。
ChatroomHandle.manage.dismiss()
解散聊天室(所有者),不能恢复。全部成员被移出,ChatroomRemovedEvent 的原因为 dismissed。
返回值:Future<void>。
可能的错误:permission_denied(role_required)、chatroom_disabled(被平台封禁的聊天室)、invalid_state。
数据结构
服务端对象(继承 DRObject)的常用字段以 getter 提供;时间为 UTC 的 DateTime。服务端返回的全部字段都在 raw(Map<String, Object?>)中。
ChatroomSnapshot
句柄的快照,不可修改。
| 字段 | 类型 | 说明 |
|---|---|---|
info | ChatroomInfo | 聊天室信息,含 self |
attributes | Map<String, ChatroomAttribute> | 全部属性,键到属性 |
attributesVersion | int | 属性的版本号,每次变化加一 |
messages | List<ChatroomMessageView> | 最近的消息和之后收到、发出的消息,按到达顺序,最多 500 条 |
state | String | entering 正在进入 / in 在聊天室中 / reentering 断线后正在重新进入 / removed 已被移出 / left 已离开 |
removed | ChatroomRemoved? | 只在 state 为 removed 时有,见下 |
ChatroomRemoved
| 字段 | 类型 | 说明 |
|---|---|---|
reason | String | kicked、banned、dismissed、disabled 等,见被移出聊天室 |
operator | String? | 操作人,只有 kicked、banned 有 |
expiresAt | DateTime? | 封禁的到期时间,只有 banned 有;永久封禁为 null |
ChatroomInfo
| 字段 | 类型 | 说明 |
|---|---|---|
roomId | String | 聊天室 ID |
name | String | 名称 |
description | String | 简介 |
avatarUrl | String | 封面地址,没有时为空字符串 |
announcement | String | 公告 |
announcementUpdatedAt | DateTime? | 公告的修改时间 |
announcementUpdatedBy | String? | 在客户端修改公告的用户;你的服务端修改时为 null |
owner | String? | 所有者的用户名,没有所有者时为 null |
admins | List<String> | 管理员的用户名 |
maxMembers | int | 实际生效的人数上限 |
muteAll | bool | 是否开启了全员禁言 |
listed | bool | 是否出现在聊天室列表中 |
status | String | active 正常 / disabled 已封禁 / dismissed 已解散 |
disabledBy | String? | 封禁方,未封禁时为 null |
memberCount | int | 在线人数,近似值 |
infoVersion | int | 资料的版本号,每次变化加一 |
attributesVersion | int | 属性的版本号 |
createdAt | DateTime? | 创建时间 |
self | ChatroomSelf? | 你在这个聊天室中的状态;列表、创建的结果中没有 |
ChatroomSelf
| 字段 | 类型 | 说明 |
|---|---|---|
role | String | owner / admin / member |
inRoom | bool | 是否在聊天室中 |
muted | bool | 是否在这个聊天室被禁言(不含全局禁言) |
mutedUntil | DateTime? | 禁言的到期时间,永久禁言为 null |
allowlisted | bool | 是否在白名单中 |
ChatroomListItem
im.chatrooms.list() 的一项。
| 字段 | 类型 | 说明 |
|---|---|---|
roomId | String | 聊天室 ID |
name | String | 名称 |
description | String | 简介 |
avatarUrl | String | 封面地址 |
owner | String? | 所有者 |
maxMembers | int | 人数上限 |
memberCount | int | 在线人数 |
ChatroomOnlineCount
| 字段 | 类型 | 说明 |
|---|---|---|
roomId | String | 聊天室 ID |
memberCount | int | 在线人数 |
ChatroomMessageView
messages 中的一条消息:服务端的聊天室消息,加上本地的状态 local。
| 字段 | 类型 | 说明 |
|---|---|---|
roomId | String | 聊天室 ID |
messageId | String? | 消息 ID;正在发送、发送失败或被丢弃的为 null |
clientMsgId | String? | 发送者生成的去重 ID |
sender | String? | 发送者的用户名,系统消息为 null |
senderType | String | user 用户 / system 系统 |
senderNickname | String? | 发送时的昵称 |
senderAvatarUrl | String? | 发送时的头像 |
senderRole | String? | 发送时的身份:owner / admin / member |
type | String | 消息类型:text、image、voice、video、file、location、custom |
body | Map<String, Object?>? | 消息内容,见消息格式 |
ext | Map<String, Object?>? | 扩展字段 |
priority | String | high / normal / low |
via | String | 发送途径:client 客户端 / openapi 你的服务端 / console 控制台 / system 系统 |
createdAt | DateTime? | 发送时间;正在发送的为 null |
local | ChatroomMessageLocal | 本地的状态,见下 |
收到的消息(ChatroomMessageReceived 事件、最近的消息)的 messageId 和 createdAt 总是有值。
ChatroomMessageLocal
| 字段 | 类型 | 说明 |
|---|---|---|
status | String | sending 正在发送 / sent 已发送 / failed 发送失败 / dropped 低优先级的消息被丢弃 |
error | DRException? | 发送失败的原因,只在 failed 时有 |
trusted | bool | 为 true 表示由你的服务端、控制台或系统发出 |
ChatroomMember
members() 的一项。
| 字段 | 类型 | 说明 |
|---|---|---|
username | String | 用户名 |
nickname | String | 昵称,没有时为空字符串 |
avatarUrl | String | 头像地址,没有时为空字符串 |
role | String | owner / admin / member |
joinedAt | DateTime? | 这一次进入的时间 |
ChatroomAttribute
一个属性。
| 字段 | 类型 | 说明 |
|---|---|---|
value | String? | 值 |
owner | String? | 最近一次设置这个键的用户;你的服务端设置的为 null |
autoDelete | bool | 是否在设置人离开聊天室时自动删除 |
updatedAt | DateTime? | 最近一次设置的时间 |
AttributeResult
setAttributes()、removeAttributes() 的结果。
| 字段 | 类型 | 说明 |
|---|---|---|
changed | bool | 是否有键被写入或删除 |
attributesVersion | int | 操作之后的属性版本号 |
failed | Map<String, Map<String, Object?>> | 失败的键到 code、reason:permission_denied(attribute_owned,键属于别人)或 limit_exceeded(attribute_limit,个数或总大小超出)。没有失败时为空 |
ChatroomMemberResult
批量管理操作中一个用户的结果:成功的有 result,失败的有 code。
| 字段 | 类型 | 说明 |
|---|---|---|
username | String | 用户名 |
ok | bool | 是否成功 |
result | String? | 成功时的结果:muted、unmuted、banned、unbanned、removed、not_in_room、added、unchanged |
code | String? | 失败时的错误码 |
message | String? | 失败时的说明 |
details | Map<String, Object?>? | 失败的详细原因,如 reason 为 owner_protected |
ChatroomMuteEntry、ChatroomBanEntry、ChatroomAllowlistEntry
禁言名单、封禁名单和白名单的一项。
| 字段 | 类型 | 说明 |
|---|---|---|
username | String | 用户名 |
nickname | String | 昵称 |
avatarUrl | String | 头像地址 |
mutedUntil | DateTime? | 只在禁言名单中:到期时间,永久为 null |
expiresAt | DateTime? | 只在封禁名单中:到期时间,永久为 null |
reason | String | 禁言、封禁的原因(白名单没有) |
createdBy | String? | 操作人的用户名 |
createdByRole | String | 禁言、封禁时操作人的身份:owner、admin 或 server(白名单没有) |
createdAt | DateTime? | 加入名单的时间 |
ChatroomPatch
manage.update() 的参数,构造函数的参数都是可选的命名参数,省略的字段不改。
| 字段 | 类型 | 说明 |
|---|---|---|
name | String? | 名称,不能清空(所有者、管理员) |
description | String? | 简介,空字符串表示清空(所有者、管理员) |
avatarUrl | String? | 封面地址,空字符串表示清空(所有者、管理员) |
announcement | String? | 公告,空字符串表示清空(所有者、管理员) |
maxMembers | int? | 人数上限(所有者) |
resetMaxMembers | bool | 为 true 时人数上限改为跟随运行策略(所有者),默认 false |
listed | bool? | 是否出现在聊天室列表中(所有者) |
ChatroomPriority
枚举:high、normal、low,见优先级。
