构建用于 Microsoft Teams 的 Java 消息扩展和连接器,第 3 部分:构建 Webhook 和通知连接器





5.00/5 (3投票s)
如何构建 Webhook 和通知连接器
到目前为止,在这个分为三部分的系列中,我们已经为 Microsoft Teams 构建了一个用于搜索的消息传递扩展,然后又构建了一个链接展开消息传递扩展。在第三篇也是最后一篇文章中,我们将创建 Webhook 和通知连接器。
探索 Webhook
在我们为 Microsoft Teams 开发的所有以前的消息传递扩展中,我们都依赖于 Microsoft Bot Framework。更具体地说,我们的 Web 服务使用 Bot 基础结构来建立安全通信。有时,尤其是在遗留系统中,您可能无法使用 Bot Framework SDK。在这种情况下,您可以使用 Webhook 和连接器。
Webhook 是自定义 HTTP 回调。在 Microsoft Teams 频道中,您可以使用它们通过提及(例如,@ToDoService
)向您的 Web 服务发送通知(传出 Webhook)。或者,您可以使用 Webhook 将通知推送到频道(传入 Webhook)。
连接器类似于传入 Webhook,但工作在发布-订阅模型中。连接器使用户能够订阅外部 Web 服务发布的通知。
实际上,我们为传出 Webhook 提供 REST API 资源的 URL。每当有人提及该服务时,Microsoft Teams 都会在此端点下发送 POST 请求。我们的应用程序将 POST
请求发送到传入 Webhook 和连接器的 HTTP 端点。
在此通信过程中,我们使用 JSON 格式的字符串。请求符合Activity 接口。当我们使用传出 Webhook 时,我们的服务会收到此信息。我们还发送符合 Activity 接口的 JSON 字符串以发送响应(用于传出 Webhook)或将消息发布到 Microsoft Team 频道(传入 Webhook 和连接器)。但是,我们还会附加自适应卡片。后者是 JSON 格式的字符串,表示用户界面 (UI) 组件,用于呈现 Microsoft Teams 对话。
在本教程中,我将解释如何使用 Webhook 和连接器。我们将使用 Java Spring Boot 实现 ToDoController
。我们将为名为 ToDoService
的传出 Webhook 配置控制器。因此,我们将能够通过提及 @ToDoService
向此服务发送请求。
此外,消息文本将传递 ToDo
项目的标识符。具体来说,在 Microsoft Teams 中发布此消息后
@ToDoService Id=2
ToDoController
将提取“2
”作为 ToDo
标识符。然后,ToDoController
将向 JSON Placeholder todos 资源发送请求:https://jsonplaceholder.typicode.com/todos/2。
该应用程序将以如下自适应卡片的形式将此 API 中的数据发送回 Microsoft Teams
您可以在 GitHub 上找到该项目的代码。
使用 ToDoController
我们通过创建 Java Spring Boot 应用程序来启动我们的项目。在这里,我使用 Spring 初始化器并选择了 Spring Boot。然后,我将 com.microsoft.bot
依赖项添加到我的 pom.xml 以访问 Activity 接口
<dependency>
<groupId>com.microsoft.bot</groupId>
<artifactId>bot-integration-spring</artifactId>
<version>4.13.0</version>
<scope>compile</scope>
</dependency>
请注意,此步骤不是必需的。您只需要提供相应的普通旧 Java 对象 (POJO) 类即可。
之后,我们用 ToDo.java 补充项目,表示来自 JSON placeholder REST API 的 todo
s 的 ToDo
对象。
然后,我们开始实现 ToDoController
@RestController
public class ToDoController {
private final RestTemplate restTemplate;
public ToDoController(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
}
我们使用 RestTemplate
自动将 JSON placeholder 中的 ToDo
JSON 字符串转换为 ToDo
Java 类的实例
private ToDo GetToDoById(Activity activity) {
// Get id from activity's text
String todoId = GetToDoIdFromActivityText(activity);
// Send request
String url = "https://jsonplaceholder.typicode.com/todos/" + todoId;
// Get todo
return this.restTemplate.getForObject(url, ToDo.class, todoId);
}
上面的方法首先从 Microsoft Teams 中提取 ToDo
项目的 ID
。然后,它使用 ID
构造请求并将其发送到 JSON placeholder。最后,代码将结果包装成一个 ToDo
Java 类实例。
使用以下辅助方法从 Activity 中提取 ToDo ID
private String GetToDoIdFromActivityText(Activity activity) {
// Split text at 'Id='
var splittedText = activity.getText().split("Id=");
// Return the id value (assuming the input string has the correct format)
return splittedText[splittedText.length-1];
}
上面的代码中,Activity
类具有 getText
方法,使我们能够检索用户在 Microsoft Teams 中发布的消息。例如,它会将 @ToDoService Id=1
表示为 <at>ToDoService</at> Id=1
。
为了提取 ID,我们使用“Id=
”模式拆分输入文本。假设所有消息都具有相同的格式,我们获取拆分字符串集合的最后一个元素并将其作为 ToDo
ID 返回。
一旦我们有了 ToDo
Java 对象,我们就需要将其发送回 Microsoft Teams
@PostMapping("/todos/")
public Activity sendToDo(@RequestBody Activity activity) throws URISyntaxException,
IOException {
// Get ToDo
ToDo todo = GetToDoById(activity);
// Create adaptive card
Attachment cardAttachment = createAdaptiveCardAttachment("card.json", todo);
// Send response
Activity responseActivity = Activity.createMessageActivity();
responseActivity.setAttachment(cardAttachment);
return responseActivity;
}
如上面的代码所示,我们需要将 ToDo
包装到自适应卡片中。然后,我们将此卡片附加到 Activity
类实例并发送生成的响应。我们将在下一节中创建自适应卡片。
编辑自适应卡片
自适应卡片是符合此架构的 JSON 字符串。对于 C#,您可以使用专用类来加速自适应卡片的创建。在 Java 中,您可以使用 App Studio 中的**卡片编辑器**。它提供了带有示例自适应卡片代码和预览的 JSON 编辑器,您可以在其中实时查看更改。
使用**卡片编辑器**,创建以下自适应卡片
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Webhooks and Connectors",
"weight": "bolder",
"size": "medium"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "ToDo Description",
"weight": "bolder",
"wrap": true
}
]
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "FactSet",
"facts": [
{
"title": "UserID:",
"value": "<USER_ID>"
},
{
"title": "Id:",
"value": "<ID>"
},
{
"title": "Title:",
"value": "<TITLE>"
},
{
"title": "Completed:",
"value": "<COMPLETED>"
}
]
}
]
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Set due date",
"card": {
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Input.Date",
"id": "dueDate"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "OK"
}
]
}
},
{
"type": "Action.ShowCard",
"title": "Comment",
"card": {
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Input.Text",
"id": "comment",
"isMultiline": true,
"placeholder": "Enter your comment"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "OK"
}
]
}
}
]
}
该卡片将显示从 JSON Placeholder 检索到的 ToDo
项目的 UserID
、ID
、Title
和 Completed
属性。为了动态配置这些值,我们在 JSON 中使用 <USER_ID>
、<ID>
、<TITLE>
和 <COMPLETED>
占位符。然后,我们将上述 JSON 保存到 Java Spring Boot 项目的 resources/card.json 文件中。
为了在运行时读取 JSON,我们在 ToDoController
中实现 createAdaptiveCardAttachment
private Attachment createAdaptiveCardAttachment(String filePath, ToDo todo)
throws URISyntaxException, IOException {
try {
// Read JSON
InputStream inputStream =
this.getClass().getClassLoader().getResourceAsStream(filePath);
String adaptiveCardJson = IOUtils.toString(inputStream,
StandardCharsets.UTF_8);
// Replace placeholders with the actual values
adaptiveCardJson = StringUtils.replace(adaptiveCardJson,
"<USER_ID>", String.valueOf(todo.getUserId()));
adaptiveCardJson = StringUtils.replace(adaptiveCardJson,
"<ID>", String.valueOf(todo.getId()));
adaptiveCardJson = StringUtils.replace(adaptiveCardJson,
"<TITLE>", todo.getTitle());
adaptiveCardJson = StringUtils.replace(adaptiveCardJson,
"<COMPLETED>", String.valueOf(todo.getCompleted()));
// Create attachment
Attachment attachment = new Attachment();
attachment.setContentType("application/vnd.microsoft.card.adaptive");
attachment.setContent(Serialization.jsonToTree(adaptiveCardJson));
return attachment;
}
catch(Exception e) {
e.printStackTrace();
return new Attachment();
}
}
上述方法读取 card.json,然后将占位符 <USER_ID>
、<ID>
、<TITLE>
和 <COMPLETED>
替换为 ToDo
对象的实际值。最后,JSON 创建一个 Attachment
类的实例。我们在前面描述的 sendToDo
方法中使用此类的实例。
配置设置
现在让我们测试上述解决方案。首先,启动 ngrok
以在 8080 端口转发流量
ngrok http -host-header=rewrite 8080
读取 ngrok
生成的转发 URL。您稍后将需要它来配置 Webhook。
然后,构建(mvn clean install
)并启动 Web 服务(java -jar target/todos-0.0.1-SNAPSHOT.jar
)。接下来,打开 Microsoft Teams。单击**团队**选项卡并**创建团队**。您可以从头开始创建团队,也可以使用模板。在这里,我使用**管理项目**模板
将团队设置为公开,并将其名称设置为**我的团队**
最后,单击**创建**。当团队准备就绪后,单击三点,然后从上下文菜单中选择**管理团队**。
在**管理团队**中,转到**应用**选项卡。然后,单击**创建传出 Webhook**(在右下角)
在**创建传出 Webhook**页面中,填写以下字段
- **名称**:
ToDoService
。您稍后将使用此名称提及您的 Web 服务。 - **回调 URL**:来自
ngrok
的转发 URL,并补充 /todos(例如,https://14a9-87-206-227-230.ngrok.io/todos) - **描述**:输入任何内容
单击**创建**后,Teams 将提供安全令牌
现在,您可以转到**常规**频道并开始对话。然后,键入 @ToDoService ToDo=1
。此命令向您的 Web 服务发送请求。然后,Web 服务确定 ToDo
标识符并相应地配置自适应卡片 JSON。
您应该会看到类似以下图像的结果
使用传入 Webhook 和连接器
要使用传入 Webhook 和连接器,请以类似的方式进行。按照 Microsoft 的描述注册传入 Webhook。同样,使用 Teams 清单创建连接器
注册传入 Webhook 或连接器后,您将获得一个 URL,您可以在其中以消息或自适应卡片的形式发布数据,如 Microsoft 所述。
后续步骤
在本教程中,我们学习了如何使用 Webhook 和连接器。在此过程中,我们还学习了如何使用自适应卡片。您可以使用这些卡片为 Microsoft Teams 应用程序构建丰富且交互式的 UI 组件。您还可以克服使用 Bot Frameworks 进行 Webhook 通信的需求。
既然您已经知道如何构建用于搜索的消息传递扩展、链接展开消息传递扩展以及 Webhook 和通知连接器,您就可以很好地为 Microsoft Teams 创建 Java 应用程序了。通过人人都可以访问和使用的便捷应用程序,帮助提高您或您的同事的生产力,这些应用程序可以在他们日常协作的地方使用。立即开始为您自己或您的组织构建 Microsoft Teams 应用程序。
要了解如何使用 Azure 服务构建、迁移和扩展 Azure 上的 Java 应用程序,请查看Azure Java 入门。