提醒、推送設定與舉報
本頁介紹三件事:收到新訊息時怎樣決定是否提醒使用者、由哪個標籤頁提醒;用 im.push 讀取和修改使用者的推送設定與會話免打擾;用 im.reports 舉報訊息、使用者、群和聊天室。
| 要做什麼 | 看哪裡 |
|---|---|
| 新訊息彈出瀏覽器通知、播放提示音 | 收到訊息時提醒 |
| 多個標籤頁只提醒一次 | 由哪個標籤頁提醒 |
| 全域性免打擾、夜間免打擾、通知顯示的內容 | 推送設定 |
| 某個單聊或群免打擾 | 會話免打擾 |
| 舉報訊息、使用者、群或聊天室 | 舉報 |
收到訊息時提醒
Web SDK 不會自己彈出瀏覽器通知或播放提示音,由你在收到 message.received 事件時決定。事件的載荷中已經帶好了兩項判斷:
alert:本標籤頁是不是負責提醒的標籤頁(見由哪個標籤頁提醒);notify:按使用者的推送設定和會話免打擾,這條訊息是否應該提醒,以及通知的標題和正文(NotifyDecision)。
import type { NotifyDecision } from '@deeprespond/im-web';
im.on('message.received', ({ message, notify, alert }) => {
const decision = notify as NotifyDecision | null;
if (!alert || !decision?.notify) return;
if (document.visibilityState === 'visible') {
showToast(`${decision.title}:${decision.body}`); // 页面可见时用页面内的提示
return;
}
if (Notification.permission === 'granted') {
const n = new Notification(decision.title || '新消息', { body: decision.body, tag: message.conversation_id });
n.onclick = () => {
window.focus();
// 打开这个会话
};
}
});
// 在用户的操作中(如点击“开启通知”)请求权限
async function enableNotification(): Promise<void> {
await Notification.requestPermission();
}message.received只對透過長連線實時收到的、別人發來的訊息發出;離線期間的訊息在重新連線後由同步補齊,不發出這個事件,也就不會在上線時彈出一連串通知。notify的型別宣告為unknown,請按NotifyDecision | null使用。- 使用者正在看的會話通常不需要彈出通知。SDK 的判斷不考慮這一點,請自行判斷(如這個會話的訊息列表開啟著、頁面可見時跳過)。
- 網頁關閉後收不到任何提醒:Web SDK 沒有瀏覽器推送(Web Push),新訊息只能透過長連線得知。
由哪個標籤頁提醒
使用者在同一瀏覽器中開啟了多個頁面時,每個標籤頁都會收到 message.received,如果都彈出通知,使用者會收到重複的通知、聽到重疊的提示音。SDK 選出一個“提醒標籤頁”:
- 有可見的標籤頁時,取最近獲得焦點的那個;
- 都不可見時(如瀏覽器最小化),取同一瀏覽器中負責長連線的那個標籤頁。
只在事件載荷中 alert 為 true 的標籤頁提醒即可。im.connection.isAlertTab 是本標籤頁當前是否為提醒標籤頁,變化時發出 tabs.roleChanged,可以用於顯示狀態;彈出通知請以事件載荷中的 alert 為準,它是事件發出時的值。來電的提醒同樣如此,見音影片的 call.incoming。
提醒的判斷
notify 就是 im.push.shouldNotify(message) 的結果。也可以對任何一條訊息自己呼叫,如在列表中補發提醒:
const decision = im.push.shouldNotify(message);
if (decision.notify) {
console.log(decision.title, decision.body);
} else {
console.log('不提醒', decision.reason);
}不提醒時 reason 說明原因:
reason | 說明 |
|---|---|
self | 本人發的訊息 |
tip | 群提示。被移出群、群解散等由群組的事件得知,見群組 |
call | 通話記錄。來電由 call.incoming 單獨提醒 |
excluded | 傳送時設定了 exclude_from_unread |
not_loaded | 推送設定還沒有同步到(剛登入時),可以自行決定是否提醒 |
dnd | 使用者開啟了全域性免打擾 |
quiet_hours | 當前在夜間免打擾時段內 |
muted | 這個會話設定了免打擾 |
not_mentioned | 群設定了“只提醒 @ 我”,這條訊息沒有 @ 本人或全體成員 |
提醒時 title 和 body 按使用者的預覽方式(effective_preview)生成,與手機上收到的推送一致:
| 預覽方式 | 單聊 | 群聊 |
|---|---|---|
full | 標題為傳送者,正文為訊息摘要 | 標題為群名,正文為“傳送者: 摘要”,@ 了本人的前面加“[有人@我]” |
sender_only | 標題為傳送者,正文為“發來一條新訊息” | 標題為群名,正文為“某某 發來一條新訊息” |
none | 標題為空,正文為“你收到了一條新訊息” | 同左 |
傳送者取暱稱(沒有暱稱為使用者名稱),不使用好友備註和群暱稱。訊息摘要的規則與服務端的推送相同:文字取前 100 個字元,圖片為“[圖片]”,檔案為“[檔案] 檔名”,自定義訊息為“[訊息]”。
這個判斷與服務端的離線推送規則近似,有以下差別:
- 傳送訊息時的
push.disabled、push.force不隨訊息下發,按都沒有設定處理; - @ 全體成員按能突破“只提醒 @ 我”處理(服務端的預設值);
- 標題和正文只使用內建的中文模板,不使用你在控制檯中覆蓋的通知模板。
推送設定
使用者的推送設定包括全域性免打擾、夜間免打擾和預覽方式。它們決定使用者在手機 App 上收到的離線推送,也是上面網頁提醒判斷的依據。設定儲存在服務端,在使用者的全部裝置間同步:使用者在網頁上修改後,手機上的推送隨之變化。網頁本身沒有離線推送,見離線推送。
im.push.settings() 同步讀取本地已同步到的設定,變化時發出 push.changed 事件:
function renderSettings(): void {
const s = im.push.settings();
if (!s) return; // 还没有同步到
render({
dnd: s.dnd ? (s.dnd.until ? `免打扰至 ${s.dnd.until}` : '一直免打扰') : '关闭',
quietHours: s.quiet_hours ? `${s.quiet_hours.start}~${s.quiet_hours.end}` : '关闭',
preview: s.effective_preview, // full / sender_only / none
});
}
im.on('push.changed', ({ settings }) => {
if (settings) renderSettings();
});
renderSettings();修改時只給出要改的項:
// 8 小时内免打扰
await im.push.updateSettings({ dnd: { duration_seconds: 8 * 3600 } });
// 一直免打扰;关闭免打扰
await im.push.updateSettings({ dnd: { duration_seconds: null } });
await im.push.updateSettings({ dnd: null });
// 每天 22:00 到次日 07:30 免打扰,按上海时间
await im.push.updateSettings({ quiet_hours: { start: '22:00', end: '07:30', timezone: 'Asia/Shanghai' } });
await im.push.updateSettings({ quiet_hours: null });
// 通知只显示发送者;恢复为应用的默认方式
await im.push.updateSettings({ preview: 'sender_only' });
await im.push.updateSettings({ preview: null });- 全域性免打擾:一段時間內(1 秒到 10 年)或一直不推送。到期後 SDK 在本地自動恢復,發出
push.changed。 - 夜間免打擾:每天的一個時段,
end早於start表示跨過午夜,開始時刻算在時段內、結束時刻不算。時刻為HH:MM(24 小時制);時區為 IANA 時區名稱,可以用Intl.DateTimeFormat().resolvedOptions().timeZone取得瀏覽器的時區。時區不隨裝置變化,使用者換了時區需要重新設定。 - 預覽方式:
full顯示傳送者和內容,sender_only只顯示傳送者,none都不顯示。preview是使用者自己的選擇,為null時使用應用的預設方式;effective_preview是實際生效的方式。
會話免打擾
對一個單聊或群設定免打擾:
// 单聊:不提醒
await im.push.mute({ conversation_type: 'single', target: 'lisi' }, { mode: 'none' });
// 群:只在有人 @ 我时提醒,24 小时后自动恢复
await im.push.mute({ conversation_type: 'group', target: groupId }, { mode: 'mention_only', duration_seconds: 86400 });
// 取消
await im.push.unmute({ conversation_type: 'group', target: groupId });
// 读取(同步)
const mute = im.push.getMute({ conversation_type: 'group', target: groupId });
console.log(mute?.mode, mute?.until);target為單聊對方的使用者名稱或群 ID。mode為none(不提醒)或mention_only(只在 @ 本人或全體成員時提醒,只用於群)。- 單聊只能設定
none;不能對自己設定。這兩種情況 SDK 在本地以local_validation拒絕(reason為invalid_mode、self_conversation)。 - 省略
duration_seconds一直有效;給出的(1 秒到 10 年)到期後自動恢復,SDK 在本地到期時更新會話列表併發出push.changed。 - 設定後會話列表中這個會話的
muted隨之變化(見會話),im.conversations.getUnreadTotal({ excludeMuted: true })不再計入它。 - 免打擾隻影響推送和提醒,不影響訊息的接收和未讀數。退出或被移出群、群解散後,對這個群的免打擾隨之刪除。每個使用者最多 10000 個會話免打擾。
push.changed 事件的載荷為 { settings, mutes }:settings 為 true 表示推送設定變了;mutes 列出免打擾變化了的會話({ conversation_type, target })。其他裝置上的修改同樣實時同步過來。
舉報
使用者可以舉報一條訊息、一個使用者、一個群、一個聊天室或一條聊天室訊息。舉報進入你在控制檯中的稽核記錄,由稽核人員處理,見內容安全。
import type { ReportReason } from '@deeprespond/im-web';
if (im.config?.report_enabled !== false) {
// 举报一条消息
const { duplicate } = await im.reports.submit({ target_type: 'message', message_id: '99584445836689408', reason: 'fraud' });
showToast(duplicate ? '你已举报过' : '已举报,我们会尽快处理');
}
// 举报一个用户,附上他发来的消息作为证据(最多 5 条)
const reason: ReportReason = 'abuse';
await im.reports.submit({
target_type: 'user',
username: 'lisi',
reason,
description: '多次发送辱骂信息',
evidence_message_ids: ['99584445836689408', '99584445836689409'],
});
// 本人 90 天内的举报
const page = await im.reports.mine({ limit: 20 });
for (const r of page.items) console.log(r.target_type, r.reason, r.status === 'pending' ? '处理中' : '已处理');- 入口:執行配置的
report_enabled為false時應用關閉了客戶端舉報,請隱藏舉報入口;這時呼叫以permission_denied(report_disabled)拒絕。 - 原因:只能從固定的 10 種中選擇,見 ReportReason。說明最多 500 個字元。
- 證據:舉報使用者和群時可以附上最多 5 條訊息的
message_id。舉報使用者的證據必須是被舉報的人發的,舉報群的必須是這個群裡的,都不能是已撤回的;SDK 先用本地儲存的訊息檢查,不符合的以local_validation(reason為invalid_evidence)拒絕。請在介面上只讓使用者勾選符合條件的訊息。 - 只能舉報看得到的內容:入群之前、離開群之後的群訊息,本人已刪除的、已撤回的訊息不能舉報,服務端返回
not_found。聊天室訊息只能舉報最近 24 小時內的,讀不到時可以改為舉報聊天室或傳送者。不能舉報自己(local_validation,reason為self_report)。 - 重複舉報:同一個人對同一個物件只算一次,再次舉報返回原來的那一次,
duplicate為true,同樣提示“已舉報”。 - 頻率:每個使用者每分鐘 5 次、每天 30 次,超出時以
rate_limited拒絕,不會自動重試。 - 結果:舉報人只能看到“處理中”(
pending)和“已處理”(closed),看不到處理結論。
介面參考
方法返回 Promise 的,失敗時以 DRError 拒絕;沒有登入時以 not_signed_in 拒絕,網路錯誤為 network_error、timeout。錯誤碼見事件與錯誤和服務端的錯誤碼。
im.push.settings()
同步讀取本地的推送設定。全域性免打擾已到期的,返回的 dnd 為 null。變化時發出 push.changed(settings 為 true)。
返回值:PushSettings | null,還沒有同步到時為 null。見 PushSettings。
im.push.updateSettings()
修改推送設定,只修改給出的項。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
patch.dnd | { duration_seconds: number | null } | null | 否 | 全域性免打擾:duration_seconds 為 1 到 315360000 秒,為 null 一直開啟;dnd 為 null 關閉 |
patch.quiet_hours | { start: string; end: string; timezone: string } | null | 否 | 夜間免打擾:start、end 為 HH:MM,timezone 為 IANA 時區名稱;null 關閉 |
patch.preview | PushPreview | null | 否 | 預覽方式:'full'、'sender_only'、'none';null 使用應用的預設方式 |
至少給出一項。
返回值:Promise<PushSettings>,修改後的完整設定。
可能的錯誤:local_validation(reason 為 required,一項也沒有給出或缺少時區;out_of_range,時長超出範圍;invalid_format,時刻的格式不對;invalid_value,預覽方式不對)、invalid_argument(invalid_quiet_hours,開始等於結束;invalid_timezone,時區不是 IANA 時區名稱)。
im.push.mute()
設定會話免打擾。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
target.conversation_type | 'single' | 'group' | 是 | 單聊或群 |
target.target | string | 是 | 單聊對方的使用者名稱或群 ID |
p.mode | 'none' | 'mention_only' | 是 | none 不提醒;mention_only 只在 @ 本人時提醒,只用於群 |
p.duration_seconds | number | 否 | 時長,1 到 315360000 秒;省略一直有效 |
返回值:Promise<void>。
可能的錯誤:local_validation(reason 為 invalid_mode,單聊設定了 mention_only;self_conversation,對自己設定;out_of_range,時長超出範圍;required、invalid_value,引數缺失或不對)、limit_exceeded(會話免打擾已達 10000 個)。
im.push.unmute()
取消會話免打擾。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
target | MuteTarget | 是 | 見 MuteTarget |
返回值:Promise<void>。
可能的錯誤:local_validation(引數缺失或不對)。
im.push.getMute()
同步讀取一個會話的免打擾。變化時發出 push.changed(mutes 中列出這個會話)。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
target | MuteTarget | 是 | 見 MuteTarget |
返回值:MuteInfo | null,沒有設定或已到期時為 null。MuteInfo 為 { mode, until },見會話。
im.push.shouldNotify()
按本地的推送設定和會話免打擾,判斷一條新訊息是否應該提醒,並給出通知的標題和正文。同步執行。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
message | MessageView | 是 | 訊息,見訊息 |
返回值:NotifyDecision,見 NotifyDecision。
im.reports.submit()
提交舉報。不會自動重試。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
p | ReportInput | 是 | 舉報的物件、原因和說明,見 ReportInput |
返回值:Promise<{ report: Report; duplicate: boolean }>。duplicate 為 true 表示重複舉報,report 為原來的那一次。
可能的錯誤:local_validation(reason 為 invalid_value,物件型別或原因不對;required,缺少物件的 ID;too_long,說明超過 500 個字元;control_characters,說明中有控制字元;self_report,舉報自己;invalid_evidence,證據不符合要求;too_many_items,證據超過 5 條)、permission_denied(report_disabled)、not_found(物件不存在或本人看不到)、invalid_argument(invalid_evidence)、rate_limited。
im.reports.mine()
查詢本人 90 天內的舉報,翻頁。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
page.cursor | string | null | 否 | 上一頁返回的 next_cursor |
page.limit | number | 否 | 每頁條數 |
返回值:Promise<Page<Report>>,即 { items: Report[]; next_cursor: string | null },沒有下一頁時 next_cursor 為 null。
資料結構
NotifyDecision
| 欄位 | 型別 | 說明 |
|---|---|---|
notify | boolean | 是否應該提醒 |
title | string | 可選,提醒時給出:通知的標題,預覽方式為 none 時為空字串 |
body | string | 可選,提醒時給出:通知的正文 |
reason | string | 可選,不提醒時給出原因:self、tip、call、excluded、not_loaded、dnd、quiet_hours、muted、not_mentioned |
PushSettings
與服務端的推送設定相同。
| 欄位 | 型別 | 說明 |
|---|---|---|
dnd | { until: string | null } | null | 全域性免打擾,沒有開啟為 null;until 為到期時間,null 表示一直開啟 |
quiet_hours | { start: string; end: string; timezone: string } | null | 夜間免打擾,沒有設定為 null |
preview | PushPreview | null | 使用者選擇的預覽方式,沒有選擇為 null |
effective_preview | PushPreview | 實際生效的預覽方式 |
settings_version | number | 設定的版本號,SDK 同步用 |
PushPreview
'full' | 'sender_only' | 'none':顯示傳送者和內容、只顯示傳送者、都不顯示。
MuteTarget
| 欄位 | 型別 | 說明 |
|---|---|---|
conversation_type | 'single' | 'group' | 單聊或群 |
target | string | 單聊對方的使用者名稱或群 ID |
ReportInput
按 target_type 分為五種,都有 reason(必填)和 description(可選,最多 500 個字元,可以有換行):
target_type | 其他欄位 | 說明 |
|---|---|---|
message | message_id | 舉報一條單聊或群訊息 |
user | username;evidence_message_ids? | 舉報一個使用者,可以附上最多 5 條他發的訊息 |
group | group_id;evidence_message_ids? | 舉報一個群,可以附上最多 5 條這個群裡的訊息 |
chatroom | room_id | 舉報一個聊天室 |
chatroom_message | room_id;message_id | 舉報一條聊天室訊息 |
ReportReason
| 取值 | 含義 |
|---|---|
ad | 廣告、騷擾 |
fraud | 詐騙 |
porn | 色情低俗 |
politics | 涉政 |
terrorism | 暴恐 |
abuse | 辱罵攻擊 |
illegal | 違法違禁 |
infringement | 侵權 |
minor | 涉及未成年人 |
other | 其他 |
Report
| 欄位 | 型別 | 說明 |
|---|---|---|
report_id | string | 舉報 ID |
target_type | string | 舉報的物件型別,同 ReportInput |
username | string | null | 被舉報的使用者(舉報使用者時) |
message_id | string | null | 被舉報的訊息 |
group_id | string | null | 被舉報的群 |
room_id | string | null | 被舉報的聊天室 |
reason | ReportReason | 原因 |
status | 'pending' | 'closed' | 處理中或已處理 |
created_at | string | 舉報時間 |
