Nota
Capaian ke halaman ini memerlukan kebenaran. Anda boleh cuba mendaftar masuk atau menukar direktori.
Capaian ke halaman ini memerlukan kebenaran. Anda boleh cuba menukar direktori.
Dengan Omnichannel untuk Perkhidmatan Pelanggan, anda boleh melaksanakan penyambung untuk menyepadukan saluran pemesejan tersuai dengan menggunakan Direct Line API 3.0, yang merupakan sebahagian daripada .NET SDK. Kod sampel lengkap menggambarkan cara anda boleh mencipta penyambung anda sendiri. Untuk mengetahui lebih lanjut tentang API Direct Line 3.0, lihat Konsep utama dalam API Direct Line 3.0.
Artikel ini menerangkan cara saluran disambungkan ke Rangka Kerja Bot Talian Langsung Microsoft, yang dilampirkan secara dalaman pada Multisaluran untuk Perkhidmatan Pelanggan. Bahagian berikut termasuk coretan kod yang menggunakan API Direct Line 3.0 untuk mencipta klien Direct Line dan IChannelAdapter
antara muka untuk membina penyambung sampel.
Nota
Kod sumber dan dokumentasi menerangkan aliran keseluruhan cara saluran boleh menyambung ke Omnichannel untuk Perkhidmatan Pelanggan melalui Direct Line dan tidak menumpukan pada aspek kebolehpercayaan dan kebolehskalaan.
Komponen
Perkhidmatan API Webhook Penyesuai
Apabila pengguna memasukkan mesej, API penyesuai digunakan daripada saluran. Ia memproses permintaan masuk dan menghantar status kejayaan atau kegagalan sebagai tindak balas. Perkhidmatan API penyesuai mesti melaksanakan IChannelAdapter
antara muka dan menghantar permintaan masuk kepada penyesuai saluran masing-masing untuk memproses permintaan.
/// <summary>
/// Accept an incoming web-hook request from MessageBird Channel
/// </summary>
/// <param name="requestPayload">Inbound request Object</param>
/// <returns>Executes the result operation of the action method asynchronously.</returns>
[HttpPost("postactivityasync")]
public async Task<IActionResult> PostActivityAsync(JToken requestPayload)
{
if (requestPayload == null)
{
return BadRequest("Request payload is invalid.");
}
try
{
await _messageBirdAdapter.ProcessInboundActivitiesAsync(requestPayload, Request).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError($"postactivityasync: {ex}");
return StatusCode(500, "An error occured while handling your request.");
}
return StatusCode(200);
}
Penyesuai saluran
Penyesuai saluran memproses aktiviti masuk dan keluar, dan mesti melaksanakan IAdapterBuilder
antara muka.
Proses aktiviti masuk
Penyesuai saluran melakukan aktiviti masuk berikut:
- Sahkan tandatangan permintaan mesej masuk.
Permintaan masuk daripada saluran disahkan berdasarkan kunci tandatangan. Jika permintaan tidak sah, mesej pengecualian "tandatangan tidak sah" akan dilemparkan. Sekiranya permintaan itu sah, ia diteruskan seperti berikut:
/// <summary>
/// Validate Message Bird Request
/// </summary>
/// <param name="content">Request Content</param>
/// <param name="request">HTTP Request</param>
/// <param name="messageBirdSigningKey">Message Bird Signing Key</param>
/// <returns>True if there request is valid, false if there aren't.</returns>
public static bool ValidateMessageBirdRequest(string content, HttpRequest request, string messageBirdSigningKey)
{
if (string.IsNullOrWhiteSpace(messageBirdSigningKey))
{
throw new ArgumentNullException(nameof(messageBirdSigningKey));
}
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (string.IsNullOrWhiteSpace(content))
{
throw new ArgumentNullException(nameof(content));
}
var messageBirdRequest = new MessageBirdRequest(
request.Headers?["Messagebird-Request-Timestamp"],
request.QueryString.Value?.Equals("?",
StringComparison.CurrentCulture) != null
? string.Empty
: request.QueryString.Value,
GetBytes(content));
var messageBirdRequestSigner = new MessageBirdRequestSigner(GetBytes(messageBirdSigningKey));
string expectedSignature = request.Headers?["Messagebird-Signature"];
return messageBirdRequestSigner.IsMatch(expectedSignature, messageBirdRequest);
}
- Tukar permintaan masuk kepada aktiviti bot.
Muatan permintaan masuk ditukar kepada aktiviti yang boleh difahami oleh Rangka Kerja Bot.
Nota
Muatan aktiviti tidak boleh melebihi had saiz mesej 28 KB.
Objek Aktiviti ini termasuk atribut berikut:
Atribut | Perihalan |
---|---|
Dari | Menyimpan maklumat akaun saluran yang terdiri daripada pengecam unik pengguna dan nama (gabungan nama pertama dan nama keluarga, dipisahkan oleh pembatas ruang). |
channelId | Menunjukkan pengecam saluran. Untuk permintaan masuk, ID saluran ialah directline . |
serviceUrl | Menunjukkan URL perkhidmatan. Untuk permintaan masuk, URL perkhidmatan ialah https://directline.botframework.com/ . |
Jenis | Menunjukkan jenis aktiviti. Untuk aktiviti mesej, jenisnya ialah message . |
Teks | Menyimpan kandungan mesej. |
Id | Menunjukkan pengecam yang digunakan oleh penyesuai untuk membalas mesej keluar. |
channelData | Menunjukkan data saluran yang terdiri daripada channelType , conversationcontext , dan customercontext . |
Jenis saluran | Menunjukkan nama saluran di mana pelanggan menghantar mesej. Sebagai contoh, MessageBird, KakaoTalk, Snapchat |
perbualankonteks | Merujuk kepada objek kamus yang memegang pembolehubah konteks yang ditakrifkan dalam aliran kerja. Omnichannel untuk Perkhidmatan Pelanggan menggunakan maklumat ini untuk menghalakan perbualan kepada wakil perkhidmatan pelanggan yang betul (wakil atau wakil perkhidmatan). Contohnya: "conversationcontext":{ "ProductName": "Xbox", "Issue":"Installation" } Dalam contoh ini, konteks menghalakan perbualan kepada wakil perkhidmatan yang berurusan dengan pemasangan Xbox. |
Konteks Pelanggan | Merujuk kepada objek kamus yang menyimpan butiran pelanggan seperti nombor telefon dan alamat e-mel. Omnichannel untuk Perkhidmatan Pelanggan menggunakan maklumat ini untuk mengenal pasti rekod hubungan pengguna. "customercontext":{ "e-mel":email@email.com, "nombor telefon":"1234567890" } |
/// <summary>
/// Build Bot Activity type from the inbound MessageBird request payload<see cref="Activity"/>
/// </summary>
/// <param name = "messagePayload"> Message Bird Activity Payload</param>
/// <returns>Direct Line Activity</returns>
public static Activity PayloadToActivity(MessageBirdRequestModel messagePayload)
{
if (messagePayload == null)
{
throw new ArgumentNullException(nameof(messagePayload));
}
if (messagePayload.Message?.Direction == ConversationMessageDirection.Sent ||
messagePayload.Type == ConversationWebhookMessageType.MessageUpdated)
{
return null;
}
var channelData = new ActivityExtension
{
ChannelType = ChannelType.MessageBird,
// Add Conversation Context in below dictionary object. Please refer the document for more information.
ConversationContext = new Dictionary<string, string>(),
// Add Customer Context in below dictionary object. Please refer the document for more information.
CustomerContext = new Dictionary<string, string>()
};
var activity = new Activity
{
From = new ChannelAccount(messagePayload.Message?.From, messagePayload.Contact?.DisplayName),
Text = messagePayload.Message?.Content?.Text,
Type = ActivityTypes.Message,
Id = messagePayload.Message?.ChannelId,
ServiceUrl = Constant.DirectLineBotServiceUrl,
ChannelData = channelData
};
return activity;
}
Sampel muatan JSON adalah seperti berikut:
{
"type": "message",
"id": "bf3cc9a2f5de...",
"serviceUrl": https://directline.botframework.com/,
"channelId": "directline",
"from": {
"id": "1234abcd",// userid which uniquely identify the user
"name": "customer name" // customer name as First Name <space> Last Name
},
"text": "Hi,how are you today.",
"channeldata":{
"channeltype":"messageBird",
"conversationcontext ":{ // this holds context variables defined in Workstream
"ProductName" : "XBox",
"Issue":"Installation"
},
"customercontext":{
"email":email@email.com,
"phonenumber":"1234567890"
}
}
}
- Hantar aktiviti ke pemproses geganti mesej.
Selepas membina muatan aktiviti, ia memanggil kaedah PostActivityAsync pemproses geganti mesej untuk menghantar aktiviti ke Direct Line. Penyesuai saluran juga harus melepasi pengendali peristiwa, yang dipanggil oleh pemproses geganti apabila ia menerima mesej keluar daripada Omnichannel untuk Perkhidmatan Pelanggan melalui Direct Line.
Proses aktiviti keluar
Pemproses geganti memanggil pengendali acara untuk menghantar aktiviti keluar ke penyesuai saluran masing-masing dan penyesuai kemudian memproses aktiviti keluar. Penyesuai saluran melakukan aktiviti keluar berikut:
- Tukar aktiviti keluar kepada model tindak balas saluran.
Aktiviti Direct Line ditukar kepada model tindak balas khusus saluran.
/// <summary>
/// Creates MessageBird response object from a Bot Framework <see cref="Activity"/>.
/// </summary>
/// <param name="activities">The outbound activities.</param>
/// <param name="replyToId">Reply Id of Message Bird user.</param>
/// <returns>List of MessageBird Responses.</returns>
public static List<MessageBirdResponseModel> ActivityToMessageBird(IList<Activity> activities, string replyToId)
{
if (string.IsNullOrWhiteSpace(replyToId))
{
throw new ArgumentNullException(nameof(replyToId));
}
if (activities == null)
{
throw new ArgumentNullException(nameof(activities));
}
return activities.Select(activity => new MessageBirdResponseModel
{
To = replyToId,
From = activity.ChannelId,
Type = "text",
Content = new Content
{
Text = activity.Text
}
}).ToList();
}
- Hantar respons melalui saluran REST API.
Penyesuai saluran memanggil API REST untuk menghantar respons keluar ke saluran, yang kemudiannya dihantar kepada pengguna.
/// <summary>
/// Send Outbound Messages to Message Bird
/// </summary>
/// <param name="messageBirdResponses">Message Bird Response object</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task SendMessagesToMessageBird(IList<MessageBirdResponseModel> messageBirdResponses)
{
if (messageBirdResponses == null)
{
throw new ArgumentNullException(nameof(messageBirdResponses));
}
foreach (var messageBirdResponse in messageBirdResponses)
{
using (var request = new HttpRequestMessage(HttpMethod.Post, $"{MessageBirdDefaultApi}/send"))
{
var content = JsonConvert.SerializeObject(messageBirdResponse);
request.Content = new StringContent(content, Encoding.UTF8, "application/json");
await _httpClient.SendAsync(request).ConfigureAwait(false);
}
}
}
Pemproses geganti mesej
Pemproses geganti mesej menerima aktiviti masuk daripada penyesuai saluran dan melakukan pengesahan model aktiviti. Pemproses geganti menyemak sama ada perbualan aktif untuk aktiviti tertentu, sebelum menghantar aktiviti ini ke Direct Line
Untuk mencari sama ada perbualan aktif, pemproses geganti mengekalkan koleksi perbualan aktif dalam kamus. Kamus ini mengandungi kunci sebagai ID Pengguna, yang secara unik mengenal pasti pengguna dan Nilai sebagai objek kelas berikut:
/// <summary>
/// Direct Line Conversation to store as an Active Conversation
/// </summary>
public class DirectLineConversation
{
/// <summary>
/// .NET SDK Client to connect to Direct Line Bot
/// </summary>
public DirectLineClient DirectLineClient { get; set; }
/// <summary>
/// Direct Line response after start a new conversation
/// </summary>
public Conversation Conversation { get; set; }
/// <summary>
/// Watermark to guarantee that no messages are lost
/// </summary>
public string WaterMark { get; set; }
}
Jika perbualan tidak aktif untuk aktiviti yang diterima oleh pemproses geganti, ia melakukan langkah berikut:
- Memulakan perbualan dengan Direct Line dan menyimpan objek perbualan yang dihantar oleh Direct Line terhadap ID pengguna dalam kamus.
/// <summary>
/// Initiate Conversation with Direct Line Bot
/// </summary>
/// <param name="inboundActivity">Inbound message from Aggregator/Channel</param>
/// <param name="adapterCallBackHandler">Call Back to send activities to Messaging API</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
private async Task InitiateConversation(Activity inboundActivity, EventHandler<IList<Activity>> adapterCallBackHandler)
{
var directLineConversation = new DirectLineConversation
{
DirectLineClient = new DirectLineClient(_relayProcessorConfiguration.Value.DirectLineSecret)
};
// Start a conversation with Direct Line Bot
directLineConversation.Conversation = await directLineConversation.DirectLineClient.Conversations.
StartConversationAsync().ConfigureAwait(false);
await directLineConversation.DirectLineClient.Conversations.
StartConversationAsync().ConfigureAwait(false);
if (directLineConversation.Conversation == null)
{
throw new Exception(
"An error occurred while starting the Conversation with direct line. Please validate the direct line secret in the configuration file.");
}
// Adding the Direct Line Conversation object to the lookup dictionary and starting a thread to poll the activities from the direct line bot.
if (ActiveConversationCache.ActiveConversations.TryAdd(inboundActivity.From.Id, directLineConversation))
{
// Starts a new thread to poll the activities from Direct Line Bot
new Thread(async () => await PollActivitiesFromBotAsync(
directLineConversation.Conversation.ConversationId, inboundActivity, adapterCallBackHandler).ConfigureAwait(false))
.Start();
}
}
- Memulakan jaluran baharu untuk mengundi aktiviti keluar daripada bot Direct Line berdasarkan selang pengundian yang dikonfigurasikan dalam fail konfigurasi. Utas pengundian aktif sehingga akhir aktiviti perbualan diterima daripada Direct Line.
/// <summary>
/// Polling the activities from BOT for the active conversation
/// </summary>
/// <param name="conversationId">Direct Line Conversation Id</param>
/// <param name="inboundActivity">Inbound Activity from Channel/Aggregator</param>
/// <param name="lineActivitiesReceived">Call Back to send activities to Messaging API</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
private async Task PollActivitiesFromBotAsync(string conversationId, Activity inboundActivity, EventHandler<IList<Activity>> lineActivitiesReceived)
{
if (!int.TryParse(_relayProcessorConfiguration.Value.PollingIntervalInMilliseconds, out var pollingInterval))
{
throw new FormatException($"Invalid Configuration value of PollingIntervalInMilliseconds: {_relayProcessorConfiguration.Value.PollingIntervalInMilliseconds}");
}
if (!ActiveConversationCache.ActiveConversations.TryGetValue(inboundActivity.From.Id,
out var conversationContext))
{
throw new KeyNotFoundException($"No active conversation found for {inboundActivity.From.Id}");
}
while (true)
{
var watermark = conversationContext.WaterMark;
// Retrieve the activity set from the bot.
var activitySet = await conversationContext.DirectLineClient.Conversations.
GetActivitiesAsync(conversationId, watermark).ConfigureAwait(false);
// Set the watermark to the message received
watermark = activitySet?.Watermark;
// Extract the activities sent from our bot.
if (activitySet != null)
{
var activities = (from activity in activitySet.Activities
where activity.From.Id == _relayProcessorConfiguration.Value.BotHandle
select activity).ToList();
if (activities.Count > 0)
{
SendReplyActivity(activities, inboundActivity, lineActivitiesReceived);
}
// Update Watermark
ActiveConversationCache.ActiveConversations[inboundActivity.From.Id].WaterMark = watermark;
if (activities.Exists(a => a.Type.Equals("endOfConversation", StringComparison.InvariantCulture)))
{
if (ActiveConversationCache.ActiveConversations.TryRemove(inboundActivity.From.Id, out _))
{
Thread.CurrentThread.Abort();
}
}
}
await Task.Delay(TimeSpan.FromMilliseconds(pollingInterval)).ConfigureAwait(false);
}
}
Nota
Di tengah-tengah kod yang menerima mesej ialah kaedah GetActivitiesAsync yang mengambil ConversationId
dan watermark
sebagai parameter. Tujuan watermark
parameter adalah untuk mendapatkan semula mesej yang tidak dihantar oleh Direct Line. Jika parameter tera air ditentukan, perbualan dimainkan semula daripada tera air, supaya tiada mesej hilang.
Hantar aktiviti ke Direct Line
/// <summary>
/// Send the activity to the bot using Direct Line client
/// </summary>
/// <param name="inboundActivity">Inbound message from Aggregator/Channel</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
private static async Task SendActivityToBotAsync(Activity inboundActivity)
{
if (!ActiveConversationCache.ActiveConversations.TryGetValue(inboundActivity.From.Id,
out var conversationContext))
{
throw new KeyNotFoundException($"No active conversation found for {inboundActivity.From.Id}");
}
await conversationContext.DirectLineClient.Conversations.PostActivityAsync(
conversationContext.Conversation.ConversationId, inboundActivity).ConfigureAwait(false);
}
Jika perbualan aktif untuk aktiviti yang diterima oleh pemproses geganti, ia menghantar aktiviti kepada pemproses geganti mesej.
Tamatkan perbualan
Untuk menamatkan perbualan, lihat Tamatkan perbualan dalam Talian Terus.
Langkah seterusnya
Sokongan untuk sembang langsung dan saluran tak segerak
Format penurunan harga dalam saluran tersuai yang menggunakan Direct Line
Maklumat berkaitan
Konfigurasikan saluran pemesejan tersuai
Rujukan API MessageBird
Amalan terbaik untuk mengkonfigurasi bot