2023-09-25 08:49:57 +09:00
|
|
|
import { EventEmitter } from 'events'
|
|
|
|
import Entity from './entity'
|
2023-09-24 01:49:47 +09:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Parser
|
|
|
|
* Parse response data in streaming.
|
|
|
|
**/
|
|
|
|
export class Parser extends EventEmitter {
|
2023-09-25 08:49:57 +09:00
|
|
|
private message: string
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
constructor() {
|
|
|
|
super()
|
|
|
|
this.message = ''
|
|
|
|
}
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
public parse(chunk: string) {
|
|
|
|
// skip heartbeats
|
|
|
|
if (chunk === ':thump\n') {
|
|
|
|
this.emit('heartbeat', {})
|
|
|
|
return
|
|
|
|
}
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
this.message += chunk
|
|
|
|
chunk = this.message
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
const size: number = chunk.length
|
|
|
|
let start: number = 0
|
|
|
|
let offset: number = 0
|
|
|
|
let curr: string | undefined
|
|
|
|
let next: string | undefined
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
while (offset < size) {
|
|
|
|
curr = chunk[offset]
|
|
|
|
next = chunk[offset + 1]
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
if (curr === '\n' && next === '\n') {
|
|
|
|
const piece: string = chunk.slice(start, offset)
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
offset += 2
|
|
|
|
start = offset
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
if (!piece.length) continue // empty object
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
const root: Array<string> = piece.split('\n')
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
// should never happen, as long as mastodon doesn't change API messages
|
|
|
|
if (root.length !== 2) continue
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
// remove event and data markers
|
|
|
|
const event: string = root[0].substr(7)
|
|
|
|
const data: string = root[1].substr(6)
|
2023-09-24 01:49:47 +09:00
|
|
|
|
2023-09-25 08:49:57 +09:00
|
|
|
let jsonObj = {}
|
|
|
|
try {
|
|
|
|
jsonObj = JSON.parse(data)
|
|
|
|
} catch (err) {
|
|
|
|
// delete event does not have json object
|
|
|
|
if (event !== 'delete') {
|
|
|
|
this.emit('error', new Error(`Error parsing API reply: '${piece}', error message: '${err}'`))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (event) {
|
|
|
|
case 'update':
|
|
|
|
this.emit('update', jsonObj as Entity.Status)
|
|
|
|
break
|
|
|
|
case 'notification':
|
|
|
|
this.emit('notification', jsonObj as Entity.Notification)
|
|
|
|
break
|
|
|
|
case 'conversation':
|
|
|
|
this.emit('conversation', jsonObj as Entity.Conversation)
|
|
|
|
break
|
|
|
|
case 'delete':
|
|
|
|
// When delete, data is an ID of the deleted status
|
|
|
|
this.emit('delete', data)
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
this.emit('error', new Error(`Unknown event has received: ${event}`))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offset++
|
|
|
|
}
|
|
|
|
this.message = chunk.slice(start, size)
|
|
|
|
}
|
2023-09-24 01:49:47 +09:00
|
|
|
}
|