@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
TypeScript
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