シナリオ概要
このチュートリアルは、SalesforceのワークフローにChat SDKを統合し、Salesforceエージェントとエンドユーザー間のコミュニケーションを活用するアプローチを示すことを目的としています。
適用製品
基本的な統合ガイドライン
前提条件
1. Tencent RTCにサインアップし、アプリを作成してチャットサービスを登録します。詳細はガイダンスを参照してください。
2. Salesforceの開発者アカウントにサインアップしてください。まだお持ちでない場合は、こちらをクリックしてください ここ。
ロードマップ
目標を達成するためには、3つの部分が不可欠です。
1. エンドユーザーが会話を開始するためのエンドユーザーアプリケーション。
2. Salesforceケースを作成し、チャットグループを作成するためのオンラインサーバー。また、Salesforceエージェントをグループに招待/削除し、ケースが閉じた際にグループを解散するためのAPIを提供します。
3. Salesforce内のコミュニケーションのためのカスタムSalesforceユーティリティコンポーネント。
以下が統合マップです:

エンドユーザーアプリケーション
チャットは最も人気のあるプラットフォーム向けにさまざまなSDKを提供しており、プラットフォームに応じて選択できます。SDKについてはAndroid, iOS, Web, Flutter, Windows, Unity, Unreal Engine。アプリケーションをゼロから構築する推奨方法は、インターフェースを重ねるためにTUIKitを利用することです。こちらを参照してください:Android, iOS, Web, Flutter。
オンラインサーバーレレー
ここでは、エンドユーザーがSalesforceケースを提出し、チャットグループを作成できるようにするウェブサーバーをステップバイステップで作成する手順を説明します。また、Salesforceエージェントをチャットグループに招待または削除し、ケースが閉じたときにグループを削除します。
エージェントチャットインターフェース
このチュートリアルでは、Salesforceにチャットインターフェースを作成し、エージェントをチャットグループに招待する方法を説明します。また、私たちのWeb UIKitを使用して、このプラグインウィジェットを構築できます。
シナリオ特有の実装
オンラインサーバーを作成する
オンラインサーバーは、SalesforceとChatを接続し、エンドユーザーがSalesforceケースを作成し、対応するチャットグループを作成できるようにするためのものです。以下に示すのは、例としてのNodeサーバーです:
const express = require("express")
const axios = require("axios")
var TLSSigAPIv2 = require("tls-sig-api-v2") // チャット用のUserSigを生成
const sf = require("node-salesforce") // Node.jsアプリケーション用のSalesforce API接続ライブラリ
const YOUR_SDKAPPID = 1400000000
const YOUR_SECRET = ""
const ADMIN_USERID = ""
const app = express()
app.use(express.json())
const port = process.env.PORT || 3000
app.use(express.json())
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "https://YOUR_DOMAIN")
res.header("Access-Control-Allow-Headers", "*")
next()
})
// エンドユーザーが/createticketを呼び出してSalesforceケースを作成し、チャットグループを作成します。Salesforceエージェントがグループに参加するのを待っています。
app.post("/createticket", async (req, res) => {
const { userId, caseInfo } = req.body
if (!userId) return res.status(500).send("userIdが不足しています")
const auth = await getSalesforceAccessToken()
if (auth.error) return res.status(500).send("Salesforce認証エラー")
const salesforceCase = await createCase(auth.token, caseInfo)
if (!salesforceCase.success)
return res.status(500).send("ケース作成に失敗しました")
const groupName = salesforceCase.id
const result = await createGroup(groupName, userId)
if (result.ErrorCode !== 0)
return res.status(500).send("グループ作成に失敗しました")
res.status(200).send(result)
})
// 新しいエージェントがグループに割り当てられたことを検出したとき、Salesforceはこのエージェントをチャットグループに参加させるリクエストを送信します
app.post("/joingroup", async (req, res) => {
const { groupId, userId } = req.body
if (!userId) return res.status(500).send("userIdが不足しています")
if (!groupId) return res.status(500).send("groupIdが不足しています")
const result = await joinGroup(groupId, userId)
if (result.ErrorCode !== 0)
return res.status(500).send("グループ参加に失敗しました")
res.status(200).send(result)
})
// エージェントがグループから削除されたことを検出したとき、Salesforceはこのエージェントをチャットグループから削除するリクエストを送信します
app.post("/leavegroup", async (req, res) => {
const { groupId, userId } = req.body
if (!userId) return res.status(500).send("userIdが不足しています")
if (!groupId) return res.status(500).send("groupIdが不足しています")
const result = await leaveGroup(groupId, userId)
if (result.ErrorCode !== 0)
return res.status(500).send("グループから離れるのに失敗しました")
res.status(200).send(result)
})
app.post("/deletegroup", async (req, res) => {
const { groupId } = req.body
if (!groupId) return res.status(500).send("groupIdが不足しています")
const result = await deleteGroup(groupId)
if (result.ErrorCode !== 0)
return res.status(500).send("グループ削除に失敗しました")
res.status(200).send(result)
})
const getSalesforceAccessToken = async function () {
const url = "https://{your_instance}.salesforce.com"
const conn = new sf.Connection({ loginUrl: url })
try {
await conn.login("SF_EMAIL", "SF_PASSWORDSF_TOKEN")
return { error: undefined, token: conn.accessToken }
} catch (e) {
return { error: e, token: undefined }
}
}
const createCase = async function (token, caseInfo) {
const { subject, desc, name, email } = caseInfo
const body = {
Subject: subject,
Description: desc,
SuppliedName: name,
SuppliedEmail: email,
}
const headers = {
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
}
const url =
"https://{your_instance}.salesforce.com/services/data/v{api_version}/sobjects/Case"
try {
const result = await axios.post(url, body, headers)
return result.data
} catch (e) {
return { id: undefined, success: false, error: e }
}
}
const generateUserSig = function () {
const expires = 600
const api = new TLSSigAPIv2.Api(YOUR_SDKAPPID, YOUR_SECRET)
return api.genSig(ADMIN_USERID, expires)
}
const generateRandom = function () {
return Math.floor(Math.random() * 4294967295)
}
const createGroup = async function (groupName, userId) {
const sig = generateUserSig()
const random = generateRandom()
// salesforceCase.idをグループIDとして使用
const data = { Owner_Account: userId, Type: "Public", Name: groupName, GroupId: groupName }
const url = `https://console.tim.qq.com/v4/group_open_http_svc/create_group?sdkappid=${YOUR_SDKAPPID}&identifier=${ADMIN_USERID}&usersig=${sig}&random=${random}&contenttype=json`
try {
const groupRes = await axios.post(url, data)
return groupRes.data
} catch (e) {
return { ErrorCode: -1, ErrorInfo: e }
}
}
const joinGroup = async function (groupId, userId) {
const sig = generateUserSig()
const random = generateRandom()
const data = { GroupId: groupId, MemberList: [{ Member_Account: userId }] }
const url = `https://console.tim.qq.com/v4/group_open_http_svc/add_group_member?sdkappid=${YOUR_SDKAPPID}&identifier=${ADMIN_USERID}&usersig=${sig}&random=${random}&contenttype=json`
try {
const groupRes = await axios.post(url, data)
return groupRes.data
} catch (e) {
return { ErrorCode: -1, ErrorInfo: e }
}
}
const leaveGroup = async function (groupId, userId) {
const sig = generateUserSig()
const random = generateRandom()
const data = { GroupId: groupId, MemberToDel_Account: [userId] }
const url = `https://console.tim.qq.com/v4/group_open_http_svc/delete_group_member?sdkappid=${YOUR_SDKAPPID}&identifier=${ADMIN_USERID}&usersig=${sig}&random=${random}&contenttype=json`
try {
const groupRes = await axios.post(url, data)
return groupRes.data
} catch (e) {
return { ErrorCode: -1, ErrorInfo: e }
}
}
const deleteGroup = async function (groupId) {
const sig = generateUserSig()
const random = generateRandom()
const data = { GroupId: groupId }
const url = `https://console.tim.qq.com/v4/group_open_http_svc/destroy_group?sdkappid=${YOUR_SDKAPPID}&identifier=${ADMIN_USERID}&usersig=${sig}&random=${random}&contenttype=json`
try {
const groupRes = await axios.post(url, data)
return groupRes.data
} catch (e) {
return { ErrorCode: -1, ErrorInfo: e }
}
}
app.listen(process.env.PORT || port, () =>
console.log(`Example app listening on port ${port}!`)
)
サーバーは、エンドユーザーがSalesforceでケースを作成し、チャットでグループを作成するための/createticketというルートをサポートします。ここで行うことは次のとおりです:
1. 最初に、SalesforceからaccessTokenを取得します。詳細はSF_TOKEN。
const getSalesforceAccessToken = async function () {
const url = "https://{your_instance}.salesforce.com"
const conn = new sf.Connection({ loginUrl: url })
try {
await conn.login("SF_EMAIL", "SF_PASSWORDSF_TOKEN")
return { error: undefined, token: conn.accessToken }
} catch (e) {
return { error: e, token: undefined }
}
}
2.Salesforceケースを作成します。
const createCase = async function (token, caseInfo) {
const { subject, desc, name, email } = caseInfo
const body = {
Subject: subject,
Description: desc,
SuppliedName: name,
SuppliedEmail: email,
}
const headers = {
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
}
const url =
"https://{your_instance}.salesforce.com/services/data/v{api_version}/sobjects/Case"
try {
const result = await axios.post(url, body, headers)
return result.data
} catch (e) {
return { id: undefined, success: false, error: e }
}
}
3. チャット用のUserSigを生成します。
const generateUserSig = function () {
const expires = 600
const api = new TLSSigAPIv2.Api(YOUR_SDKAPPID, YOUR_SECRET)
return api.genSig(ADMIN_USERID, expires)
}
4. ケースIDとユーザーIDを使ってチャットグループを作成します。
const createGroup = async function (groupName, userId) {
const sig = generateUserSig()
const random = generateRandom()
const data = { Owner_Account: userId, Type: "Public", Name: groupName }
const url = `https://console.tim.qq.com/v4/group_open_http_svc/create_group?sdkappid=${YOUR_SDKAPPID}&identifier=${ADMIN_USERID}&usersig=${sig}&random=${random}&contenttype=json`
try {
const groupRes = await axios.post(url, data)
return groupRes.data
} catch (e) {
return { ErrorCode: -1, ErrorInfo: e }
}
}
さらに、ウェブサーバーは、ケースIDとエージェントIDによるチャットグループへの参加/離脱/削除のルートを提供します。これらは、Salesforceトリガーがケースエージェントの変更を検出するときに使用されます。詳細はステップ3で説明します。
const joinGroup = async function (groupId, userId) {
const sig = generateUserSig()
const random = generateRandom()
const data = { GroupId: groupId, MemberList: [{ Member_Account: userId }] }
const url = `https://console.tim.qq.com/v4/group_open_http_svc/add_group_member?sdkappid=${YOUR_SDKAPPID}&identifier=${ADMIN_USERID}&usersig=${sig}&random=${random}&contenttype=json`
try {
const groupRes = await axios.post(url, data)
return groupRes.data
} catch (e) {
return { ErrorCode: -1, ErrorInfo: e }
}
}
const leaveGroup = async function (groupId, userId) {
const sig = generateUserSig()
const random = generateRandom()
const data = { GroupId: groupId, MemberToDel_Account: [userId] }
const url = `https://console.tim.qq.com/v4/group_open_http_svc/delete_group_member?sdkappid=${YOUR_SDKAPPID}&identifier=${ADMIN_USERID}&usersig=${sig}&random=${random}&contenttype=json`
try {
const groupRes = await axios.post(url, data)
return groupRes.data
} catch (e) {
return { ErrorCode: -1, ErrorInfo: e }
}
}
const deleteGroup = async function (groupId) {
const sig = generateUserSig()
const random = generateRandom()
const data = { GroupId: groupId }
const url = `https://console.tim.qq.com/v4/group_open_http_svc/destroy_group?sdkappid=${YOUR_SDKAPPID}&identifier=${ADMIN_USERID}&usersig=${sig}&random=${random}&contenttype=json`
try {
const groupRes = await axios.post(url, data)
return groupRes.data
} catch (e) {
return { ErrorCode: -1, ErrorInfo: e }
}
}
これでウェブサーバー側は完了です。サーバーを設定したら、/createticketエンドポイントを呼び出し:
- Salesforceでケースが作成されていることを確認します。
- ケースIDをグループIDとして持つチャットコンソールを確認します。
Chat Web UI Kitを使用してSalesforceユーティリティコンポーネントを構築する
ここでは、Chat UI Kitを使用してSalesforceユーティリティコンポーネントを作成する手順を示します。Salesforceでは、Lightning Containerを使用して、サードパーティのi-frameを静的リソースとしてアップロードし、Auraコンポーネント内でlightning:container
を使用してコンテンツをホストできます。そして、Chat Web UIKitを使用してエージェントチャットコンポーネントを構築し、Lightning Container内にSalesforceユーティリティバーウィジェットとして展開します。
1. まず、Chat Web UIKitを使用してチャットコンポーネントを開発します。それを静的リソースとしてルートindex.htmlで構築し、zipファイルとして圧縮します。ケースエージェントのIDはチャットコンポーネントに送信され、そのIDを使用してチャットに初期化とログインを行います。

