From dc59c88b0a7c431df167e91a5dac11096d3bf3b9 Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Fri, 24 May 2019 01:37:02 +0900 Subject: [PATCH] refs #41 Handle delete message from streaming --- .prettierrc | 6 ++++ example/javascript/streaming.js | 24 ++++++-------- example/javascript/web_socket.js | 30 ++++++----------- src/entities/status.ts | 50 ++++++++++++++-------------- src/parser.ts | 47 ++++++++++++++------------ src/stream_listener.ts | 12 +++---- src/web_socket.ts | 57 ++++++++++++++++++-------------- 7 files changed, 114 insertions(+), 112 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6692425 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "printWidth": 140 +} \ No newline at end of file diff --git a/example/javascript/streaming.js b/example/javascript/streaming.js index 1e23d7a..2715025 100644 --- a/example/javascript/streaming.js +++ b/example/javascript/streaming.js @@ -1,13 +1,10 @@ -const Mastodon = require( '../../lib/mastodon') +const Mastodon = require('../../lib/mastodon') -const BASE_URL = 'https://mastodon.social' +const BASE_URL = 'https://mstdn.jp' -const access_token = '...' +const access_token = process.env.MASTODON_ACCESS_TOKEN -const client = new Mastodon( - access_token, - BASE_URL + '/api/v1' -) +const client = new Mastodon(access_token, BASE_URL + '/api/v1') const stream = client.stream('/streaming/public') stream.on('connect', event => { @@ -18,27 +15,26 @@ stream.on('not-event-stream', mes => { console.log(mes) }) -stream.on('update', (status) => { +stream.on('update', status => { console.log(status) }) -stream.on('notification', (notification) => { +stream.on('notification', notification => { console.log(notification) }) -stream.on('delete', (id) => { - console.log(id) +stream.on('delete', id => { + console.log(`delete: ${id}`) }) -stream.on('error', (err) => { +stream.on('error', err => { console.error(err) }) -stream.on('heartbeat', (msg) => { +stream.on('heartbeat', msg => { console.log('thump.') }) stream.on('connection-limit-exceeded', err => { console.error(err) }) - diff --git a/example/javascript/web_socket.js b/example/javascript/web_socket.js index bc8840d..4b74f10 100644 --- a/example/javascript/web_socket.js +++ b/example/javascript/web_socket.js @@ -2,38 +2,32 @@ const Mastodon = require('../../lib/mastodon') const BASE_URL = 'wss://pleroma.io' -const access_token = '...' +const access_token = process.env.PLEROMA_ACCESS_TOKEN -const client = new Mastodon( - access_token, - BASE_URL + '/api/v1' -) +const client = new Mastodon(access_token, BASE_URL + '/api/v1') -const stream = client.socket( - '/streaming', - 'user' -) +const stream = client.socket('/streaming', 'user') stream.on('connect', event => { console.log('connect') }) -stream.on('update', (status) => { +stream.on('update', status => { console.log(status) }) -stream.on('notification', (notification) => { +stream.on('notification', notification => { console.log(notification) }) -stream.on('delete', (id) => { - console.log(id) +stream.on('delete', id => { + console.log(`delete: ${id}`) }) -stream.on('error', (err) => { +stream.on('error', err => { console.error(err) }) -stream.on('heartbeat', (msg) => { +stream.on('heartbeat', msg => { console.log('thump.') }) @@ -41,10 +35,6 @@ stream.on('close', () => { console.log('close') }) -stream.on('parser-error', (err) => { +stream.on('parser-error', err => { console.error(err) }) - -setTimeout(() => { - stream.stop() -}, 10000) diff --git a/src/entities/status.ts b/src/entities/status.ts index d853a30..2cbee1e 100644 --- a/src/entities/status.ts +++ b/src/entities/status.ts @@ -7,30 +7,30 @@ import Emoji from './emoji' import Card from './card' export default interface Status { - id: number, - uri: string, - url: string, - account: Account, - in_reply_to_id: number | null, - in_reply_to_account_id: number | null, - reblog: Status | null, - content: string, - created_at: string, - emojis: Emoji[], - replies_count: number, - reblogs_count: number, - favourites_count: number, - reblogged: boolean | null, - favourited: boolean | null, - muted: boolean | null, - sensitive: boolean, - spoiler_text: string, - visibility: 'public' | 'unlisted' | 'private' | 'direct', - media_attachments: Attachment[], - mentions: Mention[], - tags: Tag[], - card: Card | null, - application: Application, - language: string | null, + id: string + uri: string + url: string + account: Account + in_reply_to_id: number | null + in_reply_to_account_id: number | null + reblog: Status | null + content: string + created_at: string + emojis: Emoji[] + replies_count: number + reblogs_count: number + favourites_count: number + reblogged: boolean | null + favourited: boolean | null + muted: boolean | null + sensitive: boolean + spoiler_text: string + visibility: 'public' | 'unlisted' | 'private' | 'direct' + media_attachments: Attachment[] + mentions: Mention[] + tags: Tag[] + card: Card | null + application: Application + language: string | null pinned: boolean | null } diff --git a/src/parser.ts b/src/parser.ts index a8fe490..2cbd0af 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -52,30 +52,33 @@ class Parser extends EventEmitter { const event: string = root[0].substr(7) const data: string = root[1].substr(6) + let jsonObj = {} try { - const obj = JSON.parse(data) - switch (event) { - case 'update': - this.emit('update', obj as Status) - break - case 'notification': - this.emit('notification', obj as Notification) - break - case 'conversation': - this.emit('conversation', obj as Conversation) - break - case 'delete': - // When delete, data is an ID of the deleted status - this.emit('delete', obj as number) - break - default: - this.emit('error', new Error(`Unknown event has received: ${event}`)) - break - } + jsonObj = JSON.parse(data) } catch (err) { - this.emit('error', new Error(`Error parsing API reply: '${piece}', error message: '${err}'`)) - } finally { - continue + // 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 Status) + break + case 'notification': + this.emit('notification', jsonObj as Notification) + break + case 'conversation': + this.emit('conversation', jsonObj as Conversation) + break + case 'delete': + // When delete, data is an ID of the deleted status + this.emit('delete', data as string) + break + default: + this.emit('error', new Error(`Unknown event has received: ${event}`)) + continue } } offset++ diff --git a/src/stream_listener.ts b/src/stream_listener.ts index de9b2c6..9d82092 100644 --- a/src/stream_listener.ts +++ b/src/stream_listener.ts @@ -18,7 +18,6 @@ class StreamingError extends Error { } } - /** * StreamListener * Listener of streaming. It receives data, and parse when streaming. @@ -35,7 +34,6 @@ class StreamListener extends EventEmitter { private _usedFirstReconnect: boolean private _stallAbortTimeout: NodeJS.Timer | undefined - /** * @param url full url of streaming: e.g. https://mastodon.social/api/v1/streaming/user * @param headers headers of streaming request @@ -113,7 +111,7 @@ class StreamListener extends EventEmitter { this.request = Request.get(this._buildRequestOption()) this.emit('connect', this.request) - this.request.on('response', (response) => { + this.request.on('response', response => { // reset our reconnection attempt flag so next attempt goes through with 0 delay // if we get a transport-level error this._usedFirstReconnect = false @@ -125,7 +123,7 @@ class StreamListener extends EventEmitter { } if (STATUS_CODES_TO_ABORT_ON.indexOf(response.statusCode) > -1) { let body: string = '' - this.response.on('data', (chunk) => { + this.response.on('data', chunk => { body += chunk.toString() try { @@ -141,14 +139,14 @@ class StreamListener extends EventEmitter { body = '' }) } else { - this.response.on('data', (chunk) => { + this.response.on('data', chunk => { this._connectInterval = 0 this._resetStallAbortTimeout() this.parser.parse(chunk.toString()) }) - this.response.on('error', (err) => { + this.response.on('error', err => { // expose response errors on twit instance this.emit('error', err) }) @@ -286,7 +284,7 @@ class StreamListener extends EventEmitter { this.parser.on('conversation', (conversation: Conversation) => { this.emit('conversation', conversation) }) - this.parser.on('delete', (id: number) => { + this.parser.on('delete', (id: string) => { this.emit('delete', id) }) this.parser.on('error', (err: Error) => { diff --git a/src/web_socket.ts b/src/web_socket.ts index 2f0ba53..10c8b1c 100644 --- a/src/web_socket.ts +++ b/src/web_socket.ts @@ -122,17 +122,17 @@ export default class WebSocket extends EventEmitter { */ private _getClient(): client { const cli = new client() - cli.on('connectFailed', (err) => { + cli.on('connectFailed', err => { console.error(err) this._reconnect(cli) }) - cli.on('connect', (conn) => { + cli.on('connect', conn => { this._socketConnection = conn this.emit('connect', {}) - conn.on('error', (err) => { + conn.on('error', err => { this.emit('error', err) }) - conn.on('close', (code) => { + conn.on('close', code => { // Refer the code: https://tools.ietf.org/html/rfc6455#section-7.4 if (code === 1000) { this.emit('close', {}) @@ -162,7 +162,7 @@ export default class WebSocket extends EventEmitter { this.parser.on('notification', (notification: Notification) => { this.emit('notification', notification) }) - this.parser.on('delete', (id: number) => { + this.parser.on('delete', (id: string) => { this.emit('delete', id) }) this.parser.on('conversation', (conversation: Conversation) => { @@ -200,28 +200,37 @@ class Parser extends EventEmitter { return } + let event = '' + let payload = '' + let mes = {} try { const obj = JSON.parse(data) - const payload: string = obj.payload - const mes = JSON.parse(payload) - switch (obj.event) { - case 'update': - this.emit('update', mes as Status) - break - case 'notification': - this.emit('notification', mes as Notification) - break - case 'delete': - this.emit('delete', mes as number) - break - case 'conversation': - this.emit('conversation', mes as Conversation) - break - default: - this.emit('error', new Error(`Unknown event has received: ${obj}`)) - } + event = obj.event + payload = obj.payload + mes = JSON.parse(payload) } catch (err) { - this.emit('error', new Error(`Error parsing websocket reply: ${data}, error message: ${err}`)) + // delete event does not have json object + if (event !== 'delete') { + this.emit('error', new Error(`Error parsing websocket reply: ${data}, error message: ${err}`)) + return + } + } + + switch (event) { + case 'update': + this.emit('update', mes as Status) + break + case 'notification': + this.emit('notification', mes as Notification) + break + case 'conversation': + this.emit('conversation', mes as Conversation) + break + case 'delete': + this.emit('delete', payload as string) + break + default: + this.emit('error', new Error(`Unknown event has received: ${data}`)) } return }