fix: sogs reactions work again and added rate limiting

pull/2445/head
William Grant 3 years ago
parent 4889cb5b32
commit d77c9fa824

@ -8,6 +8,7 @@ import {
import { addJsonContentTypeToHeaders } from './sogsV3SendMessage';
import { AbortSignal } from 'abort-controller';
import { roomHasBlindEnabled } from './sogsV3Capabilities';
import { SOGSReactorsFetchCount } from '../../../../util/reactions';
type BatchFetchRequestOptions = {
method: 'POST' | 'PUT' | 'GET' | 'DELETE';
@ -238,10 +239,9 @@ const makeBatchRequestPayload = (
if (options.messages) {
return {
method: 'GET',
// TODO Consistency across platforms with fetching reactors
path: isNumber(options.messages.sinceSeqNo)
? `/room/${options.messages.roomId}/messages/since/${options.messages.sinceSeqNo}?t=r`
: `/room/${options.messages.roomId}/messages/recent`,
? `/room/${options.messages.roomId}/messages/since/${options.messages.sinceSeqNo}?t=r&reactors=${SOGSReactorsFetchCount}`
: `/room/${options.messages.roomId}/messages/recent?reactors=${SOGSReactorsFetchCount}`,
};
}
break;

@ -2,6 +2,7 @@ import { AbortSignal } from 'abort-controller';
import { Data } from '../../../../data/data';
import { Action, OpenGroupReactionResponse, Reaction } from '../../../../types/Reaction';
import { getEmojiDataFromNative } from '../../../../util/emoji';
import { hitRateLimit } from '../../../../util/reactions';
import { OnionSending } from '../../../onions/onionSend';
import { OpenGroupPollingUtils } from '../opengroupV2/OpenGroupPollingUtils';
import { batchGlobalIsSuccess, parseBatchGlobalStatusCode } from './sogsV3BatchPoll';
@ -45,7 +46,12 @@ export const sendSogsReactionOnionV4 = async (
return false;
}
// for an invalid reaction we use https://emojipedia.org/frame-with-an-x/ as a replacement since it cannot rendered as an emoji
if (hitRateLimit()) {
return false;
}
// The SOGS endpoint supports any text input so we need to make sure we are sending a valid unicode emoji
// for an invalid input we use https://emojipedia.org/frame-with-an-x/ as a replacement since it cannot rendered as an emoji but is valid unicode
const emoji = getEmojiDataFromNative(reaction.emoji) ? reaction.emoji : '🖾';
const endpoint = `/room/${room}/reaction/${reaction.id}/${emoji}`;
const method = reaction.action === Action.REACT ? 'PUT' : 'DELETE';

@ -23,6 +23,8 @@ import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/Ope
import { UnsendMessage } from '../messages/outgoing/controlMessage/UnsendMessage';
import { CallMessage } from '../messages/outgoing/controlMessage/CallMessage';
import { OpenGroupMessageV2 } from '../apis/open_group_api/opengroupV2/OpenGroupMessageV2';
import { AbortController } from 'abort-controller';
import { sendSogsReactionOnionV4 } from '../apis/open_group_api/sogsv3/sogsV3SendReaction';
type ClosedGroupMessageType =
| ClosedGroupVisibleMessage
@ -75,6 +77,18 @@ export class MessageQueue {
// Skipping the queue for Open Groups v2; the message is sent directly
try {
// NOTE Reactions are handled separately
if (message.reaction) {
await sendSogsReactionOnionV4(
roomInfos.serverUrl,
roomInfos.roomId,
new AbortController().signal,
message.reaction,
blinded
);
return;
}
const result = await MessageSender.sendToOpenGroupV2(
message,
roomInfos,
@ -82,11 +96,6 @@ export class MessageQueue {
filesToLink
);
// NOTE Reactions are handled in the MessageSender
if (message.reaction) {
return;
}
const { sentTimestamp, serverId } = result as OpenGroupMessageV2;
if (!serverId || serverId === -1) {
throw new Error(`Invalid serverId returned by server: ${serverId}`);

@ -26,7 +26,6 @@ import {
sendSogsMessageOnionV4,
} from '../apis/open_group_api/sogsv3/sogsV3SendMessage';
import { AbortController } from 'abort-controller';
import { sendSogsReactionOnionV4 } from '../apis/open_group_api/sogsv3/sogsV3SendReaction';
const DEFAULT_CONNECTIONS = 1;
@ -288,25 +287,14 @@ export async function sendToOpenGroupV2(
filesToLink,
});
if (rawMessage.reaction) {
const msg = await sendSogsReactionOnionV4(
roomInfos.serverUrl,
roomInfos.roomId,
new AbortController().signal,
rawMessage.reaction,
blinded
);
return msg;
} else {
const msg = await sendSogsMessageOnionV4(
roomInfos.serverUrl,
roomInfos.roomId,
new AbortController().signal,
v2Message,
blinded
);
return msg;
}
const msg = await sendSogsMessageOnionV4(
roomInfos.serverUrl,
roomInfos.roomId,
new AbortController().signal,
v2Message,
blinded
);
return msg;
}
/**

@ -11,10 +11,28 @@ import { UserUtils } from '../session/utils';
import { Action, OpenGroupReactionList, ReactionList, RecentReactions } from '../types/Reaction';
import { getRecentReactions, saveRecentReations } from '../util/storage';
export const SOGSReactorsFetchCount = 5;
const rateCountLimit = 20;
const rateTimeLimit = 60 * 1000;
const latestReactionTimestamps: Array<number> = [];
export function hitRateLimit(): boolean {
const timestamp = Date.now();
latestReactionTimestamps.push(timestamp);
if (latestReactionTimestamps.length > rateCountLimit) {
const firstTimestamp = latestReactionTimestamps[0];
if (timestamp - firstTimestamp < rateTimeLimit) {
latestReactionTimestamps.pop();
window.log.warn('Only 20 reactions are allowed per minute');
return true;
} else {
latestReactionTimestamps.shift();
}
}
return false;
}
/**
* Retrieves the original message of a reaction
*/
@ -46,7 +64,7 @@ const getMessageByReaction = async (
};
/**
* Sends a Reaction Data Message, don't use for OpenGroups
* Sends a Reaction Data Message
*/
export const sendMessageReaction = async (messageId: string, emoji: string) => {
const found = await Data.getMessageById(messageId);
@ -62,27 +80,23 @@ export const sendMessageReaction = async (messageId: string, emoji: string) => {
return;
}
// TODO need to add rate limiting to SOGS function
const timestamp = Date.now();
latestReactionTimestamps.push(timestamp);
if (hitRateLimit()) {
return;
}
if (latestReactionTimestamps.length > rateCountLimit) {
const firstTimestamp = latestReactionTimestamps[0];
if (timestamp - firstTimestamp < rateTimeLimit) {
latestReactionTimestamps.pop();
return;
let me = UserUtils.getOurPubKeyStrFromCache();
let id = Number(found.get('sent_at'));
if (found.get('isPublic')) {
if (found.get('serverId')) {
id = found.get('serverId') || id;
me = getUsBlindedInThatServer(conversationModel) || me;
} else {
latestReactionTimestamps.shift();
window.log.warn(`Server Id was not found in message ${messageId} for opengroup reaction`);
return;
}
}
if (found?.get('isPublic')) {
window.log.warn("sendMessageReaction() shouldn't be used in opengroups");
return;
}
const id = Number(found.get('sent_at'));
const me = UserUtils.getOurPubKeyStrFromCache();
const author = found.get('source');
let action: Action = Action.REACT;
@ -231,6 +245,11 @@ export const handleOpenGroupMessageReactions = async (
reactor => !isUsAnySogsFromCache(reactor)
);
// If you aren't included in the reactors then remove the extra reactor to match with the SOGSReactorsFetchCount.
if (reactorsWithoutMe.length === SOGSReactorsFetchCount) {
reactorsWithoutMe.pop();
}
const conversationModel = originalMessage?.getConversation();
if (conversationModel) {
const me =

Loading…
Cancel
Save