用户、好友与在线状态
本页介绍与“人”有关的功能:本人的资料、其他用户的资料、好友和好友申请、黑名单、在线状态,以及本人的登录设备。
- 用户资料(
im.users):本人资料随登录取得并保存在本地,可以修改昵称、头像和自定义属性;其他用户的资料按需批量获取并缓存,列表界面直接同步读取。 - 好友(
im.friends):好友列表和黑名单完整保存在本地,以可订阅的列表提供,其他设备上的变化实时同步过来;好友申请按页查询。 - 在线状态(
im.presence):订阅要显示的人,状态变化时收到事件;也可以查询一次。 - 登录设备(
im.devices):查看本人当前在线的设备和全部登录设备,踢掉其他设备。
这些方法都要在登录之后调用(见初始化与登录)。可订阅的列表、同步读取的状态和事件的通用规则见事件与错误。
本人资料
读取本人资料
im.users.me() 同步返回本地保存的本人资料,未登录时为 null。资料在其他设备上被修改、或被你的服务端修改时,SDK 自动更新本地的资料并发出 me.changed 事件。
const me = im.users.me();
render(me?.nickname || me?.username);
im.on('me.changed', (profile) => {
render(profile?.nickname || profile?.username);
});需要确认拿到的是服务端最新的资料时,调用 im.users.refreshMe()。
修改昵称、头像和自定义属性
im.users.updateMe() 只提交传入的字段,没有传的字段保持不变。清空昵称或头像时传空字符串;自定义属性按项合并,值为 null 的键被删除,整个 attributes 为 null 时清空全部属性。修改成功后本地资料随即更新,并发出 me.changed。
const updated = await im.users.updateMe({
nickname: '张三',
attributes: { sign: '今天也要加油', city: null }, // 设置 sign,删除 city
});
console.log(updated.version);昵称和头像要经过内容安全检查,不通过时以 content_rejected 拒绝,details.field 为 nickname 或 avatar_url。各字段的格式要求见服务端的用户管理。
多台设备可能同时修改资料时,可以带上读取到的 version,资料已被别处修改时以 version_conflict 拒绝,不做任何修改:
const current = im.users.me();
try {
await im.users.updateMe({ nickname: '张三', version: current?.version });
} catch (err) {
if (err instanceof DRError && err.code === 'version_conflict') {
await im.users.refreshMe(); // 取得最新的资料,让用户重新确认
}
}设置头像
头像要先以用途 user_avatar 上传,得到公开地址后再写入资料。上传的图片会被缩放到长边不超过 640 像素,详见文件。
async function changeAvatar(file: File) {
const info = await im.files.upload(file, { purpose: 'user_avatar' });
await im.users.updateMe({ avatar_url: info.url });
}应用在运行策略中开启了 media_url_only 时,只能使用本应用上传的头像地址(或允许的主机的 https 地址),其他地址以 invalid_argument 拒绝,details.reason 为 url_not_allowed。
本人是否被禁言
你的服务端可以对用户全局禁言。im.users.myMute 给出本人在单聊(chat)、群聊(group)、聊天室(room)三个范围的禁言,没有禁言的为 null,变化时发出 me.muteChanged:
im.on('me.muteChanged', ({ scope, muted, expires_at }) => {
if (muted) showToast(`你已被禁言${expires_at ? `,到 ${new Date(expires_at).toLocaleString()} 解除` : ''}`);
});只用于显示
服务端没有查询本人禁言的接口,myMute 只反映本次连接期间收到的变化:刷新页面、重新打开之后它为空,即使用户仍在禁言中。不要据此禁用输入框;发送时以服务端返回的 user_muted 错误为准。
其他用户的资料
在列表中显示昵称和头像
im.users.get(username) 同步返回缓存中的资料。缓存中没有或已过期(默认 10 分钟,可用创建选项 userCacheTtlMs 修改)时,它返回已有的值(可能是 undefined),并在后台获取;获取到之后发出 users.changed,事件载荷中列出资料有变化的用户名。
会话列表、成员列表这类一次显示很多人的界面,应该用 get 加 users.changed 重新渲染,不要逐个 await。50 毫秒内的多次后台获取会合并成一次请求,每次最多 100 人。
function nameOf(username: string): string {
const profile = im.users.get(username);
return profile?.nickname || username;
}
im.on('users.changed', ({ usernames }) => {
render(usernames.map(nameOf));
});要按“好友备注、群昵称、昵称、用户名”的顺序显示名字,可以用 @deeprespond/im-web/render 中的 displayName(im, username),它同样同步读取、在资料到达后随事件更新。
查询资料
确实需要等待结果时(如打开某人的资料页),调用 im.users.fetch()。结果是以小写用户名为键的 Map,不存在或已删除的用户不在结果中。缓存未过期的直接返回缓存,force: true 时一律向服务端查询。
const profiles = await im.users.fetch(['lisi', 'wangwu']);
const lisi = profiles.get('lisi');
if (!lisi) showToast('用户不存在');资料缓存在本地,页面刷新后仍然有效。好友修改资料时服务端不推送,所以别人的资料最多在缓存时间之后才更新。
好友
显示好友列表
im.friends.list() 返回好友的可订阅列表,包含全部好友,按备注、昵称、用户名中第一个非空的排序(中文按拼音)。登录后 SDK 自动同步好友列表,之后好友的增删改(包括在其他设备上的操作)都实时反映到列表中。列表不再显示时调用 dispose()。
const friends = im.friends.list();
const unsubscribe = friends.subscribe(() => {
const { items, loading } = friends.getSnapshot();
if (!loading) render(items.map((f: Friend) => f.remark || f.nickname || f.username));
});
// 页面离开时
unsubscribe();
friends.dispose();只需要判断某人是不是好友,或读取他的备注时,用同步的 im.friends.get(username),不是好友时为 undefined。im.friends.listInfo 给出好友数和上限:
const info = im.friends.listInfo;
if (info) render(`好友 ${info.count} / ${info.max_count}`);React 中用 useLiveList:
import type { Friend } from '@deeprespond/im-web';
import { useLiveList } from '@deeprespond/im-web-react';
export function FriendList() {
const { snapshot } = useLiveList<Friend>((im) => im.friends.list(), []);
if (snapshot.loading) return <p>加载中…</p>;
return (
<ul>
{snapshot.items.map((f) => (
<li key={f.username}>{f.remark || f.nickname || f.username}</li>
))}
</ul>
);
}检查与某人的关系
在用户资料页上显示“加好友”“等待验证”“已拉黑”等状态时,用 im.friends.check(),一次最多 100 人。本人和不存在的用户不在结果中。
const [relation] = await im.friends.check(['lisi']);
if (relation?.is_friend) render('发消息');
else if (relation?.request === 'sent') render('等待验证');
else if (relation?.request === 'received') render('对方已向你发出申请');
else render('加好友');blocked 只表示本人是否拉黑了对方;对方是否拉黑了本人不会告诉你。
设置备注和自定义属性
备注和自定义属性只属于本人这一侧,对方看不到。自定义属性可以用来实现星标好友、好友分组;按项合并,值为 null 的键被删除。
await im.friends.update('lisi', { remark: '老李', attributes: { star: '1', group: '同事' } });删除好友
删除后双方都不再是好友,对方的好友列表也会同步移除本人。删除本来就不是好友的人同样成功。
await im.friends.remove('lisi');好友申请
发送申请
const result = await im.friends.requests.send({
username: 'lisi',
message: '我是张三,项目组的',
remark: '李四', // 成为好友后自动设置的备注
add_source: 'search', // 添加来源,由你定义,会展示给对方
});
if (result.status === 'accepted') showToast('已成为好友');
else showToast('已发送,等待对方验证');结果取决于对方的加好友方式:对方需要验证时 status 为 pending;对方允许任何人添加,或对方此前已向本人发出了申请时,双方直接成为好友,status 为 accepted,friend 为新好友;对方拒绝任何人添加时以 friend_add_denied 拒绝。
常见的错误:
code | 说明 |
|---|---|
already_exists | 已经是好友。SDK 按成功处理,返回 status: 'accepted',不会以这个错误拒绝 |
friend_add_denied | 对方设置了拒绝任何人添加 |
user_blocked | 双方有一方把另一方拉黑了 |
limit_exceeded | 本人的好友数已满,或对方允许任何人添加而对方的好友数已满 |
rate_limited | 发送过于频繁,details.reason 为 request_rate(每分钟 20 个、每天 500 个)、pair_daily_limit(对同一个人每天 5 次)或 declined_cooldown(被拒绝后 24 小时内) |
content_rejected | 附言或添加来源未通过内容安全检查 |
permission_denied | 你的服务端通过加好友前回调拒绝了这次申请,details.reason 为 app_rejected 或 callback_unavailable |
timeout | 网络错误或超时,SDK 查询后确认申请没有发出 |
发送申请不会自动重发:网络错误或超时后,SDK 先查询双方的关系,已经成为好友或申请已经发出的按成功返回;确认没有发出的以 timeout 拒绝,由用户决定是否再发。遇到 rate_limited 时请按 details.reason 提示用户。
申请发出后不能撤回。申请的完整规则(有效期 30 天、重复申请会更新原来的申请等)见服务端的好友申请。
收到的申请和未读数
im.friends.requests.unreadCount 同步给出收到的、还没有查看过的申请数(最多统计到 100,超过 99 时可以显示“99+”),变化时发出 friendRequests.changed:
im.on('friendRequests.changed', ({ unread_count }) => {
render(unread_count > 99 ? '99+' : String(unread_count || ''));
});打开“新朋友”页面时查询收到的申请,展示之后标记为已读:
const page = await im.friends.requests.received();
render(page.items.map((r: FriendRequest) => `${r.nickname || r.username}:${r.message}(${r.status})`));
await im.friends.requests.markRead();- 不带
cursor和status时返回 SDK 内存中的第一页,不发请求。SDK 在登录、重连和收到申请相关的通知时会刷新这一页,所以它是最新的。带status筛选或翻页时向服务端查询。 markRead()不带参数时,把当前第一页中未读的申请标为已读;也可以传入read_until(最新一条已展示的申请的requested_at)。
发出的申请用 im.friends.requests.sent() 查询,规则相同。申请在最近一次发送后 30 天内未处理即过期,状态变为 expired。
同意或拒绝
const friend = await im.friends.requests.accept('wangwu', { remark: '王五' });
await im.friends.requests.decline('zhaoliu');同意后双方成为好友,好友列表随即更新;拒绝后对方 24 小时内不能再向本人申请。申请已过期时以 not_found 拒绝。同意时双方任何一方的好友数已满,以 limit_exceeded 拒绝。
黑名单
把某人加入黑名单后,他不能给本人发单聊消息、不能向本人发好友申请、不能邀请本人入群;本人发给他的、尚未处理的好友申请被一并撤回。拉黑不解除好友关系,也不通知对方。详见服务端的黑名单。
await im.friends.blacklist.add('spammer');
if (im.friends.blacklist.has('spammer')) render('已拉黑');
const blacklist = im.friends.blacklist.list(); // 按拉黑时间从新到旧
blacklist.subscribe(() => render(blacklist.getSnapshot().items));
await im.friends.blacklist.remove('spammer');黑名单最多 1000 人。应用在运行策略中关闭了黑名单拦截(user_blacklist_enabled)时,不能再拉黑别人,add 以 permission_denied 拒绝;查看和移出照常可用。
加好友方式
每个用户可以设置别人向自己发送好友申请时怎样处理:
| 取值 | 说明 |
|---|---|
need_confirm | 需要验证:生成待处理的申请,本人同意后成为好友 |
allow_any | 允许任何人添加:不需要本人同意,直接成为好友 |
deny_any | 拒绝任何人添加:对方的申请以 friend_add_denied 失败 |
const { friend_add_mode, effective_friend_add_mode } = await im.friends.settings.get();
render(friend_add_mode === null ? `跟随应用默认(${effective_friend_add_mode})` : friend_add_mode);
await im.friends.settings.set('need_confirm');
await im.friends.settings.set(null); // 清除本人的设置,改用应用的默认值用户没有设置过时使用应用运行策略中的默认值(默认 need_confirm)。
在线状态
在线状态需要应用在运行策略中开启 presence_enabled(默认关闭)。运行策略还决定能看到谁的状态:presence_scope 为 friends(默认)时只能看到好友的,为 all 时可以看到任何用户的;presence_last_seen_visible 关闭后看不到最近离线时间(last_seen_at 为 null)。可以从 im.config 读取这几项,决定界面上是否显示在线状态:
const config = im.config;
const showPresence = config?.presence_enabled === true;关闭了在线状态时,subscribe 和 query 以 permission_denied(details.reason 为 presence_disabled)失败。
订阅要显示的人
im.presence.subscribe() 登记要持续关注的人,返回取消这次订阅的函数。状态变化时发出 presence.changed,之后用同步的 im.presence.get(username) 读取:
const visible = ['lisi', 'wangwu'];
const stop = im.presence.subscribe(visible);
im.on('presence.changed', ({ items, reset }) => {
for (const username of visible) {
const state = im.presence.get(username);
render(username, state === undefined ? '未知' : state.online ? `在线(${state.platforms.join('、')})` : '离线');
}
});
// 不再显示时
stop();- 按需订阅:只订阅当前看得到的人,如会话列表中可见的好友、打开的单聊对方。同一个人被订阅多次时计数,全部取消后才真正退订。
- 延迟:订阅的变化合并 2 秒后才发给服务端,取消后 30 秒才退订(期间又需要的不退订),所以快速滚动列表不会产生大量请求。
- 上限:一个浏览器最多同时订阅 3000 人。
- 多个标签页:同一浏览器的多个标签页共用一个连接,订阅由 SDK 合并,各标签页的
subscribe互不影响。 - 重新连接:断线重连后 SDK 自动重新订阅,期间
presence.changed的reset为true,表示之前的状态都已清除。
get 在以下情况返回 undefined,界面应显示为“未知”而不是“离线”:还没有收到订阅结果;对方不存在或看不到;重新连接后还没有重新订阅完;presence_scope 为 friends 而对方已不是好友。
没能订阅的人以 presence.subscribeFailed 事件报告:
im.on('presence.subscribeFailed', ({ usernames, reason }) => {
// reason:denied(不存在或看不到)、limit_exceeded(超出上限)、presence_disabled(应用关闭了在线状态)
console.warn('在线状态订阅失败', reason, usernames);
});服务端的限流(订阅按用户每分钟 30 次)由 SDK 等待后自动重试,不报事件。
在 React 中可以用 usePresence:
import { usePresence } from '@deeprespond/im-web-react';
export function OnlineDot({ username }: { username: string }) {
const states = usePresence([username]);
const state = states.get(username);
return <span className={state === undefined ? 'unknown' : state.online ? 'online' : 'offline'} />;
}查询一次
不需要持续更新时(如打开资料页时显示一次),用 im.presence.query() 查询,一次最多 100 人。查询的结果不会更新 get 读到的状态。
const [state] = await im.presence.query(['lisi']);
if (state && !state.online && state.last_seen_at) render(`最近在线:${new Date(state.last_seen_at).toLocaleString()}`);离线的判定有延迟:设备异常断网时,服务端要在连接空闲超时之后才判定离线,见服务端的在线状态。
本人的登录设备
当前在线的设备
im.devices.online() 同步返回本人当前保持着连接的设备(包括这台),变化时发出 devices.changed,可以用来显示“电脑已登录”:
im.on('devices.changed', ({ devices }) => {
const others = devices.filter((d) => !d.current);
render(others.length > 0 ? `${others.map((d) => d.platform).join('、')} 已登录` : '');
});本人在新设备上登录时发出 devices.newLogin,可以提示用户:
im.on('devices.newLogin', ({ device_name, platform, created_at }) => {
showToast(`你的账号于 ${new Date(created_at).toLocaleString()} 在 ${device_name || platform} 上登录`);
});全部登录设备与踢下线
im.devices.sessions() 返回本人全部有效的登录(包括目前没有连接的设备),按最近活跃时间从新到旧,current 标出这台设备。im.devices.kick() 让指定的设备下线:
const sessions = await im.devices.sessions();
render(sessions.map((s: LoginSession) => `${s.device_name || s.platform}${s.current ? '(本机)' : ''}`));
const target = sessions.find((s) => !s.current);
if (target) await im.devices.kick(target.session_id);被踢的设备立即下线,需要重新登录。指定的是当前设备时等同于退出登录。同时登录的设备数受运行策略的限制,超出时最早的设备会被挤下线,见服务端的登录与凭证。
接口参考
im.users.me()
同步返回本地保存的本人资料。
返回值:SelfProfile | null,未登录时为 null。变化时发出 me.changed。
im.users.refreshMe()
向服务端获取本人资料,更新本地保存的资料。
返回值:Promise<SelfProfile>。
im.users.updateMe()
修改本人资料,只提交传入的字段。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
patch.nickname | string | 否 | 昵称,最长 64 个字符;空字符串表示清空 |
patch.avatar_url | string | 否 | 头像地址;空字符串表示清空 |
patch.attributes | Record<string, string | null> | null | 否 | 自定义属性,按项合并:值为 null 的键被删除;整个字段为 null 时清空。最多 32 项,合计不超过 4 KB |
patch.version | number | 否 | 读取到的资料版本号,与服务端不一致时不修改 |
返回值:Promise<SelfProfile>,修改后的资料。
可能的错误:
code | 说明 |
|---|---|
local_validation | nickname 或 avatar_url 不是字符串 |
invalid_argument | 字段格式不对;头像地址不被允许(details.reason 为 url_not_allowed) |
content_rejected | 昵称或头像未通过内容安全检查 |
version_conflict | version 与服务端不一致 |
im.users.myMute
只读属性:本人在三个范围的全局禁言 { chat, group, room },每项为 GlobalMute | null。只反映本次连接期间收到的变化,变化时发出 me.muteChanged。
im.users.get()
同步返回缓存中的用户资料;没有或过期时在后台获取,获取后发出 users.changed。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 用户名 |
返回值:UserProfile | undefined。
im.users.fetch()
批量查询用户资料,缓存未过期的直接使用缓存。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | string[] | 是 | 用户名,超过 100 个时分批查询 |
opts.force | boolean | 否 | 为 true 时不使用缓存 |
返回值:Promise<Map<string, UserProfile>>,键为小写的用户名;不存在或已删除的用户不在结果中。
im.friends.list()
返回全部好友的可订阅列表(LiveList<Friend>),按备注、昵称、用户名中第一个非空的排序。快照的 hasMore 总是 false。不再使用时调用 dispose()。
未登录时抛出 not_signed_in。
im.friends.get()
同步返回一位好友,不是好友时为 undefined。变化时发出 friends.changed。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 用户名 |
返回值:Friend | undefined。
im.friends.listInfo
只读属性:{ count: number; max_count: number } | null,好友数和好友数上限;好友列表还没有同步时为 null。
im.friends.check()
检查本人与其他用户的关系。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | string[] | 是 | 1 到 100 个用户名 |
返回值:Promise<FriendCheck[]>,按请求中的顺序;本人和不存在的用户不在结果中。
可能的错误:local_validation(usernames 为空或超过 100 个)。
im.friends.update()
修改好友的备注和自定义属性。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 好友的用户名 |
p.remark | string | 否 | 备注,最长 64 个字符;空字符串表示清空 |
p.attributes | Record<string, string | null> | null | 否 | 自定义属性,按项合并;整个字段为 null 时清空。最多 16 项,合计不超过 1 KB |
remark 和 attributes 至少给出一项。
返回值:Promise<Friend>。
可能的错误:local_validation(两项都没有给出)、not_found(不是好友)、invalid_argument(备注或自定义属性不符合规则)。
im.friends.remove()
删除好友,双方的好友列表都会移除对方。不是好友时同样成功。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 好友的用户名 |
返回值:Promise<void>。
im.friends.requests.send()
发送好友申请。不自动重发,规则见发送申请。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
p.username | string | 是 | 对方的用户名 |
p.message | string | 否 | 附言,最长 256 个字符 |
p.remark | string | 否 | 成为好友后给对方设置的备注,最长 64 个字符 |
p.add_source | string | 否 | 添加来源,如 search、qrcode |
返回值:Promise<{ status: 'pending' | 'accepted'; friend?: Friend }>。
可能的错误:friend_add_denied、user_blocked、limit_exceeded、rate_limited、content_rejected、permission_denied、not_found(对方不存在)、timeout,见发送申请。
im.friends.requests.received()
查询收到的好友申请,按发送时间从新到旧。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
p.status | RequestStatus | 否 | 只列出这种状态的申请:pending、accepted、declined、expired |
p.cursor | string | null | 否 | 下一页的游标,取自上一页的 next_cursor |
p.limit | number | 否 | 每页条数 |
返回值:Promise<Page<FriendRequest>>。不带 cursor 和 status 时返回 SDK 内存中的第一页。
im.friends.requests.sent()
查询本人发出的好友申请,参数和返回值同 im.friends.requests.received()。
im.friends.requests.unreadCount
只读属性:收到的未读申请数,最多 100。变化时发出 friendRequests.changed。
im.friends.requests.markRead()
把收到的申请标为已读。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
p.read_until | string | 否 | 已展示的最新一条申请的 requested_at,这个时间及以前的申请标为已读。省略时按 SDK 内存中第一页的未读申请计算 |
返回值:Promise<void>。
im.friends.requests.accept()
同意好友申请,双方成为好友。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 申请人的用户名 |
p.remark | string | 否 | 给申请人设置的备注 |
返回值:Promise<Friend>,新好友。
可能的错误:not_found(申请不存在或已过期)、limit_exceeded(好友数已满)、permission_denied(被加好友前回调拒绝)。
im.friends.requests.decline()
拒绝好友申请。对方 24 小时内不能再向本人申请。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 申请人的用户名 |
返回值:Promise<void>。
可能的错误:not_found(申请不存在、已同意或已过期)。
im.friends.blacklist.list()
返回黑名单的可订阅列表(LiveList<BlacklistEntry>),按拉黑时间从新到旧。不再使用时调用 dispose()。
im.friends.blacklist.has()
同步判断本人是否拉黑了某人。变化时发出 blacklist.changed。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 用户名 |
返回值:boolean。
im.friends.blacklist.add()
把用户加入黑名单。已在黑名单中时同样成功。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 用户名 |
返回值:Promise<void>。
可能的错误:permission_denied(应用关闭了黑名单拦截)、limit_exceeded(黑名单已满 1000 人,details.reason 为 blacklist_limit)、not_found(用户不存在)、invalid_argument(拉黑自己)。
im.friends.blacklist.remove()
把用户移出黑名单。不在黑名单中时同样成功。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 用户名 |
返回值:Promise<void>。
im.friends.settings.get()
查询本人的加好友方式。
返回值:Promise<{ friend_add_mode: FriendAddMode | null; effective_friend_add_mode: FriendAddMode }>。friend_add_mode 为本人的设置,没有设置过时为 null;effective_friend_add_mode 为实际生效的方式。
im.friends.settings.set()
设置本人的加好友方式。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
mode | FriendAddMode | null | 是 | need_confirm、allow_any、deny_any;null 清除设置,改用应用的默认值 |
返回值:Promise<void>。
im.presence.subscribe()
登记要持续关注在线状态的人。同步执行,不等待服务端的结果。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | string[] | 是 | 用户名 |
返回值:() => void,取消这次订阅的函数,可以重复调用。
可能的错误(同步抛出):not_signed_in、permission_denied(presence_disabled)。客户端已 destroy() 时什么也不做,返回的函数同样什么也不做。
im.presence.get()
同步返回已订阅的人的在线状态。变化时发出 presence.changed。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是 | 用户名 |
返回值:PresenceState | undefined,未知时为 undefined。
im.presence.query()
查询一次在线状态,不订阅。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
usernames | string[] | 是 | 1 到 100 个用户名 |
返回值:Promise<PresenceState[]>;看不到的用户不在结果中。
可能的错误:local_validation(为空或超过 100 个)、permission_denied(presence_disabled)、rate_limited。
im.devices.online()
同步返回本人当前在线的设备。变化时发出 devices.changed。
返回值:readonly OnlineDevice[]。
im.devices.sessions()
查询本人全部有效的登录设备。
返回值:Promise<LoginSession[]>,按最近活跃时间从新到旧。
im.devices.kick()
让本人的一台设备下线;指定的是当前设备时等同于退出登录。设备已经下线时同样成功。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
session_id | string | 是 | 登录设备的 session_id |
返回值:Promise<void>。
可能的错误:local_validation(session_id 为空)、not_found(不是本人的登录设备)。
相关事件
| 事件 | 载荷 | 说明 |
|---|---|---|
me.changed | SelfProfile | null | 本人资料变化 |
me.muteChanged | { scope, muted, expires_at } | 本人的全局禁言变化;scope 为 chat、group 或 room |
users.changed | { usernames: string[] } | 这些用户的缓存资料有变化 |
friends.changed | { upserted: string[]; removed: string[] } | 好友列表变化 |
blacklist.changed | { upserted: string[]; removed: string[] } | 黑名单变化 |
friendRequests.changed | { lists: Array<'received' | 'sent'>; unread_count: number } | 好友申请或未读数变化,SDK 内存中的第一页已刷新 |
presence.changed | { items: PresenceState[]; reset: boolean; cleared?: string[] } | 在线状态变化;reset 为 true 时全部状态已清除,cleared 中的人的状态已清除 |
presence.subscribeFailed | { usernames: string[]; reason } | 没能订阅;reason 为 denied、limit_exceeded 或 presence_disabled |
devices.changed | { devices: OnlineDevice[] } | 本人在线的设备变化 |
devices.newLogin | { session_id, device_name, platform, login_method, created_at } | 本人在新设备上登录 |
数据结构
SelfProfile
本人资料。
| 字段 | 类型 | 说明 |
|---|---|---|
username | string | 用户名 |
nickname | string | 昵称,没有时为空字符串 |
avatar_url | string | 头像地址,没有时为空字符串 |
attributes | Record<string, string> | null | 自定义属性,没有时为 null |
has_password | boolean | 是否设置过密码 |
version | number | 资料的版本号,每次修改加一 |
服务端返回的其他字段原样保留。
UserProfile
其他用户的公开资料。
| 字段 | 类型 | 说明 |
|---|---|---|
username | string | 用户名 |
nickname | string | 昵称,没有时为空字符串 |
avatar_url | string | 头像地址,没有时为空字符串 |
attributes | Record<string, string> | null | 自定义属性 |
GlobalMute
| 字段 | 类型 | 说明 |
|---|---|---|
expires_at | string | null | 解除禁言的时间,永久禁言为 null |
Friend
本人视角的一位好友。
| 字段 | 类型 | 说明 |
|---|---|---|
username | string | 好友的用户名 |
nickname | string | 好友的昵称 |
avatar_url | string | 好友的头像地址 |
remark | string | 本人给好友设置的备注,没有时为空字符串 |
attributes | Record<string, string> | null | 本人给好友设置的自定义属性 |
add_source | string | 添加来源,没有时为空字符串 |
created_at | string | 成为好友的时间 |
FriendRequest
站在本人的角度描述一条好友申请:收到的申请中“对方”是申请人,发出的申请中“对方”是被申请人。
| 字段 | 类型 | 说明 |
|---|---|---|
username | string | 对方的用户名 |
nickname | string | 对方的昵称 |
avatar_url | string | 对方的头像地址 |
message | string | 附言,没有时为空字符串 |
add_source | string | 添加来源 |
status | RequestStatus | pending 待处理、accepted 已成为好友、declined 已拒绝、expired 已过期 |
unread | boolean | 只在收到的申请中出现:是否还没有查看过 |
remark | string | 只在发出的申请中出现:预先给对方设置的备注 |
requested_at | string | 最近一次发送的时间 |
expires_at | string | null | 过期时间,只有 pending 的申请有值 |
handled_at | string | null | 同意、拒绝或成为好友的时间 |
FriendCheck
| 字段 | 类型 | 说明 |
|---|---|---|
username | string | 对方的用户名 |
is_friend | boolean | 是否好友 |
blocked | boolean | 本人是否拉黑了对方 |
request | 'sent' | 'received' | null | 双方之间未过期的待处理申请:sent 为本人发出,received 为对方发来 |
BlacklistEntry
| 字段 | 类型 | 说明 |
|---|---|---|
username | string | 被拉黑的用户名 |
nickname | string | 昵称 |
avatar_url | string | 头像地址 |
created_at | string | 拉黑的时间 |
FriendAddMode
'need_confirm' | 'allow_any' | 'deny_any',见加好友方式。
PresenceState
| 字段 | 类型 | 说明 |
|---|---|---|
username | string | 用户名 |
online | boolean | 是否在线 |
platforms | string[] | 在线的平台,如 web、ios、windows;离线时为空数组 |
last_seen_at | string | null | 最近一次离线的时间;在线、30 天内没有上线过,或应用不允许查看时为 null |
version | number | 状态的版本号 |
OnlineDevice
本人当前在线的一台设备。
| 字段 | 类型 | 说明 |
|---|---|---|
session_id | string | 登录设备的会话 ID,与 LoginSession 的相同 |
platform | string | 平台 |
connected_at | string | 建立连接的时间 |
current | boolean | 是否为当前这台设备 |
LoginSession
本人的一次有效登录。
| 字段 | 类型 | 说明 |
|---|---|---|
session_id | string | 会话 ID,用于 kick() |
device_id | string | 设备标识 |
device_name | string | 设备名称,没有时为空字符串 |
platform | string | 平台:ios、android、harmony、windows、macos、linux、web、mini_program 或 other |
current | boolean | 是否为当前这台设备 |
sdk_version | string | 客户端版本 |
login_method | string | 登录方式:ticket 凭证登录,password 密码登录 |
ip | string | 登录时的 IP |
created_at | string | 登录时间 |
last_active_at | string | 最近一次登录或续期的时间 |
expires_at | string | null | 会话的过期时间,null 表示不过期 |
TypeScript 类型中只声明了前五个字段,其余字段同样存在,可以按索引读取。
