使用飞书消息卡片变量功能,批量数据快速录入消息卡片
在开发短链助手的时候,我需要实现一个查看当前用户创建的所有短链接的能力。这个依然希望通过消息卡片来完成。而作为一个 JSON,想要构建一套合适的内容,就变得十分的麻烦和复杂。
解构消息卡片
我要发送的消息卡片当中,可以区分为动态内容和静态内容,对于静态内容,我可能长期都不会变化,而静态内容,则会根据用户的数据发生变化。
如果整体都放在代码中生成,我就需要有一段又臭又长的代码来维护其中的变化的 JSON ,而我希望整个代码的简洁,不要有比较长的代码只是用来生成卡片的逻辑,所以就用上了消息卡片的新功能:循环对象数组。
而进一步看动态内容,则我们可以将其视为是变量 A 和变量 B 在不断的被重复赋予,最终形成了一行一行的结果。
而我们想要实现这样功能。首先,需要在卡片搭建工具中创建一个循环对象数组,并将其绑定在一个「多列布局」上。
绑定完成后,你的多列布局就有了被循环的可能性。
接下来你需要在多列布局中去构建你的每一行的结果,并在对应的位置绑定上变量,比如我这里就给多列布局防止了一个 Markdown 文本组件,并在这个文本组件中,填入了 ${source}
作为变量 A 进行填充。
当你根据你的需要,构建出需要的卡片结构后,点击右上角的保存并发布,就可以准备写代码来实现批量发送数据的逻辑了。
代码片段
这里的逻辑不复杂,首先需要从数据库中提取出需要用用作列表循环的数据,这里以 data.data 为例,data.data 是一个包含了 Object 的 Array,其中每一个 Object 都有 Postfix 和 Link 两个字段。这两个字段就是我们稍后要塞在卡片中的。
// data.data = [{Postfix:"a",Link:"https://amazon.cn"}]
let links = data.data.map(item => {
return {
source: `[${item.Postfix}](https://link.feishu.io/${item.Postfix})`,
target: item.Link
}
})
await client.request({
method: "POST",
url: "https://open.feishu.cn/open-apis/im/v1/messages",
data: {
receive_id: ctx.body.event.operator.operator_id.open_id,
msg_type: 'interactive',
content: JSON.stringify({
"type": "template",
"data": {
"template_id": "ctp_AAmFBm5vnHfs",
"template_variable": {
"CONTENT": links
}
}
}),
},
params: {
receive_id_type: 'open_id',
},
})
最终我们构建出来,发给飞书服务器的 JSON 其实是这样子的,这段 JSON 就会和我们在卡片搭建工具中构建的 JSON 租和,自动进行拼接,从而实现我们想要的循环效果。
{
"type": "template",
"data": {
"template_id": "ctp_AAmFBm5vnHfs",
"template_variable": {
"CONTENT": [
{
"source":"a",
"target":"https://amazon.cn"
},
{
"source":"b",
"target":"https://baidu.com"
}
]
}
}
}
文章中构建的出的卡片
构建出的卡片 JSON 是这样的,方便你参考:
{
"elements": [
{
"tag": "markdown",
"content": "你创建的链接如下:"
},
{
"tag": "column_set",
"flex_mode": "none",
"background_style": "grey",
"columns": [
{
"tag": "column",
"width": "weighted",
"weight": 1,
"vertical_align": "top",
"elements": [
{
"tag": "div",
"text": {
"content": "${source}",
"tag": "lark_md"
}
}
]
},
{
"tag": "column",
"width": "weighted",
"weight": 4,
"vertical_align": "top",
"elements": [
{
"tag": "div",
"text": {
"content": "${target}",
"tag": "lark_md"
}
}
]
}
],
"_varloop": "${CONTENT}"
}
],
"header": {
"template": "turquoise",
"title": {
"content": "链接清单",
"tag": "plain_text"
}
},
"card_link": {
"url": "",
"pc_url": "",
"android_url": "",
"ios_url": ""
}
}
完整代码参考
import axios from 'axios'
let appid = "";
let secret = ""
const lark = require('@larksuiteoapi/node-sdk');
const client = new lark.Client({
appId: appid,
appSecret: secret
});
export default async function (ctx: FunctionContext) {
console.log("event",ctx.body);
if (ctx.body.challenge) {
return ctx.body
}
if (Object.hasOwn(ctx.body, "action") && ctx.body.action) {
if (ctx.body.action.name != "submit") return { code: 1 };
try {
// function to create link
if (status == 200) {
return JSON.stringify({
"type": "template",
"data": {
"template_id": "ctp_AAmFBm5vnlt0",
"template_variable": {
"source": ctx.body.action.form_value.postfix,
"target": ctx.body.action.form_value.link
}
}
})
}
return {};
} catch (e) {
return JSON.stringify({
"type": "template",
"data": {
"template_id": "ctp_AAmFBm5vZYuo",
"template_variable": {
"POSTFIX": ctx.body.action.form_value.postfix
}
}
});
}
}
if (Object.hasOwn(ctx.body, "header") && ctx.body.header.event_type == 'application.bot.menu_v6') {
// 处理按钮
if (ctx.body.event.event_key == "help") {
try {
let content = JSON.stringify({
template_id: "ctp_AAmFBFOpYX0S"
});
await client.request({
method: "POST",
url: "https://open.feishu.cn/open-apis/im/v1/messages",
data: {
receive_id: ctx.body.event.operator.operator_id.open_id,
msg_type: 'interactive',
content: JSON.stringify({
"type": "template",
"data": {
"template_id": "ctp_AAmFBFOpYX0S",
}
}),
},
params: {
receive_id_type: 'open_id',
},
})
return {};
} catch (e) {
console.log(`key: ${ctx.body.event.event_key}, user:${ctx.body.event.operator.operator_id.open_id},error`, e);
return {};
}
}
if (ctx.body.event.event_key == "mylink") {
try {
// function to get all my link
let links = data.data.map(item => {
return {
source: `[${item.Postfix}](https://link.feishu.io/${item.Postfix})`,
target: item.Link
}
})
await client.request({
method: "POST",
url: "https://open.feishu.cn/open-apis/im/v1/messages",
data: {
receive_id: ctx.body.event.operator.operator_id.open_id,
msg_type: 'interactive',
content: JSON.stringify({
"type": "template",
"data": {
"template_id": "ctp_AAmFBm5vnHfs",
"template_variable": {
"CONTENT": links
}
}
}),
},
params: {
receive_id_type: 'open_id',
},
})
return {};
} catch (e) {
console.log(`key: ${ctx.body.event.event_key}, user:${ctx.body.event.operator.operator_id.open_id},error`, e);
return {};
}
}
if (ctx.body.event.event_key == "create") {
try {
await client.request({
method: "POST",
url: "https://open.feishu.cn/open-apis/im/v1/messages",
data: {
receive_id: ctx.body.event.operator.operator_id.open_id,
msg_type: 'interactive',
content: "{\"header\":{\"template\":\"turquoise\",\"title\":{\"content\":\"创建短链接\",\"tag\":\"plain_text\"}},\"elements\":[{\"tag\":\"form\",\"name\":\"form_1\",\"elements\":[{\"tag\":\"input\",\"name\":\"postfix\",\"placeholder\":{\"tag\":\"plain_text\",\"content\":\"请输入后缀\"},\"max_length\":10,\"label\":{\"tag\":\"plain_text\",\"content\":\"请输入后缀:\"},\"label_position\":\"left\",\"value\":{\"k\":\"v\"}},{\"tag\":\"input\",\"name\":\"link\",\"placeholder\":{\"tag\":\"plain_text\",\"content\":\"请输入要跳转链接\"},\"label\":{\"tag\":\"plain_text\",\"content\":\"请输入要跳转链接:\"},\"label_position\":\"left\",\"value\":{\"k\":\"v\"}},{\"action_type\":\"form_submit\",\"name\":\"submit\",\"tag\":\"button\",\"text\":{\"content\":\"提交\",\"tag\":\"lark_md\"},\"type\":\"primary\",\"confirm\":{\"title\":{\"tag\":\"plain_text\",\"content\":\"创建短链接\"},\"text\":{\"tag\":\"plain_text\",\"content\":\"确认提交吗\"}}}]}]}",
},
params: {
receive_id_type: 'open_id',
},
})
return {};
} catch (e) {
console.log(`key: ${ctx.body.event.event_key}, user:${ctx.body.event.operator.operator_id.open_id},error`, e);
return {};
}
}
}
return { data: 'hi, laf' }
}