集成任何应用程序与 Slack






3.47/5 (7投票s)
本文演示了如何将任何应用程序与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_id
、client_secret
、scope
、redirect_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_cmd的req.body.response_url
)将其发送到Slack。有关buildGroupContent
和sendSlackMsgWithObject
的函数定义,请参阅实际代码库。
群组命令的截图
监听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 添加问题或答案。如果您有任何此类集成,我很乐意了解,请在此处发布或提供任何建议。
历史
这是我文章的当前版本,但如果我遇到任何建议或改进,我将在此处发布。