0

我正在使用 JavaScript Bolt 框架构建一个 Slack 应用程序。应用程序的概念只是在频道中收听特定的消息关键字,然后将这些消息转发给应用程序的用户。

我想要实现的是在转发的消息中包含一个永久链接。我正在尝试使用该chat.getPermalink方法获取 url,然后将其包含在我的chat.postMessage方法中。我正在尝试利用 Bolt 的“上下文”将 chat.getPermalink 中的属性传递给 chat.postMessage。我在这里寻求帮助,因为我无法让上下文工作..

   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    
4

1 回答 1

2

我可以在示例代码中看到几个问题,但我认为主要问题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_textand @rdu_qa,而是将它们组合在一个正则表达式中,当其中任何一个值出现在文本中时匹配:/jira|@rdu_qa/.
  • 我没有使用中间件来查找消息的永久链接,而是将该代码移到了侦听器中。应该使用中间件在多个侦听器之间重用代码(或使用全局中间件在所有侦听器之间重用代码)。在您的示例中,查找永久链接的代码似乎没有被重用,但如果您确实在许多侦听器中使用它,它应该相对容易提取。另一个优点是现在逻辑仅在模式匹配运行,因此您不会对机器人在它所属的所有通道中看到的每条消息进行这些调用(这对性能来说要好得多)。
  • 用于Promise.all()将每个调用的 Promise 收集chat.postMessage到一个 Promise 中。目前,您正在使用userzArray.forEach(...),它不会返回任何东西。因此,使用await该值将立即解决,并且实际上并没有做任何有用的事情。我们需要做的是收集每个 Promise 并等待它们全部完成。这就是这样Promise.all()做的。我们只需要传入一个 Promises 数组,只需将其更改userzArray.forEach()userzArray.map().
    • 你打电话的方式有问题chat.postMessage。您正在尝试使用 Slackbot 发送这些消息,但不建议这样做,因为用户不太可能了解该消息的来源。相反,您应该从您的机器人用户那里以 DM 的形式发送此消息。为此,您需要为每个要向其发送此通知的用户提供对话 ID,而不是用户 ID。其中一项userzArray已经是 DM 对话 ID(以 开头D),但另一项不是。为了使这项工作始终如一,我创建了一个数组,其中包含在调用创建 DMconversationsToNotify后每个用户的对话 ID 。conversations.open现在在代码中,您将conversationsToNotify.map()看到userzArray.map(). 您的 Slack 应用程序现在需要im:writechat:write权限范围(添加范围后不要忘记重新安装)。如果数组中的用户数量变大,查找对话 ID 会减慢您的应用程序启动速度。我的建议是将对话 ID 保存在您的代码(或数据库)中,而不是用户 ID。这将确保您的应用程序始终如一地快速启动。
    • 有机会做得更好。当第一次调用chat.postMessage失败时会发生什么?按照我上面写代码的方式,错误会被记录到控制台,但是稍后如果第二次调用失败,就没有办法知道了。那是因为Promise.all()返回一个承诺,一旦任何一个承诺拒绝,就会拒绝,然后忽略之后发生的事情如果您使用的是 Node v12.9.0 或更高版本,我建议您Promise.allSettled()改用(这也需要对您的catch子句进行一些更改)。
  • 一般清理:
    • message在侦听器中到处使用参数而不是payload参数。在处理message事件时,这些实际上是相同的值。payload主要只在处理多种事件(动作、事件、视图、快捷方式等)的中间件中有用,因此有一种方法可以引用它们的所有有效负载。
    • 移到听者userzArray之外,并使其成为常数。每次运行时在侦听器中重新声明它是没有意义的,它不会改变。
    • 我添加了一个将用户 ID 转换为对话 ID 的函数 ( convertUsersToConversations)。在启动应用程序之前调用此函数,以避免在应用程序知道要通知哪些通道之前处理传入消息的竞争条件。
    • 将消息的文本内容格式化为引用文本,格式化频道提及,并格式化永久链接。我还推荐的一项改进是使用上下文块来显示消息作者的姓名和头像图像。
于 2020-06-08T00:34:18.377 回答