slack.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /**
  2. * slack
  3. */
  4. module.exports = function(crowi) {
  5. 'use strict';
  6. const SLACK_URL = 'https://slack.com';
  7. const debug = require('debug')('crowi:util:slack'),
  8. Config = crowi.model('Config'),
  9. SlackWebClient = require('@slack/client').WebClient,
  10. sprintf = require('sprintf'),
  11. pageEvent = crowi.event('page'),
  12. slack = {};
  13. slack.client = undefined;
  14. // get client with access token,
  15. // if access token is not fetched, return undefiend
  16. slack.getClient = function() {
  17. // alreay created
  18. if (slack.client) {
  19. return slack.client;
  20. }
  21. const config = crowi.getConfig();
  22. let client;
  23. if (Config.hasSlackToken(config)) {
  24. client = new SlackWebClient(config.notification['slack:token']);
  25. slack.client = client;
  26. }
  27. return slack.client;
  28. };
  29. // this is called to generate redirect_uri
  30. slack.getSlackAuthCallbackUrl = function()
  31. {
  32. var config = crowi.getConfig();
  33. // Web アクセスがきてないと app:url がセットされないので crowi.setupSlack 時にはできない
  34. // cli, bot 系作るときに問題なりそう
  35. return (config.crowi['app:url'] || '') + '/admin/notification/slackAuth';
  36. }
  37. // this is called to get the url for oauth screen
  38. slack.getAuthorizeURL = function () {
  39. const config = crowi.getConfig();
  40. if (Config.hasSlackConfig(config)) {
  41. const slackClientId = config.notification['slack:clientId'];
  42. const redirectUri = slack.getSlackAuthCallbackUrl();
  43. return `${SLACK_URL}/oauth/authorize?client_id=${slackClientId}&redirect_uri=${redirectUri}&scope=chat:write:bot`;
  44. } else {
  45. return '';
  46. }
  47. }
  48. // this is called to get access token with code (oauth process)
  49. slack.getOauthAccessToken = function(code) {
  50. const client = new SlackWebClient();
  51. const config = crowi.getConfig();
  52. const clientId = config.notification['slack:clientId'];
  53. const clientSecret = config.notification['slack:clientSecret'];
  54. const redirectUri = slack.getSlackAuthCallbackUrl();
  55. return client.oauth.access(clientId, clientSecret, code, {redirect_uri: redirectUri});
  56. }
  57. slack.post = function (channel, message, opts) {
  58. const client = slack.getClient();
  59. return new Promise(function(resolve, reject) {
  60. client.chat.postMessage(channel, message, opts, function(err, res) {
  61. if (err) {
  62. debug('Post error', err, res);
  63. debug('Sent data to slack is:', message);
  64. return reject(err);
  65. }
  66. resolve(res);
  67. });
  68. });
  69. };
  70. slack.convertMarkdownToMrkdwn = function(body) {
  71. var config = crowi.getConfig();
  72. var url = '';
  73. if (config.crowi && config.crowi['app:url']) {
  74. url = config.crowi['app:url'];
  75. }
  76. body = body
  77. .replace(/\n\*\s(.+)/g, '\n• $1')
  78. .replace(/#{1,}\s?(.+)/g, '\n*$1*')
  79. .replace(/(\[(.+)\]\((https?:\/\/.+)\))/g, '<$3|$2>')
  80. .replace(/(\[(.+)\]\((\/.+)\))/g, '<' + url + '$3|$2>')
  81. ;
  82. return body;
  83. };
  84. slack.prepareAttachmentTextForCreate = function(page, user) {
  85. var body = page.revision.body;
  86. if (body.length > 2000) {
  87. body = body.substr(0, 2000) + '...';
  88. }
  89. return this.convertMarkdownToMrkdwn(body);
  90. };
  91. slack.prepareAttachmentTextForUpdate = function(page, user, previousRevision) {
  92. var diff = require('diff');
  93. var diffText = ''
  94. diff.diffLines(previousRevision.body, page.revision.body).forEach(function(line) {
  95. debug('diff line', line)
  96. var value = line.value.replace(/\r\n|\r/g, '\n');
  97. if (line.added) {
  98. diffText += sprintf(':pencil2: ...\n%s', line.value);
  99. } else if (line.removed) {
  100. // diffText += '-' + line.value.replace(/(.+)?\n/g, '- $1\n');
  101. // 1以下は無視
  102. if (line.count > 1) {
  103. diffText += sprintf(':wastebasket: ... %s lines\n', line.count);
  104. }
  105. } else {
  106. //diffText += '...\n';
  107. }
  108. });
  109. debug('diff is', diffText)
  110. return diffText;
  111. };
  112. slack.prepareSlackMessage = function(page, user, channel, updateType, previousRevision) {
  113. var config = crowi.getConfig();
  114. var url = config.crowi['app:url'] || '';
  115. var body = page.revision.body;
  116. if (updateType == 'create') {
  117. body = this.prepareAttachmentTextForCreate(page, user);
  118. } else {
  119. body = this.prepareAttachmentTextForUpdate(page, user, previousRevision);
  120. }
  121. var attachment = {
  122. color: '#263a3c',
  123. author_name: '@' + user.username,
  124. author_link: url + '/user/' + user.username,
  125. author_icon: user.image,
  126. title: page.path,
  127. title_link: url + '/' + page._id,
  128. text: body,
  129. mrkdwn_in: ["text"],
  130. };
  131. if (user.image) {
  132. attachment.author_icon = user.image;
  133. }
  134. var message = {
  135. channel: '#' + channel,
  136. username: 'Crowi',
  137. text: this.getSlackMessageText(page, user, updateType),
  138. attachments: [attachment],
  139. };
  140. return message;
  141. };
  142. slack.getSlackMessageText = function(page, user, updateType) {
  143. let text;
  144. const config = crowi.getConfig();
  145. const url = config.crowi['app:url'] || '';
  146. const pageLink = `<${url}/${page._id}|${page.path}>`;
  147. if (updateType == 'create') {
  148. text = sprintf(':white_check_mark: %s created a new page! %s', user.username, pageLink);
  149. } else {
  150. text = sprintf(':up: %s updated %s', user.username, pageLink);
  151. }
  152. return text;
  153. };
  154. return slack;
  155. };