在上一篇([04]瞭解Bot Builder SDK的組成)完整看了EchoBot的程式碼組成,并且瞭解了Bot Builder SDK一些常見的物件。并且依照所學調整了部分程式碼。
這一篇將會聚焦在其中一個管理上下文以及對來連綫的物件IDialogContext
。
IDialogContext的用途
還記得主要邏輯是在RootDialog.cs
裡面,而其中在裏面的程式碼用IDialogContext做了兩件事情:
- context.Wait() - 用來控制接下來的訊息要被什麽方法處理
- context.PostyAsync - 用來回傳訊息給使用者的方法
透過這兩個方法,可以看出IDialogContext有兩個作用:
- 控制流程 (底層
Internals.IDialogStack
) -Wait
代表接下來的訊息處理,還有一些別的,例如Call
用來呼叫別的Dialog - 和外界聯係 (底層
Internals.IBotToUser
) -PostAsync
把訊息回傳給使用者。
IDialogContext還有一個重要的功能,就是用來儲存和conversation或者user有關訊息的方式 (底層Internals.IBotData
)。
儲存和conversation或者user有關訊息的方式
記錄訊息有三個等級:
- ConversationData
- 和某個conversation有關的記錄。
- PrivateConversationData
- 和某個user在某個conversation有關的記錄
- UserData
- 和某個user有關的訊息 - 包含所有的channel以及conversation。
這3個等級要依照需求去儲存,例如,假設是一個和使用者有關的訊息但是不和任一個conversation有關,例如他的名字,那麽可以儲存在UserData
。反過來説,如果是某些訊息和某次conversation有關,例如
查旅館的時候記錄選擇了那個,那麽就可以使用PrivateConversationData
。
最後,要使用這些儲存很容易,把他們當成Dictionary使用就好。
Conversation是什麽
在上面提到儲存等級的時候,提到了兩個詞,Channel以及conversation。
channel之前提到過,其實就是使用者使用的溝通平臺。有可能你的bot部署到多個平臺,那麽用channel來區分可以做一些特定平臺相關處理
conversation則是某一次的交談。例如,一整串的對話就屬於一組conversation。等到10天後又過來就變成另外一組conversation。
這些資料其實有儲存在Activity
裡面,我們透過emulator可以看到實際的值:
實際修改
上面提到了這些理論,接下來我們就來實際調整chatbot。
這邊的需求是,如果沒有使用者的姓名,需要先和使用者取得,取到了之後,不管使用者輸入什麽都會直接回傳并且加上使用者的姓名。
要做到上面的需求,整個流程變成:
- 先判斷是否已經有使用者的姓名
- 當詢問使用者名稱的時候記錄下一個回答是記錄名字
- 把回傳是使用者名稱,儲存起來
先判斷是否已經有使用者的姓名
這邊使用了UserData
做儲存,因爲使用者姓名可能在多個conversation都可以使用(當然用什麽取決需求)
整個code調整如下:
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
context.UserData.TryGetValue<string>("Name", out string name);
if(string.IsNullOrEmpty(name))
{
// 還沒有姓名
}
else
{
// 已經有姓名直接輸出 姓名 + 輸入内容
await context.PostAsync($"{name}: {activity.Text}");
}
context.Wait(MessageReceivedAsync);
}
當詢問使用者名稱的時候記錄下一個回答是記錄名字
我們接下來要處理詢問使用者名稱的部分。整個code如下:
....
if(string.IsNullOrEmpty(name))
{
context.PrivateConversationData.
TryGetValue<bool>("IsAskName", out bool isAskName);
if(isAskName)
{
// 詢問過名字,準備記錄
}
else
{
context.PrivateConversationData.SetValue<bool>("IsAskName", true);
await context.PostAsync("您的名字是?");
}
}
....
用PrivateConversationData
來記錄是否詢問過名字,這邊選擇PrivateConversationData
是因爲這個資訊和使用者有關,并且只和這次的conversation有關。
需要這個中繼的IsAskName
是這樣才好區分已經在問名字還是正在詢問名字。
把回傳是使用者名稱,儲存起來
那最後一塊就是做名稱儲存就是這樣:
...
if(isAskName)
{
context.UserData.SetValue<string>("Name", activity.Text);
await context.PostAsync($"{activity.Text} 您好,能夠幫助您什麽");
}
else
{
context.PrivateConversationData.SetValue<bool>("IsAskName", true);
await context.PostAsync("您的名字是?");
}
...
最後結果
接下來我們可以把chatbot run起來并且用emulator做一些測試。
ActivityTypes.ConversationUpdate
來做這些處理。
結語
這一篇介紹了IDialogContext
這個物件的作用,并且這篇著重於如何儲存個人資料。希望透過這篇對於IDialogContext的作用及用途會更清楚。
在下一篇([06]不只能輸出文字 - 看看各種内建卡片模式以及可自定的Adaptive Card)我們切換一下,如果只能輸出一般文字對現在使用者來説還是太乾了,來看看BotbBuilder在傳輸不同資料格式上面有什麽幫助。