slack.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /**
  2. * slack
  3. */
  4. module.exports = function(crowi) {
  5. 'use strict';
  6. const debug = require('debug')('growi:util:slack'),
  7. config = crowi.getConfig(),
  8. Config = crowi.model('Config'),
  9. Slack = require('slack-node'),
  10. slack = {};
  11. const postWithIwh = function(messageObj, callback) {
  12. const client = new Slack();
  13. client.setWebhook(config.notification['slack:incomingWebhookUrl']);
  14. client.webhook(messageObj, callback);
  15. };
  16. const postWithWebApi = function(messageObj, callback) {
  17. const client = new Slack(config.notification['slack:token']);
  18. // stringify attachments
  19. if (messageObj.attachments != null) {
  20. messageObj.attachments = JSON.stringify(messageObj.attachments);
  21. }
  22. client.api('chat.postMessage', messageObj, callback);
  23. };
  24. const convertMarkdownToMrkdwn = function(body) {
  25. var url = '';
  26. if (config.crowi && config.crowi['app:url']) {
  27. url = config.crowi['app:url'];
  28. }
  29. body = body
  30. .replace(/\n\*\s(.+)/g, '\n• $1')
  31. .replace(/#{1,}\s?(.+)/g, '\n*$1*')
  32. .replace(/(\[(.+)\]\((https?:\/\/.+)\))/g, '<$3|$2>')
  33. .replace(/(\[(.+)\]\((\/.+)\))/g, '<' + url + '$3|$2>')
  34. ;
  35. return body;
  36. };
  37. const prepareAttachmentTextForCreate = function(page, user) {
  38. var body = page.revision.body;
  39. if (body.length > 2000) {
  40. body = body.substr(0, 2000) + '...';
  41. }
  42. return convertMarkdownToMrkdwn(body);
  43. };
  44. const prepareAttachmentTextForUpdate = function(page, user, previousRevision) {
  45. var diff = require('diff');
  46. var diffText = '';
  47. diff.diffLines(previousRevision.body, page.revision.body).forEach(function(line) {
  48. debug('diff line', line);
  49. /* eslint-disable no-unused-vars */
  50. var value = line.value.replace(/\r\n|\r/g, '\n');
  51. /* eslint-enable */
  52. if (line.added) {
  53. diffText += `:pencil2: ...\n${line.value}`;
  54. }
  55. else if (line.removed) {
  56. // diffText += '-' + line.value.replace(/(.+)?\n/g, '- $1\n');
  57. // 1以下は無視
  58. if (line.count > 1) {
  59. diffText += `:wastebasket: ... ${line.count} lines\n`;
  60. }
  61. }
  62. else {
  63. //diffText += '...\n';
  64. }
  65. });
  66. debug('diff is', diffText);
  67. return diffText;
  68. };
  69. const prepareAttachmentTextForComment = function(comment) {
  70. let body = comment.comment;
  71. if (body.length > 2000) {
  72. body = body.substr(0, 2000) + '...';
  73. }
  74. if (comment.isMarkdown) {
  75. return convertMarkdownToMrkdwn(body);
  76. }
  77. else {
  78. return body;
  79. }
  80. };
  81. const prepareSlackMessageForPage = function(page, user, channel, updateType, previousRevision) {
  82. const url = config.crowi['app:url'] || '';
  83. let body = page.revision.body;
  84. if (updateType == 'create') {
  85. body = prepareAttachmentTextForCreate(page, user);
  86. }
  87. else {
  88. body = prepareAttachmentTextForUpdate(page, user, previousRevision);
  89. }
  90. const attachment = {
  91. color: '#263a3c',
  92. author_name: '@' + user.username,
  93. author_link: url + '/user/' + user.username,
  94. author_icon: user.image,
  95. title: page.path,
  96. title_link: url + '/' + page._id,
  97. text: body,
  98. mrkdwn_in: ['text'],
  99. };
  100. if (user.image) {
  101. attachment.author_icon = user.image;
  102. }
  103. const message = {
  104. channel: '#' + channel,
  105. username: Config.appTitle(config),
  106. text: getSlackMessageTextForPage(page.path, user, updateType),
  107. attachments: [attachment],
  108. };
  109. return message;
  110. };
  111. const prepareSlackMessageForComment = function(comment, user, channel, path) {
  112. const url = config.crowi['app:url'] || '';
  113. const body = prepareAttachmentTextForComment(comment);
  114. const attachment = {
  115. color: '#263a3c',
  116. author_name: '@' + user.username,
  117. author_link: url + '/user/' + user.username,
  118. author_icon: user.image,
  119. text: body,
  120. mrkdwn_in: ['text'],
  121. };
  122. if (user.image) {
  123. attachment.author_icon = user.image;
  124. }
  125. const message = {
  126. channel: '#' + channel,
  127. username: Config.appTitle(config),
  128. text: getSlackMessageTextForComment(path, user),
  129. attachments: [attachment],
  130. };
  131. return message;
  132. };
  133. const getSlackMessageTextForPage = function(path, user, updateType) {
  134. let text;
  135. const url = config.crowi['app:url'] || '';
  136. const pageUrl = `<${url}${path}|${path}>`;
  137. if (updateType == 'create') {
  138. text = `:white_check_mark: ${user.username} created a new page! ${pageUrl}`;
  139. }
  140. else {
  141. text = `:up: ${user.username} updated ${pageUrl}`;
  142. }
  143. return text;
  144. };
  145. const getSlackMessageTextForComment = function(path, user) {
  146. const url = config.crowi['app:url'] || '';
  147. const pageUrl = `<${url}${path}|${path}>`;
  148. const text = `:speech_balloon: ${user.username} commented on ${pageUrl}`;
  149. return text;
  150. };
  151. // slack.post = function (channel, message, opts) {
  152. slack.postPage = (page, user, channel, updateType, previousRevision) => {
  153. const messageObj = prepareSlackMessageForPage(page, user, channel, updateType, previousRevision);
  154. return promisifiedSlackPost(messageObj);
  155. };
  156. slack.postComment = (comment, user, channel, path) => {
  157. const messageObj = prepareSlackMessageForComment(comment, user, channel, path);
  158. return promisifiedSlackPost(messageObj);
  159. };
  160. const promisifiedSlackPost = (messageObj) => {
  161. return new Promise((resolve, reject) => {
  162. // define callback function for Promise
  163. const callback = function(err, res) {
  164. if (err) {
  165. debug('Post error', err, res);
  166. debug('Sent data to slack is:', messageObj);
  167. return reject(err);
  168. }
  169. resolve(res);
  170. };
  171. // when incoming Webhooks is prioritized
  172. if (Config.isIncomingWebhookPrioritized(config)) {
  173. if (Config.hasSlackIwhUrl(config)) {
  174. debug('posting message with IncomingWebhook');
  175. postWithIwh(messageObj, callback);
  176. }
  177. else if (Config.hasSlackToken(config)) {
  178. debug('posting message with Web API');
  179. postWithWebApi(messageObj, callback);
  180. }
  181. }
  182. // else
  183. else {
  184. if (Config.hasSlackToken(config)) {
  185. debug('posting message with Web API');
  186. postWithWebApi(messageObj, callback);
  187. }
  188. else if (Config.hasSlackIwhUrl(config)) {
  189. debug('posting message with IncomingWebhook');
  190. postWithIwh(messageObj, callback);
  191. }
  192. }
  193. resolve();
  194. });
  195. };
  196. return slack;
  197. };