如何在Xamarin.Forms项目中使用Bot Service(DirectLine版)

背景

这篇文档是基于之前那篇WebView版本的。当时在那篇文档中也说过,WebView版本的特点是,简单易用,把Bot的Url嵌入到WebView的Source对象中。但是这个方法的弊端就是,Xamarin的可控性太小,所有的都依赖于网页版的Bot的开发,并且WebView的性能终究不会比得上Xamarin通过自己发送请求。

那么这篇文档介绍的会是另外一种方式,也是比较推荐的,就是使用DirectLine API来直接通过Xamarin与Bot通信。

优点

这个方式的优点是,使用了Xamarin的MVVM的架构来实现,那么我现在的理解是,这个的可控性非常高,包括这个Bot的聊天样式,由Xamarin来控制,因为是Xamarin的Page,包括Bot返回的内容,可以是复杂的Card结构等等。

缺点

需要对Xamarin和Bot的开发都比较精通,要了解Bot的机制,Xamarin的MVVM等等。

技术介绍

首先是Key concepts in Direct Line API 3.0,这个是我们必须要熟悉的,因为这样才可以知道Direct Line究竟是什么和怎么使用。

但是这个文档中我们并不会使用这个里面介绍的调用API的方式,原因是如果看了这个文档,就会发现,Direct Line API还是基于Web API来调用,比如说,当开启一个新的conversation的时候,Direct Line API的指导使用是这样的:

如果这样使用的话,就意味着我们的每一次发送信息啊,等等,都是要使用类似HttpClient来发起,这样的话是非常麻烦的。所以其实Direct Line给到我们另一种选择,就是有NuGet的类库。

类库介绍

想要使用Direct Line的类库,我们一共有两个NuGet的类库需要添加:

第一个类库非常直接了当,就是封装了所有Direct Line API的调用方法,变成一个DLL,这样避免我们不停开启HttpClient请求。

第二个类库应该是一个微软的客户端使用REST API请求时候的Runtime的依赖类库,我们没有直接的代码调用是从这个类库出来的。

具体实现

Azure

首先是要在Azure Portal建立一个Direct Line的Channel,方式是Azure Portal => Bot Service => Channels => Enable Direct Line Channel.

老样子,我们的Key是从这个Channel点击Edit之后得到的。

准备好Azure的部分之后,我们就可以回到Xamarin中了。

Xamarin

首先我们要开发一个类,命名为BotConnection.cs,这个类中定义了包括连接到Bot,发送信息,接受信息等基本上是一切与Bot交流的实现。

先看代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class BotConnection
{
// Fields
public string botId "Your_Bot_Id_here";
public string directLineSecret = "Your_Bot_Secret_here";
public DirectLineClient directLineClient;
public Conversation MainConversation;
public ChannelAccount Account;

public BotConnection(string accountName)
{
// Obtain a token using Direct Line Secret
var tokenResponse = new DirectLineClient(directLineSecret).Tokens.GenerateTokenForNewConversation();

// Use token to create conversation
directLineClient = new DirectLineClient(tokenResponse.Token);
MainConversation = directLineClient.Conversations.StartConversation();
Account = new ChannelAccount { Id = accountName, Name = accountName };
}
}

先介绍这些Fields。

首先是botId,是用来确定是哪个bot在和我们的Xamarin应用交流的,这个在之后接受信息的方法中会用到,我们只监听当前与我们交流的这个Bot给我们发送回来的信息。

directLineSecret就是从Azure portal得到的那个Secret Key。

DirectLineClient是用来实例化一个Direct Line的,这个Client可以用来新建对话,发送信息,接受信息,等等,一切和Direct Line相关的操作都是由它来发起。

Conversation故名思意是一个Bot和Xamarin应用之间的对话实例,这边我们在字段中就声明一个MainConversation,我assume bot应该可以有不止一个Conversation,来达到更复杂的应用场景,不过现在还没有涉及到。

ChannelAccount是Xamarin这个应用的一个identity,来指明具体是哪个Account在和Bot进行通信。

这边我们还是用到了和之前WebView版本一样的使用token来创建conversation的方式,注意我们还是可以使用最简便的直接使用subscription key的方式来启用。

接下来我们需要开发一个SendMessage的方法,这个方法是end user给Bot发送信息的。我们所有发送的所谓Message,在Bot中其实都是一个ActivityActivity的Type属性需要赋值一个ActivityType类的其中某个枚举值,普通的文字信息我们就是用的ActivityType.Message格式。具体代码如下:

1
2
3
4
5
6
7
8
9
10
public void SendMessage(string message)
{
Activity activity = new Activity
{
From = Account,
Text = message,
Type = ActivityType.Message
};
directLineClient.Conversations.PostActivity(MainConversation.ConversationId, activity);
}

由于现在是使用纯的.NET类库来实现这个,所以我们要设计一个Model,来表达我们UI显示的时候每一条“信息”具体的一些内容和属性。现在由于实现的是最普通的文字版交流的Bot,所以Message的字段主要就是Text和Sender,但是我们还会增加一些类似于TextAlignment的属性来确定Bot的信息的位置和end user信息的位置是一边一个的。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Message
{
public string Text {get;set;}
public string Sender {get;set;}

public TextAlignment LblMessageHorizontalTextAlignment {get;set;}
public TextAlignment LblSenderHorizontalTextAlignment {get;set;}

public Message(string text, string sender)
{
Text = text;
Sender = sender;
if (Sender.ToUpper() == "ME")
{
LblMessageHorizontalTextAlignment = TextAlignment.End;
LblSenderHorizontalTextAlignment = TextAlignment.End;
}
else
{
LblMessageHorizontalTextAlignment = TextAlignment.Start;
LblSenderHorizontalTextAlignment = TextAlignment.Start;
}
}
}

注意上述代码中,hard code了sender为ME的情况,这是由于目前只有一个用户进行测试,之后会具体详细研究一下BotId和BotName以及UserId和UserName的用法。

最后不过也是最重要的就是GetMessageAsync方法。这个方法是用来监听用户给Bot发送的信息的。具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public async Task GetMessageAsync(ObservableCollection<Message> collection)
{
string watermark = null;
while (true)
{
Debug.WriteLine("Reading message every 3 seconds");

var activitySet = await directLineClient.Conversations.GetActivitiesAsync(MainConversation.ConversationId, watermark);
watermark = activitySet?.Watermark;

foreach (var activity in activitySet.Activities)
{
if (activity.From.Id == botId)
{
collection.Add(new Message(activity.Text, activity.From.Name));
}
}

await Task.Delay(3000);
}
}

说明:
首先是参数使用ObservableCollection是典型的XAML中的绑定机制,当这个collection中的数据有变化时,UI自动也会发生变化。
最后一句话是让线程等待3秒,在循环中,实现了每3秒监听一次的效果。

最重要的概念是这个叫Watermark的东西。这个是一个mark,记录了当前客户端见到的最后一条Bot给客户端发送的记录。这样我们每3秒更新一次的时候才只会更新最新的bot给我们需要发送的记录,并且重新设置这个watermark值。

最后一个就是开发UI并且绑定这个ObservableCollection<Message>数据,这个就非常简单了,只是一个ListView,所以这里就不具体写了。

具体我写的code的下载链接:https://allenxamlistfunctionapp.blob.core.windows.net/allensharing/XamBotDirectLine.zip

使用.NET SDK来运行PowerShell的命令 如何在Xamarin.Forms项目中使用Bot Service(WebView版)
You need to set install_url to use ShareThis. Please set it in _config.yml.

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×