import { fetch } from "xhr" ; import { Transformer } from "./types" ; import { BaseMessage , TextMessage , MessageType , LinkAttachment , AttachmentType , Attachment , ImageAttachment , VideoAttachment , } from "sharedTypes/messageModel" ; import { unfurl , Preview } from "features/unfurl" ; import { isSafe } from "features/moderation/images" ; const createLinkAttachment = ( preview ) : LinkAttachment => { return { type : AttachmentType . Link , url : preview . url , title : preview . title , description : preview . description , provider : { name : preview . site , url : preview . domain , } , author : preview . author && { name : preview . author , url : preview . authorUrl || preview . url , } , icon : { source : preview . icon , } , image : { source : preview . image , } , video : { source : preview . video , } , } ; } ; const createImageAttachment = ( source ) : ImageAttachment => ( { type : AttachmentType . Image , image : { source , } , } ) ; const createVideoAttachment = ( source ) : VideoAttachment => ( { type : AttachmentType . Video , video : { source , } , } ) ; const extensions = { apng : "image/apng" , bmp : "image/bmp" , gif : "image/gif" , ico : "image/x-icon" , cur : "image/x-icon" , jpg : "image/jpeg" , jpeg : "image/jpeg" , jfif : "image/jpeg" , pjpeg : "image/jpeg" , pjp : "image/jpeg" , png : "image/png" , svg : "image/svg" , tiff : "image/tiff" , tif : "image/tiff" , webp : "image/webp" , avi : "video/x-msvideo" , mpeg : "video/mpeg" , mp4 : "video/mp4" , ogv : "video/ogg" , ts : "video/mp2t" , webm : "video/webm" , "3gp" : "video/3gpp" , "3g2" : "video/3gpp2" , } ; const getAttachment = async ( url ) : Promise < Attachment | null > => { let mimeType ; const extension = url . split ( "." ) . slice ( - 1 ) [ 0 ] . split ( "?" ) [ 0 ] ; if ( extensions . hasOwnProperty ( extension ) ) { mimeType = extensions [ extension ] ; } else { try { const response = await fetch ( url ) ; if ( response . ok ) { mimeType = response . headers [ "content-type" ] ; if ( mimeType . startsWith ( "text/html" ) ) { const unfurled = await unfurl ( response . body , response . url ) ; const allow = unfurled . image ? await isSafe ( unfurled . image , 0.1 ) : true ; return unfurled && allow ? createLinkAttachment ( unfurled ) : null ; } } else { return null ; } } catch ( e ) { console . log ( e ) ; return null ; } } if ( mimeType . startsWith ( "image/" ) ) { const allow = await isSafe ( url , 0.1 ) ; return allow ? createImageAttachment ( url ) : null ; } else if ( mimeType . startsWith ( "video/" ) ) { return createVideoAttachment ( url ) ; } else { return null ; } } ; const isTextMessage = ( message ) : message is TextMessage => message . type === MessageType . Text ; const getUrls = ( text ) : string [ ] => { return ( text . match ( /https?:\/\/(?:www\.)?[-a-zA-Z0-9]+\.[a-z-]+[-a-zA-Z0-9@:%_+.~#?&//=]*/g ) || [ ] ) ; } ; const transformer = async ( message ) => { if ( isTextMessage ( message ) ) { const urls = getUrls ( message . text ) ; message . attachments = ( await Promise . all ( urls . map ( getAttachment ) ) ) . filter ( ( preview ) => preview !== null ) ; } return message ; } ; export default transformer ;