diff --git a/keyserver/src/responders/message-responders.js b/keyserver/src/responders/message-responders.js --- a/keyserver/src/responders/message-responders.js +++ b/keyserver/src/responders/message-responders.js @@ -4,15 +4,18 @@ import t from 'tcomb'; import { createMediaMessageData, trimMessage } from 'lib/shared/message-utils'; +import { relationshipBlockedInEitherDirection } from 'lib/shared/relationship-utils'; import type { Media } from 'lib/types/media-types.js'; import { messageTypes, type SendTextMessageRequest, type SendMultimediaMessageRequest, + type SendReactionMessageRequest, type FetchMessageInfosResponse, type FetchMessageInfosRequest, defaultNumberPerThread, type SendMessageResponse, + type ReactionMessageData, } from 'lib/types/message-types'; import type { TextMessageData } from 'lib/types/messages/text'; import { threadPermissions } from 'lib/types/thread-types'; @@ -23,12 +26,14 @@ import { fetchMessageInfos, fetchMessageInfoForLocalID, + fetchMessageInfoByID, } from '../fetchers/message-fetchers'; import { checkThreadPermission } from '../fetchers/thread-permission-fetchers'; import { fetchMedia, fetchMediaFromMediaMessageContent, } from '../fetchers/upload-fetchers'; +import { fetchKnownUserInfos } from '../fetchers/user-fetchers'; import type { Viewer } from '../session/viewer'; import { assignMedia, @@ -179,8 +184,69 @@ return { newMessageInfo }; } +const sendReactionMessageRequestInputValidator = tShape({ + threadID: t.String, + targetMessageID: t.String, + reaction: t.String, + action: t.enums.of(['add_reaction', 'remove_reaction']), +}); +async function reactionMessageCreationResponder( + viewer: Viewer, + input: any, +): Promise { + const request: SendReactionMessageRequest = input; + await validateInput(viewer, sendReactionMessageRequestInputValidator, input); + + const { threadID, targetMessageID, reaction, action } = request; + + if (!targetMessageID || !reaction || !action) { + throw new ServerError('invalid_parameters'); + } + + const targetMessageInfo = await fetchMessageInfoByID(viewer, targetMessageID); + + if (!targetMessageInfo || !targetMessageInfo.id) { + throw new ServerError('invalid_parameters'); + } + + const targetMessageUserInfo = await fetchKnownUserInfos(viewer, [ + targetMessageInfo.creatorID, + ]); + const targetMessageCreatorRelationship = + targetMessageUserInfo[targetMessageInfo.creatorID]?.relationshipStatus; + + const creatorRelationshipHasBlock = + targetMessageCreatorRelationship && + relationshipBlockedInEitherDirection(targetMessageCreatorRelationship); + + const hasPermission = await checkThreadPermission( + viewer, + threadID, + threadPermissions.VOICED, + ); + + if (!hasPermission || creatorRelationshipHasBlock) { + throw new ServerError('invalid_parameters'); + } + + const messageData: ReactionMessageData = { + type: messageTypes.REACTION, + threadID, + creatorID: viewer.id, + time: Date.now(), + targetMessageID, + reaction, + action, + }; + + const rawMessageInfos = await createMessages(viewer, [messageData]); + + return { newMessageInfo: rawMessageInfos[0] }; +} + export { textMessageCreationResponder, messageFetchResponder, multimediaMessageCreationResponder, + reactionMessageCreationResponder, };