1
0

phabricator.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import { MessageEmbed } from 'discord.js';
  2. import logging from '../util/logging.js';
  3. import { got, escapeFormatting, limitLength } from '../util/functions.js';
  4. /**
  5. * Sends a Phabricator task.
  6. * @param {import('../util/i18n.js').default} lang - The user language.
  7. * @param {import('discord.js').Message} msg - The Discord message.
  8. * @param {import('../util/wiki.js').default} wiki - The wiki.
  9. * @param {URL} link - The link.
  10. * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  11. * @param {String} [spoiler] - If the response is in a spoiler.
  12. * @param {Boolean} [noEmbed] - If the response should be without an embed.
  13. */
  14. export default function phabricator_task(lang, msg, wiki, link, reaction, spoiler = '', noEmbed = false) {
  15. var regex = /^(?:https?:)?\/\/phabricator\.(wikimedia|miraheze)\.org\/T(\d+)(?:#|$)/.exec(link.href);
  16. if ( !regex || !process.env['phabricator_' + regex[1]] ) {
  17. logging(wiki, msg.guildId, 'interwiki');
  18. msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + link + ( noEmbed ? '>' : ' ' ) + spoiler );
  19. if ( reaction ) reaction.removeEmoji();
  20. return;
  21. }
  22. var site = 'https://phabricator.' + regex[1] + '.org/';
  23. logging(site, msg.guildId, 'phabricator', regex[1]);
  24. got.get( site + 'api/maniphest.search?api.token=' + process.env['phabricator_' + regex[1]] + '&attachments[projects]=1&constraints[ids][0]=' + regex[2] ).then( response => {
  25. var body = response.body;
  26. if ( response.statusCode !== 200 || !body?.result?.data || body.error_code ) {
  27. console.log( '- ' + response.statusCode + ': Error while getting the Phabricator task: ' + body?.error_info );
  28. msg.sendChannelError( spoiler + ( noEmbed ? '<' : ' ' ) + link + ( noEmbed ? '>' : ' ' ) + spoiler );
  29. if ( reaction ) reaction.removeEmoji();
  30. return;
  31. }
  32. if ( !body.result.data.length ) {
  33. msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + link + ( noEmbed ? '>' : ' ' ) + spoiler );
  34. if ( reaction ) reaction.removeEmoji();
  35. return;
  36. }
  37. var task = body.result.data[0];
  38. var status = '**' + task.fields.status.name + ':** ' + escapeFormatting(task.fields.name) + '\n';
  39. if ( !msg.showEmbed() || noEmbed ) {
  40. msg.sendChannel( spoiler + status + '<' + link + '>' + spoiler );
  41. if ( reaction ) reaction.removeEmoji();
  42. return;
  43. }
  44. var summary = escapeFormatting(task.fields.name);
  45. if ( summary.length > 250 ) summary = summary.substring(0, 250) + '\u2026';
  46. var embed = new MessageEmbed().setAuthor( {name: 'Phabricator'} ).setTitle( summary ).setURL( link ).addField( 'Status', escapeFormatting(task.fields.status.name), true ).addField( 'Priority', escapeFormatting(task.fields.priority.name), true );
  47. if ( task.fields.subtype !== 'default' ) embed.addField( 'Subtype', escapeFormatting(task.fields.subtype), true );
  48. var description = parse_text( task.fields.description.raw, site );
  49. if ( description.length > 2000 ) description = limitLength(description, 2000, 40);
  50. embed.setDescription( description );
  51. Promise.all([
  52. ( task.attachments.projects.projectPHIDs.length ? got.get( site + 'api/phid.lookup?api.token=' + process.env['phabricator_' + regex[1]] + '&' + task.attachments.projects.projectPHIDs.map( (project, i) => 'names[' + i + ']=' + project ).join('&') ).then( presponse => {
  53. var pbody = presponse.body;
  54. if ( presponse.statusCode !== 200 || !pbody?.result || pbody.error_code ) {
  55. console.log( '- ' + presponse.statusCode + ': Error while getting the projects: ' + pbody?.error_info );
  56. return;
  57. }
  58. var projects = Object.values(pbody.result);
  59. var tags = projects.map( project => {
  60. return '[' + escapeFormatting(project.fullName) + '](' + project.uri + ')';
  61. } ).join(',\n');
  62. if ( tags.length > 1000 ) tags = projects.map( project => project.fullName ).join(',\n');
  63. if ( tags.length > 1000 ) tags = tags.substring(0, 1000) + '\u2026';
  64. embed.addField( 'Tags', tags );
  65. }, error => {
  66. console.log( '- Error while getting the projects: ' + error );
  67. } ) : undefined ),
  68. ( /^#\d+$/.test( link.hash ) ? got.get( site + 'api/transaction.search?api.token=' + process.env['phabricator_' + regex[1]] + '&objectIdentifier=' + task.phid ).then( tresponse => {
  69. var tbody = tresponse.body;
  70. if ( tresponse.statusCode !== 200 || !tbody?.result?.data || tbody.error_code ) {
  71. console.log( '- ' + tresponse.statusCode + ': Error while getting the task transactions: ' + tbody?.error_info );
  72. return;
  73. }
  74. var comment = tbody.result.data.find( transaction => '#' + transaction.id === link.hash );
  75. if ( comment.type === 'comment' ) {
  76. var content = parse_text( comment.comments[0].content.raw, site );
  77. if ( content.length > 1000 ) content = limitLength(content, 1000, 20);
  78. embed.spliceFields( 0, 0, {name: 'Comment', value: content} );
  79. if ( embed.description.length > 500 ) embed.setDescription( limitLength(description, 500, 250) );
  80. }
  81. }, error => {
  82. console.log( '- Error while getting the task transactions: ' + error );
  83. } ) : undefined )
  84. ]).finally( () => {
  85. msg.sendChannel( {content: spoiler + status + '<' + link + '>' + spoiler, embeds: [embed]} );
  86. if ( reaction ) reaction.removeEmoji();
  87. } );
  88. }, error => {
  89. console.log( '- Error while getting the Phabricator task: ' + error );
  90. msg.sendChannelError( spoiler + ( noEmbed ? '<' : ' ' ) + link + ( noEmbed ? '>' : ' ' ) + spoiler );
  91. if ( reaction ) reaction.removeEmoji();
  92. } );
  93. }
  94. /**
  95. * Parse Phabricator text.
  96. * @param {String} text - The text to parse.
  97. * @param {String} site - The site the Phabricator is for.
  98. * @returns {String}
  99. */
  100. function parse_text(text, site) {
  101. text = text.replace( /```lang=/g, '```' );
  102. text = text.replace( /^>>! (.+?)$/gm, '> *$1*' );
  103. text = text.replace( /^>>/gm, '> >' );
  104. text = text.replace( /##(.+?)##/g, '`$1`' );
  105. text = text.replace( /!!(.+?)!!/g, '`$1`' );
  106. text = text.replace( /(?<!https?:)\/\/(.+?)(?<!https?:)\/\//g, '*$1*' );
  107. text = text.replace( /\[\[ ?(.+?) ?(?:\| ?(.+?) ?)?\]\]/g, (match, target, display) => {
  108. var link = target;
  109. if ( /^(?:(?:https?:)?\/\/|\/|#)/.test(target) ) link = new URL(target, site).href;
  110. else link = site + 'w/' + target;
  111. return '[' + ( display || target ) + '](' + link + ')';
  112. } );
  113. text = text.replace( /(?<!\w)@([\w-]+)\b/g, '[@$1](' + site + 'p/$1)' );
  114. text = text.replace( /(?<!https?:\/\/[^\s]+)\b\{?(r[A-Z]+[a-f\d]+)\}?\b/g, '[$1](' + site + '$1)' );
  115. text = text.replace( /(?<!https?:\/\/[^\s]+)\b\{?([CDFHLMPQTV]\d+(?:#\d+)?)\}?\b/g, '[$1](' + site + '$1)' );
  116. text = text.replace( /(?<!https?:\/\/[^\s]+)#([a-z0-9_-]+)\b/g, '[#$1](' + site + 'tag/$1)' );
  117. return text;
  118. }