slack.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. const debug = require('debug')('growi:util:slack');
  2. const urljoin = require('url-join');
  3. /**
  4. * slack
  5. */
  6. /* eslint-disable no-use-before-define */
  7. module.exports = function(crowi) {
  8. const { WebClient } = require('@slack/web-api');
  9. const { configManager } = crowi;
  10. const slack = {};
  11. const postWithWebApi = async(messageObj) => {
  12. const client = new WebClient(configManager.getConfig('notification', 'slack:token'));
  13. // stringify attachments
  14. if (messageObj.attachments != null) {
  15. messageObj.attachments = JSON.stringify(messageObj.attachments);
  16. }
  17. try {
  18. await client.chat.postMessage(messageObj);
  19. }
  20. catch (error) {
  21. debug('Post error', error);
  22. debug('Sent data to slack is:', messageObj);
  23. throw error;
  24. }
  25. };
  26. const convertMarkdownToMarkdown = function(body) {
  27. const url = crowi.appService.getSiteUrl();
  28. return body
  29. .replace(/\n\*\s(.+)/g, '\n• $1')
  30. .replace(/#{1,}\s?(.+)/g, '\n*$1*')
  31. .replace(/(\[(.+)\]\((https?:\/\/.+)\))/g, '<$3|$2>')
  32. .replace(/(\[(.+)\]\((\/.+)\))/g, `<${url}$3|$2>`);
  33. };
  34. const prepareAttachmentTextForCreate = function(page, user) {
  35. let body = page.revision.body;
  36. if (body.length > 2000) {
  37. body = `${body.substr(0, 2000)}...`;
  38. }
  39. return convertMarkdownToMarkdown(body);
  40. };
  41. const prepareAttachmentTextForUpdate = function(page, user, previousRevision) {
  42. const diff = require('diff');
  43. let diffText = '';
  44. diff.diffLines(previousRevision.body, page.revision.body).forEach((line) => {
  45. debug('diff line', line);
  46. const value = line.value.replace(/\r\n|\r/g, '\n'); // eslint-disable-line no-unused-vars
  47. if (line.added) {
  48. diffText += `${line.value} ... :lower_left_fountain_pen:`;
  49. }
  50. else if (line.removed) {
  51. // diffText += '-' + line.value.replace(/(.+)?\n/g, '- $1\n');
  52. // 1以下は無視
  53. if (line.count > 1) {
  54. diffText += `:wastebasket: ... ${line.count} lines\n`;
  55. }
  56. }
  57. else {
  58. // diffText += '...\n';
  59. }
  60. });
  61. debug('diff is', diffText);
  62. return diffText;
  63. };
  64. const prepareAttachmentTextForComment = function(comment) {
  65. let body = comment.comment;
  66. if (body.length > 2000) {
  67. body = `${body.substr(0, 2000)}...`;
  68. }
  69. if (comment.isMarkdown) {
  70. return convertMarkdownToMarkdown(body);
  71. }
  72. return body;
  73. };
  74. slack.prepareSlackMessageForPage = (page, user, channel, updateType, previousRevision) => {
  75. const appTitle = crowi.appService.getAppTitle();
  76. const url = crowi.appService.getSiteUrl();
  77. let body = page.revision.body;
  78. if (updateType === 'create') {
  79. body = prepareAttachmentTextForCreate(page, user);
  80. }
  81. else {
  82. body = prepareAttachmentTextForUpdate(page, user, previousRevision);
  83. }
  84. const attachment = {
  85. color: '#263a3c',
  86. author_name: `@${user.username}`,
  87. author_link: urljoin(url, 'user', user.username),
  88. author_icon: user.image,
  89. title: page.path,
  90. title_link: urljoin(url, page.id),
  91. text: body,
  92. mrkdwn_in: ['text'],
  93. };
  94. if (user.image) {
  95. attachment.author_icon = user.image;
  96. }
  97. const message = {
  98. channel: (channel != null) ? `#${channel}` : undefined,
  99. username: appTitle,
  100. text: getSlackMessageTextForPage(page.path, page.id, user, updateType),
  101. attachments: [attachment],
  102. };
  103. return message;
  104. };
  105. slack.prepareSlackMessageForComment = (comment, user, channel, path) => {
  106. const appTitle = crowi.appService.getAppTitle();
  107. const url = crowi.appService.getSiteUrl();
  108. const body = prepareAttachmentTextForComment(comment);
  109. const attachment = {
  110. color: '#263a3c',
  111. author_name: `@${user.username}`,
  112. author_link: urljoin(url, 'user', user.username),
  113. author_icon: user.image,
  114. text: body,
  115. mrkdwn_in: ['text'],
  116. };
  117. if (user.image) {
  118. attachment.author_icon = user.image;
  119. }
  120. const message = {
  121. channel: (channel != null) ? `#${channel}` : undefined,
  122. username: appTitle,
  123. text: getSlackMessageTextForComment(path, String(comment.page), user),
  124. attachments: [attachment],
  125. };
  126. return message;
  127. };
  128. /**
  129. * For GlobalNotification
  130. *
  131. * @param {string} messageBody
  132. * @param {string} attachmentBody
  133. * @param {string} slackChannel
  134. */
  135. slack.prepareSlackMessageForGlobalNotification = async(messageBody, attachmentBody, slackChannel) => {
  136. const appTitle = crowi.appService.getAppTitle();
  137. const attachment = {
  138. color: '#263a3c',
  139. text: attachmentBody,
  140. mrkdwn_in: ['text'],
  141. };
  142. const message = {
  143. channel: (slackChannel != null) ? `#${slackChannel}` : undefined,
  144. username: appTitle,
  145. text: messageBody,
  146. attachments: [attachment],
  147. };
  148. return message;
  149. };
  150. const getSlackMessageTextForPage = function(path, pageId, user, updateType) {
  151. let text;
  152. const url = crowi.appService.getSiteUrl();
  153. const pageUrl = `<${urljoin(url, pageId)}|${path}>`;
  154. if (updateType === 'create') {
  155. text = `:rocket: ${user.username} created a new page! ${pageUrl}`;
  156. }
  157. else {
  158. text = `:heavy_check_mark: ${user.username} updated ${pageUrl}`;
  159. }
  160. return text;
  161. };
  162. const getSlackMessageTextForComment = function(path, pageId, user) {
  163. const url = crowi.appService.getSiteUrl();
  164. const pageUrl = `<${urljoin(url, pageId)}|${path}>`;
  165. const text = `:speech_balloon: ${user.username} commented on ${pageUrl}`;
  166. return text;
  167. };
  168. slack.postPage = (page, user, channel, updateType, previousRevision) => {
  169. const messageObj = slack.prepareSlackMessageForPage(page, user, channel, updateType, previousRevision);
  170. return slackPost(messageObj);
  171. };
  172. slack.postComment = (comment, user, channel, path) => {
  173. const messageObj = slack.prepareSlackMessageForComment(comment, user, channel, path);
  174. return slackPost(messageObj);
  175. };
  176. slack.sendGlobalNotification = async(messageBody, attachmentBody, slackChannel) => {
  177. const messageObj = await slack.prepareSlackMessageForGlobalNotification(messageBody, attachmentBody, slackChannel);
  178. return slackPost(messageObj);
  179. };
  180. const slackPost = (messageObj) => {
  181. return postWithWebApi(messageObj);
  182. };
  183. return slack;
  184. };