簡(jiǎn)介
本文主要介紹如何快速地將騰訊云 IM SDK 集成到您的 Web 項(xiàng)目中。
騰訊云即時(shí)通信IM SDK API 文檔
示例demo下載
準(zhǔn)備工作
- 在騰訊云官網(wǎng)上創(chuàng)建應(yīng)用,獲取到相應(yīng)的SDKAppID和相應(yīng)的秘鑰信息
- 安裝sdk
集成 SDK
// IM Web SDK
// 從v2.11.2起,SDK 支持了 WebSocket,推薦接入;v2.10.2及以下版本,使用 HTTP
npm install tim-js-sdk --save
// 發(fā)送圖片、文件等消息需要騰訊云即時(shí)通信 IM 上傳插件
npm install tim-upload-plugin --save
在項(xiàng)目腳本里引入模塊。
// 從v2.11.2起,SDK 支持了 WebSocket,推薦接入;v2.10.2及以下版本,使用 HTTP
import TIM from 'tim-js-sdk';
import TIMUploadPlugin from 'tim-upload-plugin';
let options = {
SDKAppID: xxxxxxxx // 接入時(shí)需要將0替換為您的即時(shí)通信 IM 應(yīng)用的 SDKAppID
};
// 創(chuàng)建 SDK 實(shí)例,`TIM.create()`方法對(duì)于同一個(gè) `SDKAppID` 只會(huì)返回同一份實(shí)例
let tim = TIM.create(options); // SDK 實(shí)例通常用 tim 表示
// 設(shè)置 SDK 日志輸出級(jí)別,詳細(xì)分級(jí)請(qǐng)參見(jiàn) <a >setLogLevel 接口的說(shuō)明</a>
tim.setLogLevel(0); // 普通級(jí)別,日志量較多,接入時(shí)建議使用
// tim.setLogLevel(1); // release 級(jí)別,SDK 輸出關(guān)鍵信息,生產(chǎn)環(huán)境時(shí)建議使用
// 注冊(cè)騰訊云即時(shí)通信 IM 上傳插件
tim.registerPlugin({'tim-upload-plugin': TIMUploadPlugin});
初始化與登錄(Web)
1. 創(chuàng)建 SDK 實(shí)例
請(qǐng)參考上面的集成SDK方法
2. 事件綁定
// 監(jiān)聽(tīng)事件,例如:
tim.on(TIM.EVENT.SDK_READY, function(event) {
// 收到離線消息和會(huì)話列表同步完畢通知,接入側(cè)可以調(diào)用 sendMessage 等需要鑒權(quán)的接口
// event.name - TIM.EVENT.SDK_READY
});
tim.on(TIM.EVENT.MESSAGE_RECEIVED, function(event) {
// 收到推送的單聊、群聊、群提示、群系統(tǒng)通知的新消息,可通過(guò)遍歷 event.data 獲取消息列表數(shù)據(jù)并渲染到頁(yè)面
// event.name - TIM.EVENT.MESSAGE_RECEIVED
// event.data - 存儲(chǔ) Message 對(duì)象的數(shù)組 - [Message]
});
tim.on(TIM.EVENT.MESSAGE_REVOKED, function(event) {
// 收到消息被撤回的通知
// event.name - TIM.EVENT.MESSAGE_REVOKED
// event.data - 存儲(chǔ) Message 對(duì)象的數(shù)組 - [Message] - 每個(gè) Message 對(duì)象的 isRevoked 屬性值為 true
});
tim.on(TIM.EVENT.MESSAGE_READ_BY_PEER, function(event) {
// SDK 收到對(duì)端已讀消息的通知,即已讀回執(zhí)。使用前需要將 SDK 版本升級(jí)至 v2.7.0 或以上。僅支持單聊會(huì)話。
// event.name - TIM.EVENT.MESSAGE_READ_BY_PEER
// event.data - event.data - 存儲(chǔ) Message 對(duì)象的數(shù)組 - [Message] - 每個(gè) Message 對(duì)象的 isPeerRead 屬性值為 true
});
tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, function(event) {
// 收到會(huì)話列表更新通知,可通過(guò)遍歷 event.data 獲取會(huì)話列表數(shù)據(jù)并渲染到頁(yè)面
// event.name - TIM.EVENT.CONVERSATION_LIST_UPDATED
// event.data - 存儲(chǔ) Conversation 對(duì)象的數(shù)組 - [Conversation]
});
tim.on(TIM.EVENT.GROUP_LIST_UPDATED, function(event) {
// 收到群組列表更新通知,可通過(guò)遍歷 event.data 獲取群組列表數(shù)據(jù)并渲染到頁(yè)面
// event.name - TIM.EVENT.GROUP_LIST_UPDATED
// event.data - 存儲(chǔ) Group 對(duì)象的數(shù)組 - [Group]
});
tim.on(TIM.EVENT.PROFILE_UPDATED, function(event) {
// 收到自己或好友的資料變更通知
// event.name - TIM.EVENT.PROFILE_UPDATED
// event.data - 存儲(chǔ) Profile 對(duì)象的數(shù)組 - [Profile]
});
tim.on(TIM.EVENT.BLACKLIST_UPDATED, function(event) {
// 收到黑名單列表更新通知
// event.name - TIM.EVENT.BLACKLIST_UPDATED
// event.data - 存儲(chǔ) userID 的數(shù)組 - [userID]
});
tim.on(TIM.EVENT.ERROR, function(event) {
// 收到 SDK 發(fā)生錯(cuò)誤通知,可以獲取錯(cuò)誤碼和錯(cuò)誤信息
// event.name - TIM.EVENT.ERROR
// event.data.code - 錯(cuò)誤碼
// event.data.message - 錯(cuò)誤信息
});
tim.on(TIM.EVENT.SDK_NOT_READY, function(event) {
// 收到 SDK 進(jìn)入 not ready 狀態(tài)通知,此時(shí) SDK 無(wú)法正常工作
// event.name - TIM.EVENT.SDK_NOT_READY
});
tim.on(TIM.EVENT.KICKED_OUT, function(event) {
// 收到被踢下線通知
// event.name - TIM.EVENT.KICKED_OUT
// event.data.type - 被踢下線的原因,例如:
// - TIM.TYPES.KICKED_OUT_MULT_ACCOUNT 多實(shí)例登錄被踢
// - TIM.TYPES.KICKED_OUT_MULT_DEVICE 多終端登錄被踢
// - TIM.TYPES.KICKED_OUT_USERSIG_EXPIRED 簽名過(guò)期被踢 (v2.4.0起支持)。
});
tim.on(TIM.EVENT.NET_STATE_CHANGE, function(event) {
// 網(wǎng)絡(luò)狀態(tài)發(fā)生改變(v2.5.0 起支持)。
// event.name - TIM.EVENT.NET_STATE_CHANGE
// event.data.state 當(dāng)前網(wǎng)絡(luò)狀態(tài),枚舉值及說(shuō)明如下:
// \- TIM.TYPES.NET_STATE_CONNECTED - 已接入網(wǎng)絡(luò)
// \- TIM.TYPES.NET_STATE_CONNECTING - 連接中。很可能遇到網(wǎng)絡(luò)抖動(dòng),SDK 在重試。接入側(cè)可根據(jù)此狀態(tài)提示“當(dāng)前網(wǎng)絡(luò)不穩(wěn)定”或“連接中”
// \- TIM.TYPES.NET_STATE_DISCONNECTED - 未接入網(wǎng)絡(luò)。接入側(cè)可根據(jù)此狀態(tài)提示“當(dāng)前網(wǎng)絡(luò)不可用”。SDK 仍會(huì)繼續(xù)重試,若用戶網(wǎng)絡(luò)恢復(fù),SDK 會(huì)自動(dòng)同步消息
});
// 開(kāi)始登錄
tim.login({userID: 'your userID', userSig: 'your userSig'});
3. 登錄
用戶登錄 IM SDK 才能正常收發(fā)消息,登錄需要用戶提供 UserID、UserSig 等信息,具體含義請(qǐng)參見(jiàn) 登錄鑒權(quán)。登錄成功后,需要先等 SDK 處于 ready 狀態(tài)才能調(diào)用 sendMessage 等需要鑒權(quán)的接口,您可以通過(guò)監(jiān)聽(tīng)事件 TIM.EVENT.SDK_READY 獲取 SDK 狀態(tài)。
// userID [String] 用戶 ID。
// userSig [String] 用戶登錄即時(shí)通信 IM 的密碼,其本質(zhì)是對(duì) UserID 等信息加密后得到的密文。具體生成方法請(qǐng)參見(jiàn) 生成 UserSig。
tim.login({userID: xxx, userSig: xxx});
返回值
該接口返回 Promise 對(duì)象。
tim.login({userID: 'your userID', userSig: 'your userSig'})
.then(function(imResponse) {
console.log(imResponse.data); // 登錄成功
if (imResponse.data.repeatLogin === true) {
// 標(biāo)識(shí)賬號(hào)已登錄,本次登錄操作為重復(fù)登錄。v2.5.1 起支持
console.log(imResponse.data.errorInfo);
}
}).catch(function(imError) {
console.warn('login error:', imError); // 登錄失敗的相關(guān)信息
});
4. 登出
// then的回調(diào)函數(shù)參數(shù)為 IMResponse,IMResponse.data為空對(duì)象。表示成功登出
tim.logout().then(function(imResponse) {
console.log(imResponse.data); // 登出成功
}).catch(function(imError) {
console.warn('logout error:', imError);
});
消息收發(fā)(Web)
一. 發(fā)送消息
發(fā)送消息需先創(chuàng)建消息內(nèi)容對(duì)象, 再通過(guò)發(fā)送消息接口發(fā)送消息給指定用戶或群組
1. 創(chuàng)建文本消息
tim.createTextMessage({
// [String] 消息接收方的 userID 或 groupID
to,
// [String] 會(huì)話類(lèi)型,取值TIM.TYPES.CONV_C2C(端到端會(huì)話)或TIM.TYPES.CONV_GROUP(群組會(huì)話)
conversationType,
// [String] 消息優(yōu)先級(jí)(默認(rèn)值: TIM.TYPES.MSG_PRIORITY_NORMAL)
priority,
// [Object] 消息內(nèi)容的容器
payload
})
payload 內(nèi)容格式如下:
| Name | Type | Description |
|---|---|---|
| text | String | 消息文本內(nèi)容 |
示例
// 發(fā)送文本消息,Web 端與小程序端相同
// 1. 創(chuàng)建消息實(shí)例,接口返回的實(shí)例可以上屏
let message = tim.createTextMessage({
to: 'user1',
conversationType: TIM.TYPES.CONV_C2C,
// 消息優(yōu)先級(jí),用于群聊(v2.4.2起支持)。如果某個(gè)群的消息超過(guò)了頻率限制,后臺(tái)會(huì)優(yōu)先下發(fā)高優(yōu)先級(jí)的消息,詳細(xì)請(qǐng)參考:https://cloud.tencent.com/document/product/269/3663#.E6.B6.88.E6.81.AF.E4.BC.98.E5.85.88.E7.BA.A7.E4.B8.8E.E9.A2.91.E7.8E.87.E6.8E.A7.E5.88.B6)
// 支持的枚舉值:TIM.TYPES.MSG_PRIORITY_HIGH, TIM.TYPES.MSG_PRIORITY_NORMAL(默認(rèn)), TIM.TYPES.MSG_PRIORITY_LOW, TIM.TYPES.MSG_PRIORITY_LOWEST
// priority: TIM.TYPES.MSG_PRIORITY_NORMAL,
payload: {
text: 'Hello world!'
}
});
// 2. 發(fā)送消息
tim.sendMessage(message).then(function(imResponse) {
// 發(fā)送成功
console.log(imResponse);
}).catch(function(imError) {
// 發(fā)送失敗
console.warn('sendMessage error:', imError);
});
2. 創(chuàng)建圖片消息
tim.createImageMessage({
// [String] 消息接收方的 userID 或 groupID
to,
// [String] 會(huì)話類(lèi)型,取值TIM.TYPES.CONV_C2C(端到端會(huì)話)或TIM.TYPES.CONV_GROUP(群組會(huì)話)
conversationType,
// [String] 消息優(yōu)先級(jí)(默認(rèn)值: TIM.TYPES.MSG_PRIORITY_NORMAL)
priority,
// [Object] 消息內(nèi)容的容器
payload
// [function] 獲取上傳進(jìn)度的回調(diào)函數(shù)
onProgress
})
payload 內(nèi)容格式如下:
| Name | Type | Description |
|--|--|-|--
| text | HTMLInputElement | Object| 用于選擇圖片的 DOM 節(jié)點(diǎn)(Web)或者 File 對(duì)象(Web)或者微信小程序 wx.chooseImage 接口的 success 回調(diào)參數(shù)。SDK 會(huì)讀取其中的數(shù)據(jù)并上傳圖片 |
示例
// Web 端發(fā)送圖片消息示例1 - 傳入 DOM 節(jié)點(diǎn)
// 1. 創(chuàng)建消息實(shí)例,接口返回的實(shí)例可以上屏
let message = tim.createImageMessage({
to: 'user1',
conversationType: TIM.TYPES.CONV_C2C,
// 消息優(yōu)先級(jí),用于群聊(v2.4.2起支持)。如果某個(gè)群的消息超過(guò)了頻率限制,后臺(tái)會(huì)優(yōu)先下發(fā)高優(yōu)先級(jí)的消息,詳細(xì)請(qǐng)參考 消息優(yōu)先級(jí)與頻率控制
// 支持的枚舉值:TIM.TYPES.MSG_PRIORITY_HIGH, TIM.TYPES.MSG_PRIORITY_NORMAL(默認(rèn)), TIM.TYPES.MSG_PRIORITY_LOW, TIM.TYPES.MSG_PRIORITY_LOWEST
// priority: TIM.TYPES.MSG_PRIORITY_NORMAL,
payload: {
file: document.getElementById('imagePicker'),
},
onProgress: function(event) { console.log('file uploading:', event) }
});
// 2. 發(fā)送消息
let promise = tim.sendMessage(message);
promise.then(function(imResponse) {
// 發(fā)送成功
console.log(imResponse);
}).catch(function(imError) {
// 發(fā)送失敗
console.warn('sendMessage error:', imError);
});
// Web 端發(fā)送圖片消息示例2- 傳入 File 對(duì)象
// 先在頁(yè)面上添加一個(gè) ID 為 "testPasteInput" 的消息輸入框,例如 <input type="text" id="testPasteInput" placeholder="截圖后粘貼到輸入框中" size="30" />
document.getElementById('testPasteInput').addEventListener('paste', function(e) {
let clipboardData = e.clipboardData;
let file;
let fileCopy;
if (clipboardData && clipboardData.files && clipboardData.files.length > 0) {
file = clipboardData.files[0];
// 圖片消息發(fā)送成功后,file 指向的內(nèi)容可能被瀏覽器清空,如果接入側(cè)有額外的渲染需求,可以提前復(fù)制一份數(shù)據(jù)
fileCopy = file.slice();
}
if (typeof file === 'undefined') {
console.warn('file 是 undefined,請(qǐng)檢查代碼或?yàn)g覽器兼容性!');
return;
}
// 1. 創(chuàng)建消息實(shí)例,接口返回的實(shí)例可以上屏
let message = tim.createImageMessage({
to: 'user1',
conversationType: TIM.TYPES.CONV_C2C,
payload: {
file: file
},
onProgress: function(event) { console.log('file uploading:', event) }
});
// 2. 發(fā)送消息
let promise = tim.sendMessage(message);
promise.then(function(imResponse) {
// 發(fā)送成功
console.log(imResponse);
}).catch(function(imError) {
// 發(fā)送失敗
console.warn('sendMessage error:', imError);
});
});
二. 撤回消息
撤回單聊消息或者群聊消息。撤回成功后,消息對(duì)象的 isRevoked 屬性值為 true。
示例
// 主動(dòng)撤回消息
let promise = tim.revokeMessage(message);
promise.then(function(imResponse) {
// 消息撤回成功
}).catch(function(imError) {
// 消息撤回失敗
console.warn('revokeMessage error:', imError);
});
tim.on(TIM.EVENT.MESSAGE_REVOKED, function(event) {
// 收到消息被撤回的通知。使用前需要將 SDK 版本升級(jí)至v2.4.0或以上。
// event.name - TIM.EVENT.MESSAGE_REVOKED
// event.data - 存儲(chǔ) Message 對(duì)象的數(shù)組 - [Message] - 每個(gè) Message 對(duì)象的 isRevoked 屬性值為 true
});
// 獲取會(huì)話的消息列表時(shí)遇到被撤回的消息
let promise = tim.getMessageList({conversationID: 'C2Ctest', count: 15});
promise.then(function(imResponse) {
const messageList = imResponse.data.messageList; // 消息列表
messageList.forEach(function(message) {
if (message.isRevoked) {
// 處理被撤回的消息
} else {
// 處理普通消息
}
});
});
三. 接收消息
接收消息需要通過(guò)事件監(jiān)聽(tīng)實(shí)現(xiàn)
let onMessageReceived = function(event) {
// event.data - 存儲(chǔ) Message 對(duì)象的數(shù)組 - [Message]
};
tim.on(TIM.EVENT.MESSAGE_RECEIVED, onMessageReceived);
1. 解析文本消息
如果您的文本消息只含有文字,則可以直接在 UI 上渲染出'xxxxxxx'文字。
含有 [呲牙] 內(nèi)容需要解析為的文本參考下方
const emojiMap = { // 根據(jù)[呲牙]可匹配的路徑地址
'[微笑]': 'emoji_0.png',
'[呲牙]': 'emoji_1.png',
'[下雨]': 'emoji_2.png'
}
const emojiUrl = 'http://xxxxxxxx/emoji/' // 為<img src="https://main.qcloudimg.com/raw/6be88c30a4552b5eb93d8eec243b6593.png" style="margin:0;">圖片的地址
function parseText (payload) {
let renderDom = []
// 文本消息
let temp = payload.text
let left = -1
let right = -1
while (temp !== '') {
left = temp.indexOf('[')
right = temp.indexOf(']')
switch (left) {
case 0:
if (right === -1) {
renderDom.push({
name: 'text',
text: temp
})
temp = ''
} else {
let _emoji = temp.slice(0, right + 1)
if (emojiMap[_emoji]) { // 如果您需要渲染表情包,需要進(jìn)行匹配您對(duì)應(yīng)[呲牙]的表情包地址
renderDom.push({
name: 'img',
src: emojiUrl + emojiMap[_emoji]
})
temp = temp.substring(right + 1)
} else {
renderDom.push({
name: 'text',
text: '['
})
temp = temp.slice(1)
}
}
break
case -1:
renderDom.push({
name: 'text',
text: temp
})
temp = ''
break
default:
renderDom.push({
name: 'text',
text: temp.slice(0, left)
})
temp = temp.substring(left)
break
}
}
return renderDom
}
// 最后的 renderDom 結(jié)構(gòu)為[{name: 'text', text: 'XXX'}, {name: 'img', src: 'http://xxx'}......]
// 渲染當(dāng)前數(shù)組即可得到想要的 UI 結(jié)果,如:XXX<img src="https://main.qcloudimg.com/raw/6be88c30a4552b5eb93d8eec243b6593.png" style="margin:0;">XXX<img src="https://main.qcloudimg.com/raw/6be88c30a4552b5eb93d8eec243b6593.png" style="margin:0;">XXX[呲牙XXX]
2. 解析系統(tǒng)消息
function parseGroupSystemNotice (payload) {
const groupName =
payload.groupProfile.groupName || payload.groupProfile.groupID
switch (payload.operationType) {
case 1:
return `${payload.operatorID} 申請(qǐng)加入群組:${groupName}`
case 2:
return `成功加入群組:${groupName}`
case 3:
return `申請(qǐng)加入群組:${groupName}被拒絕`
case 4:
return `被管理員${payload.operatorID}踢出群組:${groupName}`
case 5:
return `群:${groupName} 已被${payload.operatorID}解散`
case 6:
return `${payload.operatorID}創(chuàng)建群:${groupName}`
case 7:
return `${payload.operatorID}邀請(qǐng)你加群:${groupName}`
case 8:
return `你退出群組:${groupName}`
case 9:
return `你被${payload.operatorID}設(shè)置為群:${groupName}的管理員`
case 10:
return `你被${payload.operatorID}撤銷(xiāo)群:${groupName}的管理員身份`
case 255:
return '自定義群系統(tǒng)通知'
}
}
3. 解析群提示消息
function parseGroupTipContent (payload) {
switch (payload.operationType) {
case this.TIM.TYPES.GRP_TIP_MBR_JOIN:
return `群成員:${payload.userIDList.join(',')},加入群組`
case this.TIM.TYPES.GRP_TIP_MBR_QUIT:
return `群成員:${payload.userIDList.join(',')},退出群組`
case this.TIM.TYPES.GRP_TIP_MBR_KICKED_OUT:
return `群成員:${payload.userIDList.join(',')},被${payload.operatorID}踢出群組`
case this.TIM.TYPES.GRP_TIP_MBR_SET_ADMIN:
return `群成員:${payload.userIDList.join(',')},成為管理員`
case this.TIM.TYPES.GRP_TIP_MBR_CANCELED_ADMIN:
return `群成員:${payload.userIDList.join(',')},被撤銷(xiāo)管理員`
default:
return '[群提示消息]'
}
}
會(huì)話相關(guān)
1. 獲取某會(huì)話的消息列表
// 打開(kāi)某個(gè)會(huì)話時(shí),第一次拉取消息列表
let promise = tim.getMessageList({conversationID: 'C2Ctest', count: 15});
promise.then(function(imResponse) {
const messageList = imResponse.data.messageList; // 消息列表。
const nextReqMessageID = imResponse.data.nextReqMessageID; // 用于續(xù)拉,分頁(yè)續(xù)拉時(shí)需傳入該字段。
const isCompleted = imResponse.data.isCompleted; // 表示是否已經(jīng)拉完所有消息。
});
// 打開(kāi)某個(gè)會(huì)話時(shí),第一次拉取消息列表
// 下拉查看更多消息
let promise = tim.getMessageList({conversationID: 'C2Ctest', nextReqMessageID, count: 15});
promise.then(function(imResponse) {
const messageList = imResponse.data.messageList; // 消息列表。
const nextReqMessageID = imResponse.data.nextReqMessageID; // 用于續(xù)拉,分頁(yè)續(xù)拉時(shí)需傳入該字段。
const isCompleted = imResponse.data.isCompleted; // 表示是否已經(jīng)拉完所有消息。
});
2. 將會(huì)話設(shè)置為已讀
// 將某會(huì)話下所有未讀消息已讀上報(bào)
tim.setMessageRead({conversationID: 'C2Cexample'});
3. 獲取會(huì)話列表
// 拉取會(huì)話列表
let promise = tim.getConversationList();
promise.then(function(imResponse) {
const conversationList = imResponse.data.conversationList; // 會(huì)話列表,用該列表覆蓋原有的會(huì)話列表
}).catch(function(imError) {
console.warn('getConversationList error:', imError); // 獲取會(huì)話列表失敗的相關(guān)信息
});
4. 刪除會(huì)話
let promise = tim.deleteConversation('C2CExample');
promise.then(function(imResponse) {
//刪除成功。
const { conversationID } = imResponse.data;// 被刪除的會(huì)話 ID。
}).catch(function(imError) {
console.warn('deleteConversation error:', imError); // 刪除會(huì)話失敗的相關(guān)信息
});