65.9K
CodeProject 正在变化。 阅读更多。
Home

集成任何应用程序与 Slack

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.47/5 (7投票s)

2019年2月21日

Apache

10分钟阅读

viewsIcon

18344

downloadIcon

156

本文演示了如何将任何应用程序与Slack集成,并利用您应用程序的功能在Slack界面中进行操作。

引言

本文展示了任何应用程序如何连接到任何Slack工作区,其中应用程序用户可以从Slack进行身份验证,并从Slack执行与应用程序通信的活动。此外,应用程序还可以向Slack工作区进行通信。

背景

在我的一生中,我遇到了各种与应用程序通信的方式。举个例子,比如Dropbox应用,你可以看到有很多种方式与它通信,比如网页应用、各种移动应用、语音应用(例如Dropbox的Alexa应用)等等!现在,如果你最喜欢的工具(即Slack)让你连接到Dropbox,你会作何反应?这会节省很多时间,不是吗!你不必偏离你的工作,只需集成Dropbox并发出一些命令即可获取你的输出。

现在,将上述思维过程应用于您的应用程序的消费者。将本文视为一个辅助指南,为您的应用程序的专业用户/专业消费者启用一个额外的媒介/界面。这将增强他们与您的应用程序的交互方式,并提高他们使用您的应用程序的效率,即,为您的产品/应用程序增加一个卖点。

如果你对Slack感到好奇,这里有更多关于它的背景信息。它是一个团队协作工具,可以让你创建一个属于你组织的工作区。在你的组织内部,你可以建立封闭群组或开放群组,这些群组被称为频道。然后,每个人都可以与你组织中的其他人进行通信。

范围

如果您阅读了背景,那么通过集成来增强您的应用程序有巨大的空间。然而,在本文中,范围是有限的。从现在开始,我将不再谈论应用程序,而是举一个真实的例子。在这里,我将“Buzz Forum”作为应用程序。这个应用程序允许您注册。管理员可以创建由一些成员组成的群组。成员可以提出问题,其他成员可以发布答案,这就是这个应用程序的全部内容。这个应用程序使用“oauth”进行身份验证。它提供API来获取群组,支持事件的webhook等。这个应用程序托管在公共网站上。出于演示目的,我不会提供关于“Buzz forum”应用程序URL的任何进一步细节,而是将其用作“http://app.example.com”。由于这个应用程序主要是针对“coder”的,从现在开始我将使用“coder”应用程序。

演示中包含的内容

  • 构建带有所需设置的Slack应用
  • 将Slack应用安装到特定频道
  • 从Slack登录到coder应用
  • 从Slack获取coder应用中的所有群组
  • 从Slack订阅任何群组
  • 如果coder中发布了任何新问题,则向Slack频道发布更新

该演示存在一些限制

  • 为了使Slack应用程序可分发,我们必须对Slack托管的应用程序使用HTTPS(在所有必需的URL中)。在本文中,我使用的是HTTP,这使得它不可分发,我将参考旧的演示,当时允许通过URL安装HTTP启用的应用程序。
  • 这种集成适用于启用 OAuth 的应用程序,最好是 Web 应用程序。但是,将来如果有对其他类型身份验证的需求,我可以考虑一下。
  • 我正在使用Node.js进行此演示。所有必需的凭据/配置都保存在文件系统中,而不是更推荐的数据库中。
  • 我将更多地关注Slack-coder应用程序集成代码,而不是coder应用程序的功能。所有URL都特意隐藏,而是使用“example.com”作为域名。
  • 集成主要侧重于展示功能,而不是代码标准,显然,代码标准可以改进。

必备组件

  • 我有编码器应用程序的 oauth 凭据,即 client_idclient_secretscoperedirect_url。它托管在“https://app.example.com”。
  • 我使用此链接创建了一个 Slack 帐户 - https://slack.com/get-started。然后我创建了一个名为“code-project-v1”的 Slack 频道。
  • 我有一个有效的 coder 应用程序凭证。
  • Slack-coder应用程序托管在这里 - http://slack.example.com。它必须是公开托管/可访问的。
  • 需要安装 Node 和 npm。Node 版本高于 10,npm 版本高于 5。

Using the Code

我使用了 Node.js,这里是所需的依赖项。

