import type { ThreadItem } from './account.ts' import type { JsonObject } from './commons.ts' import type { TicketStatus } from './desk.ts' import type { Envelope } from './envelope.ts' import type { Identity } from './node.ts' export type PlainText = string export type Ticket = { id: string sequentialId: number ownerIdentity: Identity customerIdentity: Identity customerDomain: string provider: string status: TicketStatus statusDate?: string storageDate: string openDate?: string closeDate?: string firstResponseDate?: string averageAgentResponseTime?: number agentIdentity?: Identity team?: string rating: number unreadMessages: number customerInput: Type extends MessageTypes ? Document : undefined closed: boolean closedBy?: Identity tags: Array } export type Redirect = { address: Identity context: Document } export type MediaLink = { // Mime type of the content type: string uri: string title?: string text?: string } export type Reply = { replied: Document inReplyTo: Document & { id: string; direction: 'sent' | 'received' } } export type Select = { text: string scope?: 'immediate' | 'persistent' options: Array<{ text: string }> } export type ChatState = { state: 'starting' | 'composing' | 'paused' | 'deleted' | 'gone' } export type WebLink = { title?: string text: string target?: 'blank' | 'self' | 'selfCompact' | 'selfTall' uri: string } export interface MessageTypesContent { 'text/plain': PlainText 'application/vnd.iris.ticket+json': Ticket 'application/vnd.lime.redirect+json': Redirect 'application/vnd.lime.media-link+json': MediaLink 'application/json': JsonObject 'application/vnd.lime.reply+json': Reply 'application/vnd.lime.select+json': Select 'application/vnd.lime.chatstate+json': ChatState 'application/vnd.lime.web-link+json': WebLink } export type MessageTypes = keyof MessageTypesContent export type Document = { type: Type value: MessageTypesContent[Type] } export type MessageContent = { type: Type content: MessageTypesContent[Type] } export type Message = Envelope & MessageContent export type UnknownDocument = Document export type UnknownMessage = Message export type UnknownMessageContent = MessageContent export const isMessage = (envelope: Envelope): envelope is UnknownMessage => 'type' in envelope && 'content' in envelope && typeof envelope.type === 'string' export const isMessageOfType = ( message: UnknownMessage, type: Type, ): message is Message => message.type === type export const isDocumentOfType = ( document: UnknownDocument, type: Type, ): document is Document => document.type === type export const messageToDocument = ( message: Pick, 'type' | 'content'>, ): Document => ({ type: message.type, value: message.content, }) /** @description Best effort to figure out the date the message was created */ export const messageDate = (message: UnknownMessage): Date | undefined => { return message.metadata?.['#envelope.storageDate'] ? new Date(message.metadata['#envelope.storageDate']) : message.metadata?.date_created ? new Date(Number(message.metadata.date_created)) : message.metadata?.['#date_processed'] ? new Date(Number(message.metadata['#date_processed'])) : undefined } /** @returns Best effort to figure out the actual unique ID, since `message.id` can have collisions */ export const messageUniqueId = (message: UnknownMessage | ThreadItem) => { // The message id is the ticket id itself if (isMessageOfType(message, 'application/vnd.iris.ticket+json')) { return message.id } // Blip doesn't have a standard unique identifier, we need to try multiple sources that may not be present return ( message.metadata?.['#uniqueId'] ?? message.metadata?.$internalId ?? message.metadata?.['#messageId'] ?? message.metadata?.['#envelope.storageDate'] ?? message.id ) }