TelegramPWA/src/global/helpers/messageSummary.ts
2025-03-01 17:59:16 +01:00

261 lines
6.6 KiB
TypeScript

import type { TeactNode } from '../../lib/teact/teact';
import type {
ApiMediaExtendedPreview, ApiMessage, MediaContent, StatefulMediaContent,
} from '../../api/types';
import type { OldLangFn } from '../../hooks/useOldLang';
import { ApiMessageEntityTypes } from '../../api/types';
import { CONTENT_NOT_SUPPORTED } from '../../config';
import trimText from '../../util/trimText';
import { renderTextWithEntities } from '../../components/common/helpers/renderTextWithEntities';
import {
getMessageText, getMessageTranscription,
} from './messages';
const SPOILER_CHARS = ['⠺', '⠵', '⠞', '⠟'];
export const TRUNCATED_SUMMARY_LENGTH = 80;
export function getMessageSummaryText(
lang: OldLangFn,
message: ApiMessage,
statefulContent: StatefulMediaContent | undefined,
noEmoji = false,
truncateLength = TRUNCATED_SUMMARY_LENGTH,
isExtended = false,
) {
const emoji = !noEmoji && getMessageSummaryEmoji(message);
const emojiWithSpace = emoji ? `${emoji} ` : '';
const text = trimText(getMessageTextWithSpoilers(message, statefulContent), truncateLength);
const description = getMessageSummaryDescription(lang, message, statefulContent, text, isExtended);
return `${emojiWithSpace}${description}`;
}
export function getMessageTextWithSpoilers(message: ApiMessage, statefulContent: StatefulMediaContent | undefined) {
const transcription = getMessageTranscription(message);
const textWithoutTranscription = getMessageText(statefulContent?.story || message)?.text;
if (!textWithoutTranscription) {
return transcription;
}
const { entities } = message.content.text || {};
if (!entities?.length) {
return transcription ? `${transcription}\n${textWithoutTranscription}` : textWithoutTranscription;
}
const text = entities.reduce((accText, {
type,
offset,
length,
}) => {
if (type !== ApiMessageEntityTypes.Spoiler) {
return accText;
}
const spoiler = generateBrailleSpoiler(length);
return `${accText.substr(0, offset)}${spoiler}${accText.substr(offset + length, accText.length)}`;
}, textWithoutTranscription);
return transcription ? `${transcription}\n${text}` : text;
}
export function getMessageSummaryEmoji(message: ApiMessage) {
const {
photo,
video,
audio,
voice,
document,
sticker,
pollId,
paidMedia,
} = message.content;
if (message.groupedId || photo || paidMedia) {
return '🖼';
}
if (video) {
return '📹';
}
if (sticker) {
return sticker.emoji;
}
if (audio) {
return '🎧';
}
if (voice) {
return '🎤';
}
if (document) {
return '📎';
}
if (pollId) {
return '📊';
}
return undefined;
}
export function getMediaContentTypeDescription(
lang: OldLangFn, content: MediaContent, statefulContent: StatefulMediaContent | undefined,
) {
return getSummaryDescription(lang, content, statefulContent);
}
export function getMessageSummaryDescription(
lang: OldLangFn,
message: ApiMessage,
statefulContent: StatefulMediaContent | undefined,
truncatedText?: string | TeactNode,
isExtended = false,
) {
return getSummaryDescription(lang, message.content, statefulContent, message, truncatedText, isExtended);
}
function getSummaryDescription(
lang: OldLangFn,
mediaContent: MediaContent,
statefulContent: StatefulMediaContent | undefined,
message?: ApiMessage,
truncatedText?: string | TeactNode,
isExtended = false,
) {
const {
text,
photo,
video,
audio,
voice,
document,
sticker,
contact,
invoice,
location,
game,
storyData,
giveaway,
giveawayResults,
paidMedia,
} = mediaContent;
const { poll } = statefulContent || {};
let hasUsedTruncatedText = false;
let summary: TeactNode | undefined;
const boughtExtendedMedia = paidMedia?.isBought && paidMedia.extendedMedia;
const previewExtendedMedia = paidMedia && !paidMedia.isBought
? paidMedia.extendedMedia as ApiMediaExtendedPreview[] : undefined;
const isPaidMediaAlbum = paidMedia && paidMedia.extendedMedia.length > 1;
const isPaidMediaSingleVideo = !isPaidMediaAlbum
&& (boughtExtendedMedia?.[0].video || previewExtendedMedia?.[0].duration);
const isPaidMediaSinglePhoto = !isPaidMediaAlbum && !isPaidMediaSingleVideo;
if (message?.groupedId || isPaidMediaAlbum) {
hasUsedTruncatedText = true;
summary = truncatedText || lang('lng_in_dlg_album');
}
if (photo || isPaidMediaSinglePhoto) {
hasUsedTruncatedText = true;
summary = truncatedText || lang('AttachPhoto');
}
if (video || isPaidMediaSingleVideo) {
hasUsedTruncatedText = true;
summary = truncatedText || lang(video?.isGif ? 'AttachGif' : 'AttachVideo');
}
if (sticker) {
summary = lang('AttachSticker').trim();
}
if (audio) {
summary = getMessageAudioCaption(mediaContent) || lang('AttachMusic');
}
if (voice) {
hasUsedTruncatedText = true;
summary = truncatedText || lang('AttachAudio');
}
if (document) {
hasUsedTruncatedText = !isExtended;
summary = isExtended ? document.fileName : (truncatedText || document.fileName);
}
if (contact) {
summary = lang('AttachContact');
}
if (poll) {
summary = renderTextWithEntities({
text: poll.summary.question.text,
entities: poll.summary.question.entities,
asPreview: true,
});
}
if (invoice) {
summary = invoice.extendedMedia ? invoice.title : `${lang('PaymentInvoice')}: ${invoice.description}`;
}
if (text) {
if (isExtended && summary && !hasUsedTruncatedText) {
summary += `\n${truncatedText}`;
} else {
summary = truncatedText;
}
}
if (location?.mediaType === 'geo' || location?.mediaType === 'venue') {
summary = lang('Message.Location');
}
if (location?.mediaType === 'geoLive') {
summary = lang('Message.LiveLocation');
}
if (game) {
summary = `🎮 ${game.title}`;
}
if (giveaway) {
summary = lang('BoostingGiveawayChannelStarted');
}
if (giveawayResults) {
summary = lang('Message.GiveawayEndedWinners', giveawayResults.winnersCount);
}
if (storyData) {
summary = truncatedText || (message ? lang('ForwardedStory') : lang('Chat.ReplyStory'));
}
return summary || CONTENT_NOT_SUPPORTED;
}
export function generateBrailleSpoiler(length: number) {
return new Array(length)
.fill(undefined)
.map(() => SPOILER_CHARS[Math.floor(Math.random() * SPOILER_CHARS.length)])
.join('');
}
function getMessageAudioCaption(mediaContent: MediaContent) {
const {
audio,
text,
} = mediaContent;
return (audio && [audio.title, audio.performer].filter(Boolean)
.join(' — ')) || (text?.text);
}