2. コンポーネント用のLightning Containerを作成します。詳細は こちら。
2.1 Salesforce Developer Console
2.2 ファイルをクリック -> 新規 -> Lightning Component
2.3 名前 = "tim_utilities_bar"
2.4 提出をクリック
3. カスタムコンポーネントをユーティリティバーウィジェットにレンダリングします。3.1 aura:componentをユーティリティバーとして設定し、aura:idを提供します。
<!-- tim_utilities_bar.cmp -->
<aura:component implements="flexipage:availableForAllPageTypes" access="global">
<lightning:utilityBarAPI aura:id='utilitybar'" />
</aura:component>
3.2 静的リソースをLightning Containerにアップロードします。
a. Salesforceの静的リソースに移動し、「tim_bar」という新しいリソースを作成します。
b. リソースのzipファイルをアップロードし、「キャッシュ制御」を「公開」に設定します
c. 保存をクリックします。
注意事項:
- Index.htmlは常に.zipファイルのルートレベルに配置する必要があります。
- ファイルをアップロードした後は必ず「保存」をクリックしてください。
- Salesforceはリソース名を保存し、.zipファイルの名前は保存しません。
- Lightning Componentで使用するコードとアセットはすべて.zipファイルに含める必要があります。CSP信頼済みサイトリストにホワイトリストされた外部コード依存関係は機能しません。
3.3 ユーティリティバーウィジェットに静的リソース「tim_bar」を参照します。
a. ユーティリティバーウィジェットにlightning:containerタグを追加します。aura:idは「TIM_Bar」である必要があります。b. 静的リソースを参照します。"{!$Resource.tim_bar + '/index.html'}"。tim_barは保存された「静的リソース」であり、アップロードされた.zipファイルの名前ではありません。
<!-- tim_utilities_bar.cmp -->
<aura:component implements="flexipage:availableForAllPageTypes" access="global">
<lightning:utilityBarAPI aura:id='utilitybar'" />
<aura:attribute name="recordId" type="String" />
<aura:attribute name="data" type="String" />
<lightning:navigation aura:id='navService'" />
<lightning:container
aura:id='TIM_Bar'"
src="{!$Resource.tim_bar + '/index.html'}"
/>
</aura:component>
3.4 Salesforceに表示されるようにユーティリティバーウィジェットを追加します
- 「設定」をクリックし、「App Manager」を検索してユーティリティバーウィジェットが表示される場所を設定します
- アプリ「Service Console」で「▾」をクリックし、「編集」をクリックします。
- アプリ設定で、「ユーティリティアイテム(デスクトップのみ)」をクリックします
- 「ユーティリティアイテムを追加」をクリックします。
- 「tim_utilities_bar」を選択します
- 幅と高さを設定します
- 「自動的に開始」をチェックします
- 「保存」をクリックします。
3.5 Salesforce CSPファイルを更新して、Salesforce内のチャットアクセスを許可します
- Salesforceに移動 -> 設定 -> 検索 -> 「CSP Trusted sites」
- 「新しい信頼済みサイト」を追加します(すべてのCSPディレクティブを許可):wss://wss.im.qcloud.com
- Salesforceに移動 -> 設定 -> 検索 -> 「CORS」
- 「新規」許可されたオリジンリストを追加します:https://.qq.com https://.qcloud.com
- Salesforceに移動 -> 設定 -> 検索 -> ""
- 新しいリモートサイトリストを追加します!ウェブサーバーのURL。
4. Lightning Containerを初期化します。
- Lightning Containerが準備できたら、LLCメッセージを送信して通知します
- ユーティリティバーウィジェット。ユーティリティバーウィジェットはエージェントのIDをコンポーネントに送信する必要があります
- ユーティリティバーウィジェットからメッセージを受信したら、UIKitをレンダリングします。
以下のコードを参照してください:
// tim_utilities_barController.js
({
handleMessage: function(component, message, helper) {
var payload = message.getParams().payload
// コンテナが準備できたら、initUIKitを呼び出します
if (payload === "READY") helper.initUIKit(component, message, helper)
}
});
({
initUIKit: function (component, message, helper) {
// エージェントのIDを取得します
var userId = $A.get("$SObjectType.CurrentUser.Id")
var message = { userId: userId }
try {
// IDをコンポーネントに送信します
component.find("TIM_Bar").message(message)
} catch (err) {
console.error("ユーティリティバーからのエラー:", err)
}
},
})
ユーティリティバーウィジェットにonMessageハンドラを追加します:
<!-- tim_utilities_bar.cmp -->
<lightning:container
aura:id='TIM_Bar'"
src="{!$Resource.tim_bar + '/index.html'}"
onmessage="{!c.handleMessage}"
/>
スクリプト内で、LLCパッケージ を使用して、Lightning Containerが読み込まれたときにアプリをレンダリングします。
// index.js
try {
const clientState = "READY";
LLC.sendMessage(clientState);
console.warn("Lightning Container --> SALESFORCE --> 送信:", clientState);
} catch (e) {
console.error("LLCが機能していません", e);
}
try {
LLC.addErrorHandler((error) => console.log("LLCエラー:", error));
LLC.addMessageHandler((salesforceMessage) => {
console.warn("SALESFORCE --> Lightning Container --> 到着:", salesforceMessage);
const app = createApp(App, {
user: salesforceMessage
});
app
.use(store)
.use(router)
.use(TUIKit)
.use(Aegis)
.use(ElementPlus)
.mount('#app');
});
} catch (e) {
console.error("LLCからのエラー!!", e);
}
Salesforceケースの割り当てを監視し、チャットグループメンバーを設定する
Salesforceでは、ケースは手動または自動でエージェントに割り当てられます。そのため、新しいエージェントをグループに招待し、以前のエージェントをグループから離脱させる必要があります。Salesforce Apex Calloutを使用して、ケースエージェント割り当ての変更を監視します。以下は、Salesforce Apex Calloutを呼び出す手順です。
1. 手動のケース割り当てを監視する
指定されたエージェントがケースに変更されると、Apex Case Change TriggerがApex Calloutを呼び出します。このコールアウトでは、a. 前のエージェントをチャットグループから削除し、b. 新しいエージェントをグループに招待します。そして、ケースが削除されると、チャットグループも解散します。
Salesforce Developer Consoleに移動 -> 新規 -> Apex Trigger -> 名前 = "AssignAgent" & sObject = "Case"
// AssignAgent.apxt
trigger AssignAgent on Case (after update, after delete) {
if(trigger.isUpdate){
// ケースが更新されました
System.debug('ケース更新が発火しました:');
for(Case a : trigger.new){
Case oldCase = trigger.oldMap.get(a.ID);
if(String.valueOf(a.OwnerId).substring(0, 3) == '005'){
// オーナーエージェントIDが変更されました。005プレフィックスはエージェントIDを意味します。
System.debug('エージェントが招待されました :' + a.OwnerId);
// チャットグループに新しいオーナーを割り当てます
String[] data = new String[2];
data[0] = a.Id; // ケースIDはグループIDです
data[1] = a.OwnerId;
TimCallouts.joinGroup(data); // カスタムコールアウトクラス
}
// 新しいケースエージェントが現在のエージェントと異なる場合
if(String.valueOf(oldCase.OwnerId).substring(0, 3) == '005' && oldCase.OwnerId != a.OwnerId && String.valueOf(a.OwnerId).substring(0, 3) == '005'){
// 古いエージェントをグループから削除します。
// 注意: ケースの最初の所有者はシステムオーナーになります。
System.debug('古いエージェントがグループから削除されます' + a.Id);
System.debug('leaveGroup: ' + oldCase.OwnerId);
String[] removeData = new String[2];
removeData[0] = a.Id; // ケースIDはグループIDです
removeData[1] = oldCase.OwnerId;
TIMCallouts.leaveGroup(removeData);
}
}
}
if(trigger.isDelete ){
// ケースが削除されました
System.debug('ケース削除が発火しました:');
for(Case a : trigger.old){
if(String.valueOf(a.OwnerId).substring(0, 3) == '005'){
System.debug('グループを削除します :' + a.Id);
String[] data = new String[1];
data[0] = a.Id;
TimCallouts.deleteGroup(data); // カスタムコールアウトクラス
}
}
}
}
2. Salesforce Omni Channelによる自動ケース割り当てを監視する
Omni Channelはケースの割り当てを検出し、Salesforceは自動的にAgentWorkオブジェクトを作成します。エージェントが割り当てを受け入れると、AgentWork TriggerがSalesforce Calloutを使用してグループに参加することができます。
Salesforce Developer Consoleに移動 -> 新規 -> Apex Trigger -> 名前 = "AgentOmniChannel" & sObject = "AgentWork"
// AgentOmniChanne.apxt
trigger AgentOmniChannel on AgentWork (after update, after insert) {
if(Trigger.isUpdate){
for(AgentWork a : Trigger.new){
AgentWork oldCase = Trigger.oldMap.get(a.ID);
if(a.Status == 'Opened' && String.valueOf(a.OwnerId).substring(0, 3) == '005' ){
String[] data = new String[2];
data[0] = a.WorkItemId;
data[1] = a.OwnerId;
TIMCallouts.joinGroup(data);
}
}
}
}
3. エージェントを招待/削除するためのSalesforce Calloutを設定します。
Salesforce Developer Consoleに移動 -> 新規 -> Apex Class -> 名前 = "TIMCallOuts"
// TIMCallOuts.apxc
public class TIMCallouts {
@future(callout=true)
public static void joinGroup(String[] data) {
String groupId = data[0];
String userId = data[1];
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://{your_web_server}/joingroup');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
request.setBody('{"userId":"'+ userId +'", "groupId":"'+ groupId +'"}');
HttpResponse response = http.send(request);
// JSONレスポンスを解析
if (response.getStatusCode() != 200) {
System.debug('グループ参加に失敗しました: '+response.getStatusCode()+' '+response.getStatus());
} else {
System.debug('Tencent Chatのレスポンス: ' + response.getBody());
}
}
@future(callout=true)
public static void leaveGroup(String[] data) {
String groupId = data[0];
String userId = data[1];
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://{your_web_server}/leavegroup');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
// ボディをJSONオブジェクトとして設定
request.setBody('{"userId":"'+ userId +'", "groupId":"'+ groupId +'"}');
HttpResponse response = http.send(request);
// JSONレスポンスを解析
if (response.getStatusCode() != 200) {
System.debug('グループ離脱に失敗しました: ' + response.getStatusCode() + ' ' + response.getStatus());
} else {
System.debug('Tencent Chatのレスポンス: ' + response.getBody());
}
}
@future(callout=true)
public static void deleteGroup(String data) {
String groupId = data;
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('http://{your_web_server}/deletegroup');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
// ボディをJSONオブジェクトとして設定
request.setBody('{"groupId":"'+ groupId + '}');
HttpResponse response = http.send(request);
// JSONレスポンスを解析
if (response.getStatusCode() != 200) {
System.debug('グループ削除に失敗しました: ' + response.getStatusCode() + ' ' + response.getStatus());
} else {
System.debug('Tencent Chatのレスポンス: ' + response.getBody());
}
}
}
これで、エンドユーザーが任意のアプリケーションからSalesforceエージェントとのチャットを開始できるようになります。さらに質問がある場合は、info_rtc@tencent.comまでメールを送信してください。私たちはこのソリューションや、チャットを介して現代のリアルタイム通信システムを構築するための他のソリューションについて、さらに詳しい情報を喜んで提供いたします。
今すぐ注文
クリック こちら をクリックして、購入ページにすぐに移動してください。