"dependencies": {
  "body-parser": "^1.18.3",
  "express": "^4.16.4",
  "querystring": "^0.2.0",
  "request": "^2.88.0"
}

该项目主要包含2个文件,一个是用于设置(settings.js),一个是用于处理slack回调(server.js)。

这里是项目源码,我将使用它。

创建Slack应用

导航到 https://api.slack.com/apps?new_app=1 并创建一个新应用。在应用名称中,我使用了“CoderApp”,在工作区中,我选择了我的现有工作区。这里显示了对话框

下一步是配置一些所需的设置。

OAuth & 权限

用途:这用于指定当“CoderApp”(即Slack应用)安装在其他工作区时来自Slack的回调。由于我们将把我们的应用程序信息发布到频道,我们需要发布它的权限。

如何设置:按照屏幕截图操作(左侧,单击“OAuth & 权限”)。在重定向URL中,我使用了http://slack.example.com/slack。我使用的权限是“发布到Slack中的特定频道”。

斜杠命令

用途:这可以添加自定义Stack命令。您可能知道现有的斜杠命令,例如“/remind”、“/archive”等。在这里,我们将引入“/coder”作为斜杠命令。使用该命令,您可以登录并列出群组。

如何设置:按照屏幕截图(左侧,单击“斜杠命令”)。在这里,在请求URL中,我使用了http://slack.example.com/slack_cmd

交互式命令

用途:这使得Slack用户可以执行一些用户输入,从而与我们的集成应用程序进行联系。在这里,我们利用它来订阅一个群组。

如何设置:按照截图操作(左侧,点击“交互式命令”)。这里,请求URL设置为“http://slack.example.com/slack_interactive”。

至此,我们完成了Slack应用的设置。现在,转到左侧的基本信息,记下Slack应用ID、客户端ID、客户端密钥等。我们将在下一步中使用它们。以下是为我显示的基本信息。

让我们的应用程序可在其他工作区安装

在OAuth中,重定向URL设置为“http://slack.example.com/slack”。这是在代码中处理的方式

app.get('/slack', function (req, res) {
   settings.slack_callback_data.formData["code"] = req.query.code;
   slack_response_on_install = res;
   console.log("code = " + req.query.code);
   console.log( settings.slack_callback_data);
   request(settings.slack_callback_data, function (error, response, body) {
      if (error) throw new Error(error);
      console.log(body);
      json_body = JSON.parse(body);
      file_name = "data/" + json_body.team_name.replace(/\s+/g, '').toLowerCase() + ".json";
      fs.writeFile(file_name, body, function (err) {});
      sendSlackMsg(json_body.incoming_webhook.url);
      slack_response_on_install.send("Welcome : " + json_body.team_name + 
          " to coder slack connect app."); // This will display the message 
                                           // on installation page.
      slack_response_on_install = null;
   });
})

在设置中,我维护了slack_callback_data,其中包含所有Slack凭据,请参阅下一个代码片段。当有人安装我们的Slack应用程序时,Slack会调用我们的“/slack”端点——这符合设置的重定向URL,它还会发送一个querystring中的代码。我们读取该代码并再次发布到https://slack.com/api/oauth.access,以获取Slack工作区(安装我们应用程序的Slack用户)的访问令牌。获取后,我们会将其保存到文件系统(data/{slack_workspace_name}.json)。然后,我们会发送“欢迎:{slack_workspace_name}”的通知到coder Slack连接应用程序。

以下是slack_callback_data的代码片段。

slack_callback_data: {
  method: 'POST',
  url: 'https://slack.com/api/oauth.access',
  headers: {
    'cache-control': 'no-cache',
    'content-type': 'multipart/form-data;'
  },
  formData: {
    client_id: "XXX.XXX",
    client_secret: "XXXXXXX"
  }
}

在此之后,我们将获得该客户端的Slack凭据,其中incoming_webhook对我们很重要,我们将使用它向Slack频道发送消息。

从Slack验证到Coder应用

这是本文中最棘手的部分之一。在这里,我们将把Slack用户映射到Coder应用用户。您可能想知道为什么我们需要这样做。让我告诉您,从Slack,我们启用了与我们的应用交互,但我们必须告诉我们的集成应用,任何来自Slack的请求都已通过身份验证,可以与代码应用交互。在这里,Slack用户必须首先通过Coder应用进行身份验证,才能获取他们选择的群组的更新。他们必须进入安装了应用的特定频道,在那里,他们必须输入“/coder signin”,这将显示登录按钮。点击后,它将显示Coder应用的登录屏幕,然后是oauth同意屏幕,如果一切顺利,它将显示“感谢您连接Coder应用”。

