UNPKG

@stack.thefennec.dev/telegram-export-parser

Version:

TypeScript library for parsing Telegram Desktop's data export with full type safety

567 lines 19.9 kB
import { MESSAGE_TYPES, MEDIA_TYPES } from './index'; import type { Actor, Reaction, ExportedFile } from './index'; import type { ContactInfo, InlineButton, InvoiceInfo, LocationInfo, Poll } from './shared'; import type { TextEntity } from './text-entities'; import type { TelegramEvent } from './events'; /** * Base interface for all Telegram user messages containing common properties. * * This is the foundation for all message types and contains metadata * that applies to every message regardless of content type. * * @example * ```typescript * const processMessage = (msg: BaseMessage) => { * console.log(`Message ${msg.id} from ${msg.sender.displayName} at ${msg.sentAt}`) * if (msg.edited) { * console.log(`Last edited: ${msg.editedAt}`) * } * } * ``` */ export interface BaseMessage { /** Unique identifier for this message within Telegram */ id: bigint; /** Always 'message' for user messages (vs 'service' for events) */ type: typeof MESSAGE_TYPES.MESSAGE; /** When the message was originally sent */ sentAt: Date; /** Who sent this message (user, bot, or channel) */ sender: Actor; /** Rich text formatting and special elements (mentions, links, etc.) */ textEntities: TextEntity[]; /** Original sender if this message was forwarded from another chat */ forwardedFrom: Actor | undefined; /** Original sender if this message was saved from another chat */ savedFrom: Actor | undefined; /** ID of the message this is replying to (if it's a reply) */ replyToMessageId: number | undefined; /** Peer ID of the message this is replying to (for cross-chat replies) */ replyToPeerId: number | undefined; /** Whether this message has been modified after sending */ edited: boolean; /** When the message was last edited (only if edited is true) */ editedAt: Date | undefined; /** User reactions (likes, hearts, etc.) added to this message */ reactions: Reaction[]; /** Inline keyboard buttons for bot interactions (if present) */ inlineBotButtons: InlineButton[][] | undefined; /** Bot that was used to send this message (if sent via bot) */ viaBot: Actor | undefined; } /** * Base interface for messages containing media files. * * Extends BaseMessage with file metadata that applies to all media types * including photos, videos, documents, audio files, etc. * * @example * ```typescript * const getFileInfo = (msg: BaseMediaMessage) => { * const size = msg.fileSize ? `${msg.fileSize} bytes` : 'unknown size' * return `${msg.fileName || 'unnamed file'} (${size})` * } * ``` */ export interface BaseMediaMessage extends BaseMessage { /** File reference or URL to access the media */ fileURL: ExportedFile; /** Original filename as uploaded (may be undefined for some media types) */ fileName: string | undefined; /** File size in bytes (may be undefined if not available) */ fileSize: number | undefined; /** MIME type of the file (e.g., 'image/jpeg', 'video/mp4') */ mimeType: string | undefined; } /** * Mixin interface for media that has visual dimensions. * * Applied to photos, videos, animations, stickers, and other * visual media that have measurable width and height. * * @example * ```typescript * const getAspectRatio = (media: MediaWithDimensions): number | undefined => { * if (media.width && media.height) { * return media.width / media.height * } * return undefined * } * ``` */ export interface MediaWithDimensions { /** Height of the media in pixels (undefined if not available) */ height: number | undefined; /** Width of the media in pixels (undefined if not available) */ width: number | undefined; } /** * Mixin interface for media that has a preview thumbnail. * * Thumbnails are small preview images used for efficient loading * and display before the full media is accessed. * * @example * ```typescript * const showThumbnail = (media: MediaWithThumbnail) => { * return `<img src="${media.thumbnailURL}" alt="Thumbnail" />` * } * ``` */ export interface MediaWithThumbnail { /** File reference or URL to the thumbnail image */ thumbnailURL: ExportedFile; /** Size of the thumbnail file in bytes (if available) */ thumbnailFileSize: number | undefined; } /** * Mixin interface for time-based media (audio/video). * * Applied to any media that plays over time and has a measurable duration. * This includes voice messages, music, videos, and animations. * * @example * ```typescript * const formatDuration = (media: MediaWithDuration): string => { * const minutes = Math.floor(media.durationSeconds / 60) * const seconds = media.durationSeconds % 60 * return `${minutes}:${seconds.toString().padStart(2, '0')}` * } * ``` */ export interface MediaWithDuration { /** Duration of the media playback in seconds */ durationSeconds: number; } /** * Mixin interface for messages that disappear after viewing. * * Used for photos and documents that are set to self-destruct * after being viewed, providing ephemeral messaging functionality. * * @example * ```typescript * const checkSelfDestruct = (msg: SelfDestructibleMessage): boolean => { * return msg.selfDestructPeriodSeconds !== undefined * } * ``` */ export interface SelfDestructibleMessage { /** Seconds until message self-destructs after viewing (undefined if permanent) */ selfDestructPeriodSeconds: number | undefined; } /** * Text-only message type - just uses BaseMessage without any additional properties. * Contains text content through the textEntities property. * * @type TextMessage */ export type TextMessage = BaseMessage; /** * Message containing a photo/image. * * @interface PhotoMessage * @extends BaseMessage * @extends MediaWithDimensions * @extends SelfDestructibleMessage */ export interface PhotoMessage extends BaseMessage, MediaWithDimensions, SelfDestructibleMessage { /** URL or file reference to the photo */ photoURL: ExportedFile; /** Size of the photo file in bytes */ photoFileSize: number; /** Whether the photo is marked as a spoiler */ mediaSpoiler: boolean; } /** * Message containing location information. * * @interface LocationMessage * @extends BaseMessage */ export interface LocationMessage extends BaseMessage { /** Latitude and longitude coordinates */ locationInformation: LocationInfo; /** Human-readable address (if available) */ address: string | undefined; /** Name of the place/venue (if available) */ placeName: string | undefined; /** Duration in seconds for live location sharing (if applicable) */ liveLocationPeriodSeconds: number | undefined; } /** * Message containing contact information. * * @interface ContactMessage * @extends BaseMessage */ export interface ContactMessage extends BaseMessage { /** Contact's basic information (name, phone) */ contactInformation: ContactInfo; /** URL or file reference to the contact's vCard file */ contactVcardURL: ExportedFile; /** Size of the vCard file in bytes (if available) */ contactVcardFileSize: number | undefined; } /** * Message containing a poll/survey. * * @interface PollMessage * @extends BaseMessage */ export interface PollMessage extends BaseMessage { /** Poll data including question, answers, and results */ poll: Poll; } /** * Message related to a game. * * @interface GameMessage * @extends BaseMessage */ export interface GameMessage extends BaseMessage { /** Title of the game */ gameTitle: string; /** Description of the game */ gameDescription: string; /** Link to the game */ gameLink: string; } /** * Message containing an invoice for payment. * * @interface InvoiceMessage * @extends BaseMessage */ export interface InvoiceMessage extends BaseMessage { /** Invoice details including amount, currency, description */ invoiceInformation: InvoiceInfo; } /** * Message containing a sticker image. * * Stickers are predefined images or animations from sticker packs, * often associated with specific emojis for quick emotional expression. * * @example * ```typescript * const getStickerInfo = (msg: StickerMessage): string => { * return `${msg.emoji} sticker (${msg.durationSeconds}s)` * } * ``` */ export interface StickerMessage extends BaseMediaMessage, MediaWithDimensions, MediaWithDuration, MediaWithThumbnail { /** Always 'sticker' for sticker messages */ mediaType: typeof MEDIA_TYPES.STICKER; /** Emoji that represents this sticker's meaning or emotion */ emoji: string; } /** * Message containing an animated GIF or short video loop. * * Animations are typically short, looping visual content used for * reactions, memes, or brief animated expressions. * * @example * ```typescript * const isShortAnimation = (msg: AnimationMessage): boolean => { * return msg.durationSeconds <= 5 * } * ``` */ export interface AnimationMessage extends BaseMediaMessage, MediaWithDimensions, MediaWithDuration, MediaWithThumbnail { /** Always 'animation' for animated GIF/video messages */ mediaType: typeof MEDIA_TYPES.ANIMATION; } /** * Message containing a music or audio file. * * Distinguished from voice messages by having metadata like * artist/performer and title, representing produced music content. * * @example * ```typescript * const getTrackTitle = (msg: MusicMessage): string => { * if (msg.title && msg.performer) { * return `${msg.performer} - ${msg.title}` * } * return msg.fileName || 'Unknown Track' * } * ``` */ export interface MusicMessage extends BaseMediaMessage, MediaWithDuration, MediaWithThumbnail { /** Always 'audio_file' for music messages */ mediaType: typeof MEDIA_TYPES.AUDIO_FILE; /** Artist or band name (if available in metadata) */ performer: string | undefined; /** Song or track title (if available in metadata) */ title: string | undefined; } /** * Message containing a regular video file. * * Standard video content with full playback controls, as opposed * to video notes which are circular and more ephemeral. * * @example * ```typescript * const getVideoInfo = (msg: VideoMessage): string => { * const resolution = msg.width && msg.height ? `${msg.width}x${msg.height}` : 'unknown resolution' * const duration = `${Math.round(msg.durationSeconds)}s` * return `Video: ${resolution}, ${duration}` * } * ``` */ export interface VideoMessage extends BaseMediaMessage, MediaWithDimensions, MediaWithDuration, MediaWithThumbnail { /** Always 'video_file' for regular video messages */ mediaType: typeof MEDIA_TYPES.VIDEO_FILE; } /** * Message containing a video note (circular video message). * * Video notes are round, typically short video messages designed for * quick, casual communication similar to voice messages but with video. * * @example * ```typescript * const isQuickVideoNote = (msg: VideoNoteMessage): boolean => { * return msg.durationSeconds <= 60 // Most video notes are under 1 minute * } * ``` */ export interface VideoNoteMessage extends BaseMediaMessage, MediaWithDimensions, MediaWithDuration, MediaWithThumbnail { /** Always 'video_note' for circular video messages */ mediaType: typeof MEDIA_TYPES.VIDEO_NOTE; } /** * Message containing a voice note (audio recording). * * Voice messages are typically casual audio recordings made directly * through the Telegram interface for quick communication. * * @example * ```typescript * const formatVoiceLength = (msg: VoiceNoteMessage): string => { * const minutes = Math.floor(msg.durationSeconds / 60) * const seconds = msg.durationSeconds % 60 * return `Voice note: ${minutes}:${seconds.toString().padStart(2, '0')}` * } * ``` */ export interface VoiceNoteMessage extends BaseMediaMessage, MediaWithDuration { /** Always 'voice_note' for voice message recordings */ mediaType: typeof MEDIA_TYPES.VOICE_NOTE; } /** * Message containing a document or file attachment. * * Generic file type for any document, archive, or file that doesn't * fit into other specific media categories. May include self-destruct capability. * * @example * ```typescript * const getFileDescription = (msg: DocumentMessage): string => { * const name = msg.fileName || 'Unknown file' * const size = msg.fileSize ? ` (${Math.round(msg.fileSize / 1024)}KB)` : '' * return `📎 ${name}${size}` * } * ``` */ export interface DocumentMessage extends BaseMediaMessage, MediaWithDimensions, MediaWithThumbnail, SelfDestructibleMessage { /** Always 'file' for document/file attachments */ mediaType: typeof MEDIA_TYPES.FILE; } /** * Union type for all message types that contain media files. * * This includes any message with downloadable content like videos, * audio, documents, stickers, etc., but excludes photos which are * handled separately due to their different structure. * * @example * ```typescript * const processMediaMessage = (msg: MediaMessage) => { * console.log(`Media: ${msg.mediaType}, Size: ${msg.fileSize} bytes`) * if ('durationSeconds' in msg) { * console.log(`Duration: ${msg.durationSeconds}s`) * } * } * ``` */ export type MediaMessage = AnimationMessage | MusicMessage | StickerMessage | VideoMessage | VideoNoteMessage | VoiceNoteMessage | DocumentMessage; /** * Union type representing all possible Telegram user message types. * * This is the main message type used throughout the system, covering * everything from simple text to complex media and interactive content. * Use with type guards to narrow to specific message types. * * @example * ```typescript * const processMessage = (msg: TelegramMessage) => { * if (isTextMessage(msg)) { * // Handle text message * console.log('Text:', msg.textEntities) * } else if (isMediaMessage(msg)) { * // Handle media message * console.log('Media:', msg.mediaType) * } * } * ``` */ export type TelegramMessage = TextMessage | MediaMessage | PhotoMessage | LocationMessage | ContactMessage | PollMessage | GameMessage | InvoiceMessage; /** * Type guard to check if a message or event has a reply reference. * * @param msg - The message or event to check * @returns True if the item is replying to another message * * @example * ```typescript * if (hasReply(msg)) { * console.log(`Replying to message ${msg.replyToMessageId}`) * } * ``` */ export declare function hasReply(msg: TelegramMessage | TelegramEvent): boolean; /** * Type guard to check if a message or event has user reactions. * * @param msg - The message or event to check * @returns True if the item has at least one reaction */ export declare function hasReactions(msg: TelegramMessage | TelegramEvent): boolean; /** * Type guard to check if a message or event was forwarded from another chat. * * @param msg - The message or event to check * @returns True if the item was forwarded */ export declare function isForwarded(msg: TelegramMessage | TelegramEvent): boolean; /** * Type guard to check if a message or event was saved from another chat. * * @param msg - The message or event to check * @returns True if the item was saved */ export declare function isSavedMessage(msg: TelegramMessage | TelegramEvent): boolean; /** * Type guard to check if a message or event has been edited after sending. * * @param msg - The message or event to check * @returns True if the item was edited */ export declare function isEdited(msg: TelegramMessage | TelegramEvent): boolean; /** * Type guard to check if a message or event was sent via a bot. * * @param msg - The message or event to check * @returns True if the item was sent through a bot */ export declare function isViaBot(msg: TelegramMessage | TelegramEvent): boolean; /** * Type guard to check if an item is a simple text message. * * @param msg - The message or event to check * @returns True if the item is a TextMessage */ export declare function isTextMessage(msg: TelegramMessage | TelegramEvent): msg is TextMessage; /** * Type guard to check if an item is a photo message. * * @param msg - The message or event to check * @returns True if the item is a PhotoMessage */ export declare function isPhotoMessage(msg: TelegramMessage | TelegramEvent): msg is PhotoMessage; /** * Type guard to check if an item is a location message. * * @param msg - The message or event to check * @returns True if the item is a LocationMessage */ export declare function isLocationMessage(msg: TelegramMessage | TelegramEvent): msg is LocationMessage; /** * Type guard to check if an item is a contact message. * * @param msg - The message or event to check * @returns True if the item is a ContactMessage */ export declare function isContactMessage(msg: TelegramMessage | TelegramEvent): msg is ContactMessage; /** * Type guard to check if an item is a poll message. * * @param msg - The message or event to check * @returns True if the item is a PollMessage */ export declare function isPollMessage(msg: TelegramMessage | TelegramEvent): msg is PollMessage; /** * Type guard to check if an item is a game message. * * @param msg - The message or event to check * @returns True if the item is a GameMessage */ export declare function isGameMessage(msg: TelegramMessage | TelegramEvent): msg is GameMessage; /** * Type guard to check if an item is an invoice message. * * @param msg - The message or event to check * @returns True if the item is an InvoiceMessage */ export declare function isInvoiceMessage(msg: TelegramMessage | TelegramEvent): msg is InvoiceMessage; /** * Type guard to check if an item is any type of media message. * * @param msg - The message or event to check * @returns True if the item is a MediaMessage */ export declare function isMediaMessage(msg: TelegramMessage | TelegramEvent): msg is MediaMessage; /** * Type guard to check if an item is an animation message. * * @param msg - The message or event to check * @returns True if the item is an AnimationMessage */ export declare function isAnimationMessage(msg: TelegramMessage | TelegramEvent): msg is AnimationMessage; /** * Type guard to check if an item is a music/audio file message. * * @param msg - The message or event to check * @returns True if the item is a MusicMessage */ export declare function isAudioFileMessage(msg: TelegramMessage | TelegramEvent): msg is MusicMessage; /** * Type guard to check if an item is a sticker message. * * @param msg - The message or event to check * @returns True if the item is a StickerMessage */ export declare function isStickerMessage(msg: TelegramMessage | TelegramEvent): msg is StickerMessage; /** * Type guard to check if an item is a video file message. * * @param msg - The message or event to check * @returns True if the item is a VideoMessage */ export declare function isVideoFileMessage(msg: TelegramMessage | TelegramEvent): msg is VideoMessage; /** * Type guard to check if an item is a video note message. * * @param msg - The message or event to check * @returns True if the item is a VideoNoteMessage */ export declare function isVideoNoteMessage(msg: TelegramMessage | TelegramEvent): msg is VideoNoteMessage; /** * Type guard to check if an item is a voice message. * * @param msg - The message or event to check * @returns True if the item is a VoiceNoteMessage */ export declare function isVoiceMessage(msg: TelegramMessage | TelegramEvent): msg is VoiceNoteMessage; /** * Type guard to check if an item is a document message. * * @param msg - The message or event to check * @returns True if the item is a DocumentMessage */ export declare function isDocumentMessage(msg: TelegramMessage | TelegramEvent): msg is DocumentMessage; //# sourceMappingURL=messages.d.ts.map