| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import { type GrowiEventProcessor, REQUEST_TIMEOUT_FOR_PTOG } from '@growi/slack';
- import type { WebClient } from '@slack/web-api';
- import { Inject, Service } from '@tsed/di';
- import axios from 'axios';
- import { RelationRepository } from '~/repositories/relation';
- import loggerFactory from '~/utils/logger';
- const logger = loggerFactory('slackbot-proxy:services:LinkSharedService');
- type LinkSharedEventLink = {
- url: string,
- domain: string,
- }
- // aliases
- type GrowiOrigin = string;
- type TokenPtoG = string;
- export type LinkSharedRequestEvent = {
- channel: string,
- // eslint-disable-next-line camelcase
- message_ts: string,
- links: LinkSharedEventLink[],
- }
- type PrivateData = {
- isPublic: false,
- path: string,
- }
- type PublicData = {
- isPublic: true,
- path: string,
- pageBody: string,
- updatedAt: string,
- commentCount: number,
- }
- export type DataForLinkShared = PrivateData | PublicData;
- @Service()
- export class LinkSharedService implements GrowiEventProcessor {
- @Inject()
- relationRepository: RelationRepository;
- shouldHandleEvent(eventType: string): boolean {
- return eventType === 'link_shared';
- }
- async processEvent(client: WebClient, event: LinkSharedRequestEvent): Promise<void> {
- const { links } = event;
- const origins: string[] = links.map((link: LinkSharedEventLink) => (new URL(link.url)).origin);
- const originToTokenPtoGMap: Map<GrowiOrigin, TokenPtoG> = await this.generateOriginToTokenPtoGMapFromOrigins(origins); // get tokenPtoG at once
- // forward to GROWI
- const result = await this.forwardToEachGrowiOrigin(origins, event, originToTokenPtoGMap);
- // log error
- this.logErrorRejectedResults(result);
- }
- // generate Map<GrowiOrigin, TokenPtoG>
- async generateOriginToTokenPtoGMapFromOrigins(origins: GrowiOrigin[]): Promise<Map<GrowiOrigin, TokenPtoG>> {
- const originToTokenPtoGMap: Map<GrowiOrigin, TokenPtoG> = new Map();
- // get relations using origins at once
- const relations = await this.relationRepository.findAllByGrowiUris(origins);
- // increment map using relation.growiUri & relation.tokenPtoG
- relations.forEach((relation) => {
- originToTokenPtoGMap.set(relation.growiUri, relation.tokenPtoG);
- });
- return originToTokenPtoGMap;
- }
- async forwardToEachGrowiOrigin(
- origins: string[], event: LinkSharedRequestEvent, originToTokenPtoGMap: Map<GrowiOrigin, TokenPtoG>,
- ): Promise<PromiseSettledResult<void>[]> {
- return Promise.allSettled(origins.map(async(origin) => {
- const requestBody = {
- growiBotEvent: {
- eventType: 'link_shared',
- event,
- },
- data: {
- origin,
- },
- };
- try {
- // ensure tokenPtoG exists
- const tokenPtoG = originToTokenPtoGMap.get(origin);
- if (tokenPtoG == null) throw new Error('tokenPtoG is null');
- const url = new URL('/_api/v3/slack-integration/proxied/events', origin);
- await axios.post(url.toString(),
- requestBody,
- {
- headers: {
- 'x-growi-ptog-tokens': tokenPtoG,
- },
- timeout: REQUEST_TIMEOUT_FOR_PTOG,
- });
- }
- catch (err) {
- logger.error(`Error occurred while request to growi (origin=${origin}):`, err);
- throw err;
- }
- }));
- }
- // Promise util method to output rejected results
- private logErrorRejectedResults<T>(results: PromiseSettledResult<T>[]): void {
- const rejectedResults: PromiseRejectedResult[] = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected');
- rejectedResults.forEach((rejected, i) => {
- logger.error(`Error occurred (count: ${i}): `, rejected.reason.toString());
- });
- }
- }
|