工作原理:在斜杠命令中,我们添加了对/coder登录的支持,并且要执行操作的URL是“http://slack.example.com/slack_cmd”。以下是服务器端代码

app.post('/slack_cmd', function (req, res) {
   console.log("----- slack command -----");
   console.log(req.body);
   command_reply_url = req.body.response_url;
   signin_token_file = "data/" + req.body.team_domain + "-app-access.json";
   var commands = req.body.text.split(" ");
   if (commands[0] == "signin") {
      res.send(settings.signin_option);
   } else if (commands[0] == "groups") {
      no_of_comm = commands[1];;
      loadGroups("data/" + req.body.team_domain + "-app-access.json", no_of_comm);
      res.send("You will receive groups shortly");
   } else {
      res.send(req.body.text + " is not supported, only `[signin, groups]` supported");
      command_reply_url = null;
      signin_token_file = null;
   }
})

当发送signin命令时,它会发送settings.signin_option来响应。以下是signin_option的代码片段。

signin_option: {
  "text": "Authenticate with coder portal",
  "attachments": [{
    "text": "Click signin",
    "fallback": "You are unable to authenticate",
    "callback_id": "signin_callback",
    "color": "#3AA3E3",
    "attachment_type": "default",
    "actions": [{
      "name": "signin",
      "text": "Signin",
      "type": "button",
      "url": "http://slack.example.com/coder_signin",
      "value": "signin"
    }]
  }]
}

这将看起来是这样的

这里,action url 是 http://slack.example.com/coder_signin,即在服务器端,我们有 coder_signin 的处理程序,如下所示。

// Purpose: on signin request redirect to our App oauth URL
//          once it gets code from redirection, it will request for access_token
//          also it will save the access tocken to /data/teamname-app-access.json
app.get('/coder_signin', function (req, res) {
   console.log("----- coder signin start -----");
   app_response = res;
   if (req.query.code) {
      settings.app_auth.form['code'] = req.query.code;
      request(settings.app_auth, function (error, response, body) {
         if (error) throw new Error(error);
         fs.writeFile(signin_token_file, body, function (err) {});
         onAppSuccessSendToSlack(settings.app_connect_msg);
         signin_token_file = null;
      });
   } else {
      res.writeHead(302, {
         Location: settings.app_init_auth
      });
      res.end();
   }
})

这里,它将对 coder 应用进行 oauth,即首先,它将重定向到 settings.app_init_auth(在“else”部分),即“http://app.example.com/oauth/authorize?response_type=code&client_id={coder_client_id}&redirect_uri=http://slack.example.com/coder_signin”。

然后,coder 应用程序将代码发送到重定向 URL,同样的处理器再次被调用,它在“if”块中接收到代码。再次,它将带有代码、客户端密钥的请求发送到“http://app.example.com/oauth/token”以获取access_token、刷新令牌等。以下是settings.app_auth的代码片段

