RelationsService.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { Inject, Service } from '@tsed/di';
  2. import axios from 'axios';
  3. import { addHours } from 'date-fns';
  4. // import { Relation } from '~/entities/relation';
  5. import { REQUEST_TIMEOUT_FOR_PTOG } from '@growi/slack';
  6. import { RelationMock } from '~/entities/relation-mock';
  7. // import { RelationRepository } from '~/repositories/relation';
  8. import { RelationMockRepository } from '~/repositories/relation-mock';
  9. import loggerFactory from '~/utils/logger';
  10. const logger = loggerFactory('slackbot-proxy:services:RelationsService');
  11. @Service()
  12. export class RelationsService {
  13. @Inject()
  14. // relationRepository: RelationRepository;
  15. relationMockRepository: RelationMockRepository;
  16. async getSupportedGrowiCommands(relation:RelationMock):Promise<any> {
  17. // generate API URL
  18. const url = new URL('/_api/v3/slack-integration/supported-commands', relation.growiUri);
  19. return axios.get(url.toString(), {
  20. headers: {
  21. 'x-growi-ptog-tokens': relation.tokenPtoG,
  22. },
  23. timeout: REQUEST_TIMEOUT_FOR_PTOG,
  24. });
  25. }
  26. async syncSupportedGrowiCommands(relation:RelationMock): Promise<RelationMock> {
  27. const res = await this.getSupportedGrowiCommands(relation);
  28. const { permissionsForBroadcastUseCommands, permissionsForSingleUseCommands } = res.data;
  29. relation.permissionsForBroadcastUseCommands = permissionsForBroadcastUseCommands;
  30. relation.permissionsForSingleUseCommands = permissionsForSingleUseCommands;
  31. relation.expiredAtCommands = addHours(new Date(), 48);
  32. return this.relationMockRepository.save(relation);
  33. }
  34. async syncRelation(relation:RelationMock, baseDate:Date):Promise<RelationMock|null> {
  35. const distanceMillisecondsToExpiredAt = relation.getDistanceInMillisecondsToExpiredAt(baseDate);
  36. if (distanceMillisecondsToExpiredAt < 0) {
  37. try {
  38. return await this.syncSupportedGrowiCommands(relation);
  39. }
  40. catch (err) {
  41. logger.error(err);
  42. return null;
  43. }
  44. }
  45. // 24 hours
  46. if (distanceMillisecondsToExpiredAt < 1000 * 60 * 60 * 24) {
  47. try {
  48. this.syncSupportedGrowiCommands(relation);
  49. }
  50. catch (err) {
  51. logger.error(err);
  52. }
  53. }
  54. return relation;
  55. }
  56. async isPermissionsForSingleUseCommands(relation:RelationMock, growiCommandType:string, channelName:string, baseDate:Date):Promise<boolean> {
  57. const syncedRelation = await this.syncRelation(relation, baseDate);
  58. if (syncedRelation == null) {
  59. return false;
  60. }
  61. const permission = relation.permissionsForSingleUseCommands[growiCommandType];
  62. if (permission == null) {
  63. return false;
  64. }
  65. if (Array.isArray(permission)) {
  66. return permission.includes(channelName);
  67. }
  68. return permission;
  69. }
  70. async isPermissionsUseBroadcastCommands(relation:RelationMock, growiCommandType:string, channelName:string, baseDate:Date):Promise<boolean> {
  71. const syncedRelation = await this.syncRelation(relation, baseDate);
  72. if (syncedRelation == null) {
  73. return false;
  74. }
  75. const permission = relation.permissionsForBroadcastUseCommands[growiCommandType];
  76. if (permission == null) {
  77. return false;
  78. }
  79. if (Array.isArray(permission)) {
  80. return permission.includes(channelName);
  81. }
  82. return permission;
  83. }
  84. async checkPermissionForInteractions(
  85. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  86. relation:RelationMock, channelName:string, callbackId:string, actionId:string,
  87. ):Promise<{isPermittedForInteractions:boolean, commandName:string}> {
  88. let isPermittedForInteractions!:boolean;
  89. let commandName!:string;
  90. const singleUse = Object.keys(relation.permissionsForSingleUseCommands);
  91. const broadCastUse = Object.keys(relation.permissionsForBroadcastUseCommands);
  92. let permissionForInteractions:boolean|string[];
  93. [...singleUse, ...broadCastUse].forEach(async(tempCommandName) => {
  94. // ex. search OR search:handlerName
  95. const commandRegExp = new RegExp(`(^${tempCommandName}$)|(^${tempCommandName}:\\w+)`);
  96. // skip this forEach loop if the requested command is not in permissionsForBroadcastUseCommands and permissionsForSingleUseCommands
  97. if (!commandRegExp.test(actionId) && !commandRegExp.test(callbackId)) {
  98. return;
  99. }
  100. commandName = tempCommandName;
  101. // case: singleUse
  102. permissionForInteractions = relation.permissionsForSingleUseCommands[tempCommandName];
  103. // case: broadcastUse
  104. if (permissionForInteractions == null) {
  105. permissionForInteractions = relation.permissionsForBroadcastUseCommands[tempCommandName];
  106. }
  107. if (permissionForInteractions === true) {
  108. isPermittedForInteractions = true;
  109. return;
  110. }
  111. // check permission at channel level
  112. if (Array.isArray(permissionForInteractions)) {
  113. isPermittedForInteractions = true;
  114. return;
  115. }
  116. });
  117. return { isPermittedForInteractions, commandName };
  118. }
  119. }