import { LogEvent , LogFileParsingState , ParsingMetadata , RawLogEvent , StatefulLogEvent } from 'root/app/log-parser/model' ; import { asArray , asMap , asNumber , asString } from 'root/lib/type_utils' ; export function parseEvent ( data , state , options , oldlog ? ) { const rawEvent = parseAsRawEvent ( data ) ; if ( rawEvent === undefined ) { return [ ] ; } const timestamp = getEventTimestamp ( rawEvent ) ; if ( timestamp !== undefined && + timestamp > 0 ) { state . timestamp = timestamp ; } const events = postProcessEvent ( rawEvent , options ) ; for ( const event of events ) { if ( event . name === options . userChangeEvent ) { handleUserChangeEvent ( event , state ) ; } if ( event . name === options . matchStartEvent ) { handleMatchStartEvent ( event , state ) ; } if ( event . name === options . TurnInfoAllEvent ) { handleTurnEvent ( event , state ) ; } if ( event . name === options . PlayersInfoEvent ) { handlePlayersInfoEvent ( event , state ) ; } } const statefulEvents = events . map ( ( e ) => logEventToStatefulEvent ( e , state ) ) ; for ( const event of events ) { if ( event . name === options . matchEndEvent ) { handleMatchEndEvent ( event , state ) ; } } return statefulEvents ; } export function logEventToStatefulEvent ( event , state ) { return { ... event , userId : state . userId , matchId : state . matchId , timestamp : state . timestamp , turnNumber : state . turnNumber , lifeTotals : { pl1 : state . lifeTotals ?. pl1 , pl2 : state . lifeTotals ?. pl2 } , } ; } export function handleUserChangeEvent ( event , state ) { const userId = asString ( extractValue ( event . data , [ 'params' , 'payloadObject' , 'playerId' ] ) ) ; const screenName = asString ( extractValue ( event . data , [ 'params' , 'payloadObject' , 'screenName' ] ) ) ; state . userId = userId ; state . screenName = screenName ; } export function handleMatchStartEvent ( event , state ) { const matchId = asString ( extractValue ( event . data , [ 'gameRoomInfo' , 'gameRoomConfig' , 'matchId' ] ) ) ; state . matchId = matchId ; } export function handlePlayersInfoEvent ( event , state ) { const pl1 = asNumber ( extractValue ( event . data , [ 0 , 'lifeTotal' ] ) ) ; const pl2 = asNumber ( extractValue ( event . data , [ 1 , 'lifeTotal' ] ) ) ; state . lifeTotals = { pl1 , pl2 } ; } export function handleTurnEvent ( event , state ) { const turnNumber = asNumber ( extractValue ( event . data , [ 'turnNumber' ] ) ) ; state . turnNumber = turnNumber ; } export function handleMatchEndEvent ( event , state ) { state . matchId = undefined ; state . turnNumber = undefined ; state . lifeTotals = { pl1 : undefined , pl2 : undefined } ; } export function getEventTimestamp ( rawEvent ) { const timeFromRawData = asNumber ( extractValue ( rawEvent . rawData , [ 'timestamp' ] ) ) ; if ( timeFromRawData !== undefined ) { const epoch = 621355968000000000 ; return + timeFromRawData > epoch ? Math . floor ( ( timeFromRawData - epoch ) / ( 10 * 1000 ) ) : timeFromRawData . toString ( ) . length === 10 ? Math . floor ( timeFromRawData * 1000 ) : Math . floor ( timeFromRawData ) ; } const timeFromData = asString ( extractValue ( rawEvent . data , [ 'params' , 'payloadObject' , 'timestamp' ] ) ) ; if ( timeFromData !== undefined ) { try { return Date . parse ( timeFromData ) ; } catch { } } if ( rawEvent . name === '==> LogBusinessEvents' ) { const rawEventData = asMap ( rawEvent . data ) ; if ( rawEventData !== undefined ) { const eventTime = asString ( rawEventData . EventTime ) ; if ( eventTime !== undefined ) { try { return Date . parse ( eventTime ) ; } catch { } } } } return undefined ; } export function postProcessEvent ( rawEvent , options ) { const parsingOptions = options . events . find ( ( o ) => o . name . toUpperCase ( ) === rawEvent . name . toUpperCase ( ) ) ; if ( parsingOptions === undefined ) { return [ ] ; } const toLogEvent = ( ) => { const name = parsingOptions . newName !== undefined ? parsingOptions . newName : rawEvent . name ; return { ... rawEvent , name , indicator : parsingOptions . indicator } ; } ; if ( parsingOptions . constraint !== undefined ) { const value = extractValue ( rawEvent . data , parsingOptions . constraint . attributesPath ) ; if ( Array . isArray ( parsingOptions . constraint . value ) ) { if ( ! parsingOptions . constraint . value . includes ( value ) ) { return [ ] ; } } else { if ( ( parsingOptions . constraint . value !== 'defined' && value !== parsingOptions . constraint . value ) || ( parsingOptions . constraint . value === 'defined' && value === undefined ) ) { return [ ] ; } } } if ( parsingOptions . multiEvents !== undefined ) { const subEventsData = asArray ( extractValue ( rawEvent . data , parsingOptions . multiEvents . attributesPath ) ) ; if ( subEventsData !== undefined ) { let subEvents = [ ] ; for ( const subEventData of subEventsData ) { subEvents = subEvents . concat ( postProcessEvent ( { name : parsingOptions . multiEvents . subEventName , data : subEventData , rawData : subEventData } , options ) ) ; } if ( parsingOptions . indicator !== undefined ) { subEvents . push ( toLogEvent ( ) ) ; } return subEvents ; } } if ( parsingOptions . renamer !== undefined ) { const newName = asString ( extractValue ( rawEvent . data , parsingOptions . renamer ) ) ; if ( newName !== undefined ) { const events = postProcessEvent ( { ... rawEvent , name : newName } , options ) ; if ( parsingOptions . indicator !== undefined ) { events . push ( toLogEvent ( ) ) ; } return events ; } } if ( parsingOptions . subEvents !== undefined ) { let subEvents = [ ] ; parsingOptions . subEvents . forEach ( ( subEvent ) => { const data = extractValue ( rawEvent . data , subEvent . attributesPath ) ; if ( data !== undefined ) { subEvents = subEvents . concat ( postProcessEvent ( { name : subEvent . subEventName , data , rawData : data , } , options ) ) ; } } ) ; if ( parsingOptions . indicator !== undefined ) { subEvents . push ( toLogEvent ( ) ) ; } return subEvents ; } return [ toLogEvent ( ) ] ; } function isClosingEvent ( eventName ) { return [ 'FrontDoorConnection.Close' , 'Client.TcpConnection.Close' , 'FrontDoorConnection.Open' ] . includes ( eventName ) ; } export function parseAsRawEvent ( value ) { const firstOpenCurlyBraces = value . indexOf ( '{' ) ; const firstOpenRoundBraces = value . indexOf ( '(' ) ; if ( firstOpenCurlyBraces === - 1 ) { return undefined ; } try { const nameLimiter = firstOpenRoundBraces !== - 1 && firstOpenRoundBraces < firstOpenCurlyBraces ? firstOpenRoundBraces : firstOpenCurlyBraces ; const protoName = value . slice ( 0 , nameLimiter ) ; const eventName = postProcessEventName ( protoName ) ; if ( isClosingEvent ( eventName ) ) { return { name : eventName , data : { } , rawData : { } } ; } const rawData = JSON . parse ( value . slice ( firstOpenCurlyBraces ) ) ; const data = extractEventData ( eventName , rawData ) ; if ( data === undefined ) { return undefined ; } return { name : eventName , data , rawData } ; } catch ( err ) { return undefined ; } } const timedMessageRegex = /^[0-9]{1,4}[\/.:-][0-9]{1,4}[\/.:-][0-9]{1,4}/ ; export function postProcessEventName ( message ) { if ( message . match ( timedMessageRegex ) ) { const fragments = message . trim ( ) . split ( ' ' ) ; const processedName = `${ message . includes ( '==>' ) ? '==> ' : message . includes ( '<==' ) ? '<== ' : '' }${ fragments [ fragments . length - 1 ] . trim ( ) }` ; return processedName ; } return message . trim ( ) ; } export function extractEventData ( eventName , rawData ) { const dataMap = asMap ( rawData ) ; if ( dataMap === undefined ) { return undefined ; } if ( dataMap . request !== undefined ) { const requestExtracted = asMap ( parseAsJSONIfNeeded ( dataMap . request ) ) ; if ( requestExtracted ?. Payload !== undefined ) { return parseAsJSONIfNeeded ( requestExtracted . Payload ) ; } else { return requestExtracted ; } } if ( dataMap . payload !== undefined ) { return parseAsJSONIfNeeded ( dataMap . payload ) ; } if ( dataMap . Payload !== undefined ) { return parseAsJSONIfNeeded ( dataMap . Payload ) ; } const eventNameUpperCase = eventName . toUpperCase ( ) ; const eventNameProperty = Object . keys ( dataMap ) . find ( ( attr ) => attr . toUpperCase ( ) === eventNameUpperCase ) ; if ( eventNameProperty !== undefined ) { return parseAsJSONIfNeeded ( dataMap [ eventNameProperty ] ) ; } return parseAsJSONIfNeeded ( dataMap ) ; } export function parseAsJSONIfNeeded ( data ) { if ( typeof data === 'string' && data . length > 0 && data [ 0 ] === '{' ) { try { return JSON . parse ( data ) ; } catch ( err ) { } } return data ; } export function extractValue ( data , attributesPath ) { let value = data ; for ( const attribute of attributesPath ) { value = asMap ( value , { } ) [ attribute ] ; } return value ; }