app_auth: {
  method: 'POST',
  url: 'http://app.example.com/oauth/token',
  headers: {
    'cache-control': 'no-cache',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  form: {
    grant_type: 'authorization_code',
    client_id: "coder_client_id",
    client_secret: "coder_client_secret"
  }
},

所有这些完成后,我们将所有令牌保存到“data/teamname-app-access.json”并向Slack频道发送通知,内容为“感谢您连接coder应用”。

以下是onAppSuccessSendToSlack的代码片段。

// Purpose: On successfully coder App Authentication, Send message to slack
function onAppSuccessSendToSlack(message) {
   sendSlackMsg(command_reply_url, message, function(){
      if (app_response) {
         app_response.send("You may close this tab and open slack");
      }
      app_response = null;
      command_reply_url = null;
   })
}

// Purpose: Send a slack message and execute callback if supplied
function sendSlackMsg(url, message, callback) {
   settings.post_message["url"] = url;
   if (message) {
      settings.post_message.json.text = message;
   }
   request(settings.post_message, function (error, response, body) {
      if (error) throw new Error(error);
      console.log(body);
      if (callback) callback();
   });
}

点击登录后会是这个样子。目前,我已登录到 coder 应用,因此没有显示登录或同意界面。

监听Coder群组命令

请参考“/slack_cmd”处理程序,在那里我们定义了如果我们获取群组,则调用loadGroups方法。用户将发送“/coder groups {count_of_groups}”,例如“/coder groups 2”。这将从coder应用中获取2个最新群组,并在Slack中显示,提供订阅其中一个群组的选项。

以下是loadGroups的代码片段

function loadGroups(team_name, no_of_grp) {
   refreshTheToken(team_name, function(access_token){
      settings.app_groups_api.headers["Authorization"] = 'Bearer ' + access_token;
      settings.app_groups_api.qs.limit = no_of_grp;
      request(settings.app_groups_api, function (error, response, body) {
         if (error) throw new Error(error);
         console.log("Found groups");
         console.log(body);
         body = JSON.parse(body);
         content = {};
         content["text"] = "Total groups: " + body.total_count;
         content["attachments"] = [];
         for (i = 0; i < body.content.length; i++) {
            content["attachments"].push(buildGroupContent(body.content[i]));
         }
         sendSlackMsgWithObject(command_reply_url,content, function () {
             console.log("Group fetch msg sent")
         });
      });
   })    
}

在这里,我调用refreshTheToken以获取新的coder应用令牌,从而避免令牌过期,然后它使用所需的访问令牌(coder应用的)调用coder应用的group_api URL,这为我们获取了群组列表,然后我们构建格式精美的Slack兼容JSON,并通过command_reply_url(即/slack_cmdreq.body.response_url)将其发送到Slack。有关buildGroupContentsendSlackMsgWithObject的函数定义,请参阅实际代码库。

群组命令的截图

监听Slack中的订阅按钮点击

我们已经定义了 Slack 交互式命令,现在它开始发挥作用,请求 URL 是“http://slack.example.com/slack_interactive”。以下是其代码片段

// Purpose: On slack action : such as Post_Idea/ subscribe to challenge
app.post('/slack_interactive', function (req, res) {
   console.log("----- slack interactive -----");
   console.log(req.body);
   if(req.body.payload){
      payload = JSON.parse(req.body.payload);
      if(payload.actions && payload.actions[0].value == "subscribe"){
         comm_id = payload.callback_id.split("_")[1];
         subscribeToGroup(comm_id, payload.team.domain);
         res.send({
            text: "Thanks " + payload.team.domain + " for subscribing to groups!"
         });
      } else {
         res.send({
            text: "listening"
         });
      }
   }
})

在这里,我们从req.body.payload获取有效载荷,并检查命令是否为subscribe,然后调用subscribeToGroup,它将group-id保存在文件系统位置“data/{slack_workspace}-subscribe.json”中,它还会告诉coderapp哪个Slack频道订阅了哪个群组。这将帮助我们在任何群组活动发生时向相关的Slack频道发送通知。然后,我们通过向响应对象发送文本“感谢{channelname}订阅群组!”来响应Slack命令。

从代码应用程序向Slack发送通知

这是最后一篇文章,我们将在此文章中讨论,如果coder应用中发布了任何新问题,我们将向Slack发送通知。为了支持这一点,我添加了一个端点,以下是代码片段

// Purpose: read querystring which should be team_name and message, 
// then send slack message to that team's group
app.get('/message', function (req, res) {
   client = req.query.client;
   var object = JSON.parse(fs.readFileSync('data/' + client + ".json", 'utf8'));
   sendSlackMsg(object.incoming_webhook.url, req.query.msg);
   res.send("msg will be delivered");
})

每当 coder 应用程序中发生任何事件时,我们都会通过“http://slack.example.com/message?client={slack_workspace}&msg={actual_msg}”发布一条消息。以下是它看起来的截图

关注点

这篇文章帮助我将 coder 应用与 Slack 集成,主要用于信息发布,未来计划支持通过 Slack 添加问题或答案。如果您有任何此类集成,我很乐意了解,请在此处发布或提供任何建议。

历史

这是我文章的当前版本,但如果我遇到任何建议或改进,我将在此处发布。

将任何应用程序与Slack集成 - CodeProject - 代码之家
© . All rights reserved.