slack.js 6.4 KB

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