slack.js 6.5 KB

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