import type { ApiMessage } from '../../../api/types'; import type { IAlbum, IDocumentGroup } from '../../../types'; import { isActionMessage } from '../../../global/helpers'; import { getDayStartAt } from '../../../util/dates/oldDateFormat'; type SenderGroup = (ApiMessage | IAlbum | IDocumentGroup)[]; const GROUP_INTERVAL_SECONDS = 600; // 10 minutes export type MessageDateGroup = { originalDate: number; datetime: number; senderGroups: SenderGroup[]; }; export function isAlbum(messageOrAlbum: ApiMessage | IAlbum | IDocumentGroup): messageOrAlbum is IAlbum { return 'albumId' in messageOrAlbum; } export function isDocumentGroup( messageOrAlbum: ApiMessage | IAlbum | IDocumentGroup, ): messageOrAlbum is IDocumentGroup { return 'documentGroupId' in messageOrAlbum; } export function groupMessages( messages: ApiMessage[], firstUnreadId?: number, topMessageId?: number, isChatWithSelf?: boolean, withUsers?: boolean, ) { const initDateGroup: MessageDateGroup = { originalDate: messages[0].date, datetime: getDayStartAt(messages[0].date * 1000), senderGroups: [[]], }; let currentAlbum: IAlbum | undefined; let currentDocumentGroup: IDocumentGroup | undefined; const dateGroups: MessageDateGroup[] = [initDateGroup]; messages.forEach((message, index) => { const currentDateGroup = dateGroups[dateGroups.length - 1]; const currentSenderGroup = currentDateGroup.senderGroups[currentDateGroup.senderGroups.length - 1]; if (message.isInAlbum) { if (!currentAlbum) { currentAlbum = { albumId: message.groupedId!, messages: [message], mainMessage: message, hasMultipleCaptions: false, commentsMessage: message.hasComments ? message : undefined, captionMessage: message.content.text ? message : undefined, } satisfies IAlbum; } else { currentAlbum.messages.push(message); if (message.hasComments) { currentAlbum.commentsMessage = message; } if (message.content.text && !currentAlbum.hasMultipleCaptions) { if (currentAlbum.captionMessage) { currentAlbum.hasMultipleCaptions = true; currentAlbum.captionMessage = undefined; } else { currentAlbum.captionMessage = message; } } } } else if ((message.content.paidMedia?.extendedMedia.length || 0) > 1) { currentSenderGroup.push({ albumId: `paid-${message.id}`, messages: [message], mainMessage: message, hasMultipleCaptions: false, isPaidMedia: true, } satisfies IAlbum); } else if (message.groupedId) { if (!currentDocumentGroup) { currentDocumentGroup = { documentGroupId: message.groupedId, messages: [message], firstMessageId: message.id, commentsMessage: message.hasComments ? message : undefined, } satisfies IDocumentGroup; } else { currentDocumentGroup.messages.push(message); if (message.hasComments) { currentDocumentGroup.commentsMessage = message; } } } else { currentSenderGroup.push(message); } const nextMessage = messages[index + 1]; if ( currentAlbum && (!nextMessage || !nextMessage.groupedId || nextMessage.groupedId !== currentAlbum.albumId) ) { currentSenderGroup.push(currentAlbum); currentAlbum = undefined; } if ( currentDocumentGroup && (!nextMessage || !nextMessage.groupedId || nextMessage.groupedId !== currentDocumentGroup.documentGroupId) ) { currentSenderGroup.push(currentDocumentGroup); currentDocumentGroup = undefined; } const lastMessageInSenderGroup = currentSenderGroup[currentSenderGroup.length - 1]; if (nextMessage && !currentAlbum && !currentDocumentGroup) { const nextMessageDayStartsAt = getDayStartAt(nextMessage.date * 1000); if (currentDateGroup.datetime !== nextMessageDayStartsAt) { const newDateGroup: MessageDateGroup = { originalDate: nextMessage.date, datetime: nextMessageDayStartsAt, senderGroups: [[]], }; dateGroups.push(newDateGroup); } else if ( nextMessage.id === firstUnreadId || message.senderId !== nextMessage.senderId || message.guestChatViaId !== nextMessage.guestChatViaId || (!withUsers && message.paidMessageStars) || (nextMessage.suggestedPostInfo) || message.isOutgoing !== nextMessage.isOutgoing || message.postAuthorTitle !== nextMessage.postAuthorTitle || (isActionMessage(message) && message.content.action?.type !== 'phoneCall') || (isActionMessage(nextMessage) && nextMessage.content.action?.type !== 'phoneCall') || message.inlineButtons || nextMessage.inlineButtons || (nextMessage.date - message.date) > GROUP_INTERVAL_SECONDS || (topMessageId && (message.id === topMessageId || (lastMessageInSenderGroup && ( (isAlbum(lastMessageInSenderGroup) && lastMessageInSenderGroup.mainMessage.id === topMessageId) || (isDocumentGroup(lastMessageInSenderGroup) && lastMessageInSenderGroup.firstMessageId === topMessageId) ))) && nextMessage.id !== topMessageId && !(message.groupedId && message.groupedId === nextMessage.groupedId)) || (isChatWithSelf && message.forwardInfo?.fromId !== nextMessage.forwardInfo?.fromId) ) { currentDateGroup.senderGroups.push([]); } } }); return dateGroups; }