const SENTRY_PROJECT_ID = '' ; const SENTRY_KEY = '' ; const HOST = '' ; const ENV = 'production' ; const RELEASE = `slash-worker-${ require ( '../package.json' ) . version }` ; const CLIENT_NAME = 'slash-worker' ; const CLIENT_VERSION = '1.0.0' ; const RETRIES = 3 ; export async function log ( err , request ) { if ( SENTRY_PROJECT_ID === '' || SENTRY_KEY === '' || HOST === '' ) return ; if ( ! ( err instanceof Error ) ) return console . error ( err ) ; const body = JSON . stringify ( toSentryEvent ( err , request ) ) ; for ( let i = 0 ; i <= RETRIES ; i ++ ) { const res = await fetch ( `https://${ HOST }/api/${ SENTRY_PROJECT_ID }/store/` , { method : 'POST' , headers : { 'Content-Type' : 'application/json' , 'X-Sentry-Auth' : [ 'Sentry sentry_version=7' , `sentry_client=${ CLIENT_NAME }/${ CLIENT_VERSION }` , `sentry_key=${ SENTRY_KEY }` , ] . join ( ', ' ) , } , body , } ) ; if ( res . status === 200 ) { return ; } console . error ( { httpStatus : res . status , ... ( await res . json ( ) ) } ) ; } } function toSentryEvent ( err , request ) { const errType = err . name || ( err . constructor || { } ) . name ; const frames = parse ( err ) ; return { event_id : uuidv4 ( ) , message : errType + ': ' + ( err . message || '<no message>' ) , exception : { values : [ { type : errType , value : err . message , stacktrace : frames . length ? { frames : frames . reverse ( ) } : undefined , } , ] , } , extra : { guild_id : 'guild_id' in request ? request . guild_id : request . guildId , channel_id : 'channel_id' in request ? request . channel_id : request . channelId , command_name : request . data === undefined ? undefined : request . data . name , options : request . data === undefined ? undefined : request . data . options , from : request . from , } , tags : { command_id : request . data === undefined ? undefined : request . data . id , } , platform : 'javascript' , environment : ENV , timestamp : Date . now ( ) / 1000 , user : request . member . user === undefined ? undefined : { id : request . member . user . id , username : `${ request . member . user . username }#${ request . member . user . discriminator }` , } , release : RELEASE , } ; } function parse ( err ) { return ( err . stack || '' ) . split ( '\n' ) . slice ( 1 ) . map ( ( line ) => { if ( line . match ( /^\s*[-]{4,}$/ ) ) { return { filename : line } ; } const lineMatch = line . match ( /at (?:(.+)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/ ) ; if ( ! lineMatch ) { return ; } return { function : lineMatch [ 1 ] || undefined , filename : lineMatch [ 2 ] || undefined , lineno : + lineMatch [ 3 ] || undefined , colno : + lineMatch [ 4 ] || undefined , in_app : lineMatch [ 5 ] !== 'native' || undefined , } ; } ) . filter ( Boolean ) ; } function uuidv4 ( ) { const bytes = new Uint8Array ( 16 ) ; crypto . getRandomValues ( bytes ) ; bytes [ 6 ] = ( bytes [ 6 ] & 0x0f ) | 0x40 ; bytes [ 8 ] = ( bytes [ 8 ] & 0x3f ) | 0x80 ; return [ ... bytes ] . map ( ( b ) => ( '0' + b . toString ( 16 ) ) . slice ( - 2 ) ) . join ( '' ) ; }