(Slack API Bolt 项目)使用 Context 对象将 属性 从一种方法传递到另一种方法
(Slack API Bolt project) Using Context object to pass property from one method to another
我正在使用 JavaScript Bolt 框架构建一个 Slack 应用程序。该应用程序的概念只是收听频道中特定的消息关键字,然后将这些消息转发给应用程序的用户。
我想要实现的是在转发的邮件中包含一个永久链接。我正在尝试使用 chat.getPermalink
方法获取 url,然后将其包含在我的 chat.postMessage
方法中。我正在尝试利用 Bolt 的 'Context' 将 chat.getPermalink 中的 属性 传递给 chat.postMessage。我在这里寻求帮助,因为我无法让 Context 工作..
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
let token = process.env.SLACK_BOT_TOKEN,
web = new WebClient(token);
let jira_text = "jira";
let rdu_qa = '@rdu_qa';
//Get permalink
async function PermaLinks({payload, context, next}) {
let perm = app.client.chat.getPermalink({
token: context.botToken,
channel: "C0109KMQCFQ",
message_ts: payload.ts
});
context.permalink = perm.permalink;
await next();
}
app.event('message', PermaLinks, async ({ payload, message, context}) => {
let userzArray = ["D010Q34TQL9", "UVBBD8989"];
//if channel is general and incldues the text 'Jira' or 'rdu_qa'
if (payload.channel === "C0109KMQCFQ") {
if (payload.text.includes(jira_text) || payload.text.includes(rdu_qa)) {
try {
// Call the chat.postMessage to each of the users
let oneUser = await userzArray.forEach(userId => { app.client.chat.postMessage({
token: context.botToken,
bot_id: "USLACKBOT",
channel: userId,
blocks: [
{
type: "section",
text: {
text: payload.text,
type: "mrkdwn"
},
fields: [
{
type: "mrkdwn",
text: `posted by <@${message.user}>`
},
{
type:"mrkdwn",
text: "in General channel" //channel.name//getChannelNameGeneral
},
{
type:"mrkdwn",
text: context.permalink // Permalink should be right here
}
]
},
{
"type": "divider"
},
] // End of block of Jira notification stuff
});
});
// console.log(result);
} catch (error) {
console.error(error);
}
} // If text sent to General channel includes keyword 'Jira' or 'rdu_qa'
} //end of if message was posted in General channel
我可以在示例代码中看到一些问题,但我认为关于 context
的主要问题是您将 Promise 存储为 context.permalink
,而不是实际结果的方法调用。为了存储结果,你应该在调用方法之前使用await
关键字(app.client.chat.getPermalink(...)
)。
我已经修改了你在这里分享的代码,我将在下面解释修改。
const { App } = require('@slack/bolt');
const token = process.env.SLACK_BOT_TOKEN
const app = new App({
signingSecret: process.env.SLACK_SIGNING_SECRET,
token,
});
// Users who should be notified when certain messages are heard
let userzArray = ["D010Q34TQL9", "UVBBD8989"];
// Conversation IDs corresponding to the users in the array above. This variable will be set automatically when the app starts.
let conversationsToNotify;
// Match messages that include the text 'jira' or '@rdu_qa'
app.message(/jira|@rdu_qa/, async ({ message, client }) => {
// Match the messages that are in the specified channel
if (message.channel === 'C0109KMQCFQ') {
try {
// Get a permalink to this message
const permalinkResult = await client.chat.getPermalink({
channel: message.channel,
message_ts: message.ts,
});
// Send a message to each user containing the permalink for this message
await Promise.all(conversationsToNotify.map((conversationId) => {
return client.chat.postMessage({
channel: conversationId,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `>>> ${payload.text}`,
},
fields: [
{
type: 'mrkdwn',
text: `posted by <@${message.user}>`,
},
{
type: 'mrkdwn',
text: `in <#${message.channel}>`,
},
{
type:'mrkdwn',
text: `<Original|${permalinkResult.permalink}>`,
},
],
},
{
type: 'divider'
},
],
});
}));
} catch (error) {
console.error(error);
}
}
});
async function convertUsersToConversations(input) {
return Promise.all(input.map((id) => {
// For all IDs that seem like user IDs, convert them to a DM conversation ID
if (id.startsWith('U')) {
return app.client.conversations.open({
token,
users: id,
})
.then((result) => result.channel.id);
}
// For all IDs that don't seem to belong to a user, return them as is
return id;
}));
});
(async () => {
// Start the app
conversationsToNotify = await convertUsersToConversations(userzArray);
await app.start(process.env.PORT || 3000);
console.log('⚡️ Bolt app is running!');
})();
- 我删除了新
WebClient
对象的初始化。在 Bolt v1.6.0 及更高版本中,侦听器和中间件中有一个 client
参数可用,您可以使用它来调用 Web API 方法。使用 client
参数的优点是您不需要从上下文中读取令牌并将其作为参数传递给您自己的每个方法调用,Bolt 会为您找到正确的令牌。
- 我没有使用
app.event('message', ...)
方法来监听消息事件,而是改为使用 app.message(...)
。后者的工作原理基本相同,但还有一个优势:您可以传递一个模式来匹配消息文本作为第一个参数(在侦听器函数之前):app.message(pattern, ...)
。这有助于消除侦听器内部的一些条件。我没有仅使用两个字符串变量 jira_text
和 @rdu_qa
,而是将它们组合在一个正则表达式中,当在文本中看到这些值中的任何一个时匹配:/jira|@rdu_qa/
.
- 我没有使用中间件来查找消息的固定链接,而是将该代码移到了侦听器中。中间件应该用于跨多个侦听器重用代码(或全局中间件以跨所有侦听器重用代码)。在您的示例中,查找永久链接的代码似乎没有被重用,但如果您在许多侦听器中使用它,它应该相对容易提取。另一个优点是现在逻辑仅在 模式匹配后运行 ,因此您无需为机器人在它所属的所有频道中看到的每条消息进行这些调用(这对性能来说要好得多)。
- 使用
Promise.all()
将每次调用chat.postMessage
的Promise收集到一个promise中。目前,您正在使用 userzArray.forEach(...)
,而 doesn't return anything. So then using await
on that value will immediately resolve, and doesn't really do anything useful. What we need to do is collect each of the Promises and wait for them to all complete. This is what Promise.all()
does. We just need an array of Promises to pass in, which we can get by simply changing userzArray.forEach()
to userzArray.map()
。
- 您的呼叫方式有问题
chat.postMessage
。您正在尝试使用 Slackbot 发送这些消息,但不建议这样做,因为用户不太可能了解该消息的来源。相反,您应该将此消息作为 DM 从您的 bot 用户发送。为此,对于要向其发送此通知的每个用户,您需要一个对话 ID,而不是用户 ID。 userzArray
中的一项已经是 DM 对话 ID(以 D
开头),但另一项不是。为了使这项工作始终如一,我创建了 conversationsToNotify
数组,其中包含调用 conversations.open
创建 DM 后每个用户的对话 ID。现在在代码中,您将看到 conversationsToNotify.map()
而不是 userzArray.map()
。 您的 Slack 应用现在需要 im:write
和 chat:write
权限范围 (添加范围后不要忘记重新安装)。如果数组中的用户数量变大,查找对话 ID 会减慢您的应用程序的启动速度。我的建议是将对话 ID 保存在您的代码(或数据库)中,而不是用户 ID。这将确保您的应用程序始终如一地快速启动。
- 有机会做得更好。当第一次调用
chat.postMessage
失败时会发生什么?按照我上面编写代码的方式,错误将记录到控制台,但稍后如果第二次调用失败,则无从知晓。这是因为 Promise.all()
returns 一个 promise 将 reject 一旦任何一个 promise 拒绝,然后忽略之后发生的事情。如果您使用的是 Node v12.9.0 或更高版本,我建议您改用 Promise.allSettled()
(这也需要对 catch
子句进行一些更改)。
- 一般清理:
- 在监听器中到处使用
message
参数而不是 payload
参数。在处理 message
事件时,这些值实际上是相同的。 payload
主要只在处理多种事件(动作、事件、视图、快捷方式等)的中间件中有用,因此只有一种方法可以引用它们的所有有效负载。
- 将
userzArray
移到侦听器之外,并使其成为常量。每次运行时都在侦听器中重新声明它是没有意义的,而且它不会改变。
- 我添加了将用户 ID 转换为对话 ID 的功能 (
convertUsersToConversations
)。此函数在应用程序启动之前调用,以避免在应用程序知道要通知哪些渠道之前处理传入消息的竞争条件。
- 将消息的文本内容格式化为引用文本,格式化频道提及,并格式化永久链接。我还建议的一项改进是使用 context block 来显示消息作者的姓名和头像。
我正在使用 JavaScript Bolt 框架构建一个 Slack 应用程序。该应用程序的概念只是收听频道中特定的消息关键字,然后将这些消息转发给应用程序的用户。
我想要实现的是在转发的邮件中包含一个永久链接。我正在尝试使用 chat.getPermalink
方法获取 url,然后将其包含在我的 chat.postMessage
方法中。我正在尝试利用 Bolt 的 'Context' 将 chat.getPermalink 中的 属性 传递给 chat.postMessage。我在这里寻求帮助,因为我无法让 Context 工作..
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
let token = process.env.SLACK_BOT_TOKEN,
web = new WebClient(token);
let jira_text = "jira";
let rdu_qa = '@rdu_qa';
//Get permalink
async function PermaLinks({payload, context, next}) {
let perm = app.client.chat.getPermalink({
token: context.botToken,
channel: "C0109KMQCFQ",
message_ts: payload.ts
});
context.permalink = perm.permalink;
await next();
}
app.event('message', PermaLinks, async ({ payload, message, context}) => {
let userzArray = ["D010Q34TQL9", "UVBBD8989"];
//if channel is general and incldues the text 'Jira' or 'rdu_qa'
if (payload.channel === "C0109KMQCFQ") {
if (payload.text.includes(jira_text) || payload.text.includes(rdu_qa)) {
try {
// Call the chat.postMessage to each of the users
let oneUser = await userzArray.forEach(userId => { app.client.chat.postMessage({
token: context.botToken,
bot_id: "USLACKBOT",
channel: userId,
blocks: [
{
type: "section",
text: {
text: payload.text,
type: "mrkdwn"
},
fields: [
{
type: "mrkdwn",
text: `posted by <@${message.user}>`
},
{
type:"mrkdwn",
text: "in General channel" //channel.name//getChannelNameGeneral
},
{
type:"mrkdwn",
text: context.permalink // Permalink should be right here
}
]
},
{
"type": "divider"
},
] // End of block of Jira notification stuff
});
});
// console.log(result);
} catch (error) {
console.error(error);
}
} // If text sent to General channel includes keyword 'Jira' or 'rdu_qa'
} //end of if message was posted in General channel
我可以在示例代码中看到一些问题,但我认为关于 context
的主要问题是您将 Promise 存储为 context.permalink
,而不是实际结果的方法调用。为了存储结果,你应该在调用方法之前使用await
关键字(app.client.chat.getPermalink(...)
)。
我已经修改了你在这里分享的代码,我将在下面解释修改。
const { App } = require('@slack/bolt');
const token = process.env.SLACK_BOT_TOKEN
const app = new App({
signingSecret: process.env.SLACK_SIGNING_SECRET,
token,
});
// Users who should be notified when certain messages are heard
let userzArray = ["D010Q34TQL9", "UVBBD8989"];
// Conversation IDs corresponding to the users in the array above. This variable will be set automatically when the app starts.
let conversationsToNotify;
// Match messages that include the text 'jira' or '@rdu_qa'
app.message(/jira|@rdu_qa/, async ({ message, client }) => {
// Match the messages that are in the specified channel
if (message.channel === 'C0109KMQCFQ') {
try {
// Get a permalink to this message
const permalinkResult = await client.chat.getPermalink({
channel: message.channel,
message_ts: message.ts,
});
// Send a message to each user containing the permalink for this message
await Promise.all(conversationsToNotify.map((conversationId) => {
return client.chat.postMessage({
channel: conversationId,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `>>> ${payload.text}`,
},
fields: [
{
type: 'mrkdwn',
text: `posted by <@${message.user}>`,
},
{
type: 'mrkdwn',
text: `in <#${message.channel}>`,
},
{
type:'mrkdwn',
text: `<Original|${permalinkResult.permalink}>`,
},
],
},
{
type: 'divider'
},
],
});
}));
} catch (error) {
console.error(error);
}
}
});
async function convertUsersToConversations(input) {
return Promise.all(input.map((id) => {
// For all IDs that seem like user IDs, convert them to a DM conversation ID
if (id.startsWith('U')) {
return app.client.conversations.open({
token,
users: id,
})
.then((result) => result.channel.id);
}
// For all IDs that don't seem to belong to a user, return them as is
return id;
}));
});
(async () => {
// Start the app
conversationsToNotify = await convertUsersToConversations(userzArray);
await app.start(process.env.PORT || 3000);
console.log('⚡️ Bolt app is running!');
})();
- 我删除了新
WebClient
对象的初始化。在 Bolt v1.6.0 及更高版本中,侦听器和中间件中有一个client
参数可用,您可以使用它来调用 Web API 方法。使用client
参数的优点是您不需要从上下文中读取令牌并将其作为参数传递给您自己的每个方法调用,Bolt 会为您找到正确的令牌。 - 我没有使用
app.event('message', ...)
方法来监听消息事件,而是改为使用app.message(...)
。后者的工作原理基本相同,但还有一个优势:您可以传递一个模式来匹配消息文本作为第一个参数(在侦听器函数之前):app.message(pattern, ...)
。这有助于消除侦听器内部的一些条件。我没有仅使用两个字符串变量jira_text
和@rdu_qa
,而是将它们组合在一个正则表达式中,当在文本中看到这些值中的任何一个时匹配:/jira|@rdu_qa/
. - 我没有使用中间件来查找消息的固定链接,而是将该代码移到了侦听器中。中间件应该用于跨多个侦听器重用代码(或全局中间件以跨所有侦听器重用代码)。在您的示例中,查找永久链接的代码似乎没有被重用,但如果您在许多侦听器中使用它,它应该相对容易提取。另一个优点是现在逻辑仅在 模式匹配后运行 ,因此您无需为机器人在它所属的所有频道中看到的每条消息进行这些调用(这对性能来说要好得多)。
- 使用
Promise.all()
将每次调用chat.postMessage
的Promise收集到一个promise中。目前,您正在使用userzArray.forEach(...)
,而 doesn't return anything. So then usingawait
on that value will immediately resolve, and doesn't really do anything useful. What we need to do is collect each of the Promises and wait for them to all complete. This is whatPromise.all()
does. We just need an array of Promises to pass in, which we can get by simply changinguserzArray.forEach()
touserzArray.map()
。- 您的呼叫方式有问题
chat.postMessage
。您正在尝试使用 Slackbot 发送这些消息,但不建议这样做,因为用户不太可能了解该消息的来源。相反,您应该将此消息作为 DM 从您的 bot 用户发送。为此,对于要向其发送此通知的每个用户,您需要一个对话 ID,而不是用户 ID。userzArray
中的一项已经是 DM 对话 ID(以D
开头),但另一项不是。为了使这项工作始终如一,我创建了conversationsToNotify
数组,其中包含调用conversations.open
创建 DM 后每个用户的对话 ID。现在在代码中,您将看到conversationsToNotify.map()
而不是userzArray.map()
。 您的 Slack 应用现在需要im:write
和chat:write
权限范围 (添加范围后不要忘记重新安装)。如果数组中的用户数量变大,查找对话 ID 会减慢您的应用程序的启动速度。我的建议是将对话 ID 保存在您的代码(或数据库)中,而不是用户 ID。这将确保您的应用程序始终如一地快速启动。 - 有机会做得更好。当第一次调用
chat.postMessage
失败时会发生什么?按照我上面编写代码的方式,错误将记录到控制台,但稍后如果第二次调用失败,则无从知晓。这是因为Promise.all()
returns 一个 promise 将 reject 一旦任何一个 promise 拒绝,然后忽略之后发生的事情。如果您使用的是 Node v12.9.0 或更高版本,我建议您改用Promise.allSettled()
(这也需要对catch
子句进行一些更改)。
- 您的呼叫方式有问题
- 一般清理:
- 在监听器中到处使用
message
参数而不是payload
参数。在处理message
事件时,这些值实际上是相同的。payload
主要只在处理多种事件(动作、事件、视图、快捷方式等)的中间件中有用,因此只有一种方法可以引用它们的所有有效负载。 - 将
userzArray
移到侦听器之外,并使其成为常量。每次运行时都在侦听器中重新声明它是没有意义的,而且它不会改变。 - 我添加了将用户 ID 转换为对话 ID 的功能 (
convertUsersToConversations
)。此函数在应用程序启动之前调用,以避免在应用程序知道要通知哪些渠道之前处理传入消息的竞争条件。 - 将消息的文本内容格式化为引用文本,格式化频道提及,并格式化永久链接。我还建议的一项改进是使用 context block 来显示消息作者的姓名和头像。
- 在监听器中到处使用