Merge pull request #52 from h3poteto/websocket
Use ws instead of websocket as websocket library
This commit is contained in:
commit
8dcfbf7f7c
10
.travis.yml
10
.travis.yml
|
@ -1,8 +1,10 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
"9"
|
||||
- "8"
|
||||
- "9"
|
||||
- "10"
|
||||
- "11"
|
||||
cache:
|
||||
directories:
|
||||
"node_modules"
|
||||
yarn: true
|
||||
script:
|
||||
- npm run build
|
||||
- yarn run build
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import Mastodon, { Status, Notification, WebSocket } from 'megalodon'
|
||||
|
||||
const BASE_URL: string = 'wss://pleroma.io'
|
||||
declare var process: {
|
||||
env: {
|
||||
PLEROMA_HOST: string
|
||||
PLEROMA_ACCESS_TOKEN: string
|
||||
}
|
||||
}
|
||||
|
||||
const access_token: string = '...'
|
||||
const BASE_URL: string = process.env.PLEROMA_HOST
|
||||
|
||||
const client = new Mastodon(
|
||||
access_token,
|
||||
BASE_URL + '/api/v1'
|
||||
)
|
||||
const access_token: string = process.env.PLEROMA_ACCESS_TOKEN
|
||||
|
||||
const client = new Mastodon(access_token, BASE_URL + '/api/v1')
|
||||
|
||||
const stream: WebSocket = client.socket('/streaming', 'user')
|
||||
stream.on('connect', () => {
|
||||
|
@ -41,4 +45,3 @@ stream.on('close', () => {
|
|||
stream.on('parser-error', (err: Error) => {
|
||||
console.error(err)
|
||||
})
|
||||
|
||||
|
|
|
@ -29,12 +29,12 @@
|
|||
"dependencies": {
|
||||
"@types/oauth": "^0.9.0",
|
||||
"@types/request": "^2.47.0",
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "^6.0.1",
|
||||
"axios": "^0.18.1",
|
||||
"oauth": "^0.9.15",
|
||||
"request": "^2.87.0",
|
||||
"typescript": "^3.4.5",
|
||||
"websocket": "^1.0.28"
|
||||
"ws": "^7.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/core-js": "^2.5.0",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { client, connection, IMessage } from 'websocket'
|
||||
import WS from 'ws'
|
||||
import { EventEmitter } from 'events'
|
||||
import { Status } from './entities/status'
|
||||
import { Notification } from './entities/notification'
|
||||
|
@ -13,18 +13,19 @@ export default class WebSocket extends EventEmitter {
|
|||
public url: string
|
||||
public stream: string
|
||||
public parser: Parser
|
||||
public headers: {}
|
||||
public headers: { [key: string]: string }
|
||||
private _accessToken: string
|
||||
private _socketConnection: connection | null
|
||||
private _reconnectInterval: number
|
||||
private _reconnectMaxAttempts: number
|
||||
private _reconnectCurrentAttempts: number
|
||||
private _connectionClosed: boolean
|
||||
private _client: WS | null
|
||||
|
||||
/**
|
||||
* @param url Full url of websocket: e.g. https://pleroma.io/api/v1/streaming
|
||||
* @param stream Stream name, please refer: https://git.pleroma.social/pleroma/pleroma/blob/develop/lib/pleroma/web/mastodon_api/mastodon_socket.ex#L19-28
|
||||
* @param accessToken The access token.
|
||||
* @param userAgent The specified User Agent.
|
||||
*/
|
||||
constructor(url: string, stream: string, accessToken: string, userAgent: string) {
|
||||
super()
|
||||
|
@ -35,11 +36,11 @@ export default class WebSocket extends EventEmitter {
|
|||
'User-Agent': userAgent
|
||||
}
|
||||
this._accessToken = accessToken
|
||||
this._socketConnection = null
|
||||
this._reconnectInterval = 1000
|
||||
this._reconnectMaxAttempts = Infinity
|
||||
this._reconnectCurrentAttempts = 0
|
||||
this._connectionClosed = false
|
||||
this._client = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,18 +58,15 @@ export default class WebSocket extends EventEmitter {
|
|||
private _startWebSocketConnection() {
|
||||
this._resetConnection()
|
||||
this._setupParser()
|
||||
const cli = this._getClient()
|
||||
this._connect(cli, this.url, this.stream, this._accessToken, this.headers)
|
||||
this._client = this._connect(this.url, this.stream, this._accessToken, this.headers)
|
||||
this._bindSocket(this._client)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop current connection.
|
||||
*/
|
||||
public stop() {
|
||||
if (this._socketConnection) {
|
||||
this._connectionClosed = true
|
||||
this._socketConnection.close()
|
||||
}
|
||||
this._resetConnection()
|
||||
this._resetRetryParams()
|
||||
}
|
||||
|
||||
|
@ -76,9 +74,10 @@ export default class WebSocket extends EventEmitter {
|
|||
* Clean up current connection, and listeners.
|
||||
*/
|
||||
private _resetConnection() {
|
||||
if (this._socketConnection) {
|
||||
this._socketConnection.removeAllListeners()
|
||||
this._socketConnection.close()
|
||||
if (this._client) {
|
||||
this._client.removeAllListeners()
|
||||
this._client.close(1000)
|
||||
this._client = null
|
||||
}
|
||||
|
||||
if (this.parser) {
|
||||
|
@ -96,64 +95,68 @@ export default class WebSocket extends EventEmitter {
|
|||
/**
|
||||
* Reconnects to the same endpoint.
|
||||
*/
|
||||
private _reconnect(cli: client) {
|
||||
setTimeout(() => {
|
||||
if (this._reconnectCurrentAttempts < this._reconnectMaxAttempts) {
|
||||
this._reconnectCurrentAttempts++
|
||||
// Call connect methods
|
||||
console.log('Reconnecting')
|
||||
this._connect(cli, this.url, this.stream, this._accessToken, this.headers)
|
||||
}
|
||||
}, this._reconnectInterval)
|
||||
private _reconnect() {
|
||||
if (this._client) {
|
||||
setTimeout(() => {
|
||||
if (this._reconnectCurrentAttempts < this._reconnectMaxAttempts) {
|
||||
this._reconnectCurrentAttempts++
|
||||
// Call connect methods
|
||||
console.log('Reconnecting')
|
||||
this._client = this._connect(this.url, this.stream, this._accessToken, this.headers)
|
||||
this._bindSocket(this._client)
|
||||
}
|
||||
}, this._reconnectInterval)
|
||||
}
|
||||
}
|
||||
|
||||
private _connect(cli: client, url: string, stream: string, accessToken: string, headers: {}) {
|
||||
/**
|
||||
* @param url Base url of streaming endpoint.
|
||||
* @param stream The specified stream name.
|
||||
* @param accessToken Access token.
|
||||
* @param headers The specified headers.
|
||||
* @return A WebSocket instance.
|
||||
*/
|
||||
private _connect(url: string, stream: string, accessToken: string, headers: { [key: string]: string }): WS {
|
||||
const params: Array<string> = [`stream=${stream}`]
|
||||
|
||||
if (accessToken !== null) {
|
||||
params.push(`access_token=${accessToken}`)
|
||||
}
|
||||
const req_url: string = `${url}/?${params.join('&')}`
|
||||
cli.connect(req_url, undefined, undefined, headers)
|
||||
const requestURL: string = `${url}/?${params.join('&')}`
|
||||
const options: WS.ClientOptions = {
|
||||
headers: headers
|
||||
}
|
||||
|
||||
const cli: WS = new WS(requestURL, options)
|
||||
return cli
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare websocket client.
|
||||
* @param url Full url of websocket: e.g. https://pleroma.io/api/v1/streaming
|
||||
* @param stream Stream name, please refer: https://git.pleroma.social/pleroma/pleroma/blob/develop/lib/pleroma/web/mastodon_api/mastodon_socket.ex#L19-28
|
||||
* @param accessToken The access token.
|
||||
* @returns A Client instance of websocket.
|
||||
* Bind event for web socket client.
|
||||
* @param client A WebSocket instance.
|
||||
*/
|
||||
private _getClient(): client {
|
||||
const cli = new client()
|
||||
cli.on('connectFailed', err => {
|
||||
console.error(err)
|
||||
this._reconnect(cli)
|
||||
})
|
||||
cli.on('connect', conn => {
|
||||
this._socketConnection = conn
|
||||
this.emit('connect', {})
|
||||
conn.on('error', err => {
|
||||
this.emit('error', err)
|
||||
})
|
||||
conn.on('close', code => {
|
||||
// Refer the code: https://tools.ietf.org/html/rfc6455#section-7.4
|
||||
if (code === 1000) {
|
||||
this.emit('close', {})
|
||||
} else {
|
||||
console.log(`Closed connection with ${code}`)
|
||||
// If already called close method, it does not retry.
|
||||
if (!this._connectionClosed) {
|
||||
this._reconnect(cli)
|
||||
}
|
||||
private _bindSocket(client: WS) {
|
||||
client.on('close', (code: number, _reason: string) => {
|
||||
// Refer the code: https://tools.ietf.org/html/rfc6455#section-7.4
|
||||
if (code === 1000) {
|
||||
this.emit('close', {})
|
||||
} else {
|
||||
console.log(`Closed connection with ${code}`)
|
||||
// If already called close method, it does not retry.
|
||||
if (!this._connectionClosed) {
|
||||
this._reconnect()
|
||||
}
|
||||
})
|
||||
conn.on('message', (message: IMessage) => {
|
||||
this.parser.parser(message)
|
||||
})
|
||||
}
|
||||
})
|
||||
client.on('open', () => {
|
||||
this.emit('connect', {})
|
||||
})
|
||||
client.on('message', (data: WS.Data) => {
|
||||
this.parser.parse(data)
|
||||
})
|
||||
client.on('error', (err: Error) => {
|
||||
this.emit('error', err)
|
||||
})
|
||||
|
||||
return cli
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -193,13 +196,13 @@ class Parser extends EventEmitter {
|
|||
/**
|
||||
* @param message Message body of websocket.
|
||||
*/
|
||||
public parser(message: IMessage) {
|
||||
if (!message.utf8Data) {
|
||||
public parse(message: WS.Data) {
|
||||
if (typeof message !== 'string') {
|
||||
this.emit('heartbeat', {})
|
||||
return
|
||||
}
|
||||
const data = message.utf8Data
|
||||
if (data === '') {
|
||||
|
||||
if (message === '') {
|
||||
this.emit('heartbeat', {})
|
||||
return
|
||||
}
|
||||
|
@ -208,14 +211,14 @@ class Parser extends EventEmitter {
|
|||
let payload = ''
|
||||
let mes = {}
|
||||
try {
|
||||
const obj = JSON.parse(data)
|
||||
const obj = JSON.parse(message)
|
||||
event = obj.event
|
||||
payload = obj.payload
|
||||
mes = JSON.parse(payload)
|
||||
} catch (err) {
|
||||
// delete event does not have json object
|
||||
if (event !== 'delete') {
|
||||
this.emit('error', new Error(`Error parsing websocket reply: ${data}, error message: ${err}`))
|
||||
this.emit('error', new Error(`Error parsing websocket reply: ${message}, error message: ${err}`))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -234,7 +237,7 @@ class Parser extends EventEmitter {
|
|||
this.emit('delete', payload)
|
||||
break
|
||||
default:
|
||||
this.emit('error', new Error(`Unknown event has received: ${data}`))
|
||||
this.emit('error', new Error(`Unknown event has received: ${message}`))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
49
yarn.lock
49
yarn.lock
|
@ -72,10 +72,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.3.tgz#7f226d67d654ec9070e755f46daebf014628e9d9"
|
||||
integrity sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==
|
||||
|
||||
"@types/websocket@0.0.40":
|
||||
version "0.0.40"
|
||||
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-0.0.40.tgz#887cd632a8b3d0d11da7b9d0d106af85997b358c"
|
||||
integrity sha512-ldteZwWIgl9cOy7FyvYn+39Ah4+PfpVE72eYKw75iy2L0zTbhbcwvzeJ5IOu6DQP93bjfXq0NGHY6FYtmYoqFQ==
|
||||
"@types/ws@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.1.tgz#ca7a3f3756aa12f62a0a62145ed14c6db25d5a28"
|
||||
integrity sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q==
|
||||
dependencies:
|
||||
"@types/events" "*"
|
||||
"@types/node" "*"
|
||||
|
@ -238,6 +238,11 @@ astral-regex@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
||||
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
|
||||
|
||||
async-limiter@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
|
||||
integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
|
@ -427,7 +432,7 @@ debug@=3.1.0:
|
|||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^2.2.0, debug@^2.6.8, debug@^2.6.9:
|
||||
debug@^2.6.8, debug@^2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
|
@ -1040,7 +1045,7 @@ is-symbol@^1.0.2:
|
|||
dependencies:
|
||||
has-symbols "^1.0.0"
|
||||
|
||||
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
||||
is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||
|
@ -1218,11 +1223,6 @@ mute-stream@0.0.7:
|
|||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
||||
|
||||
nan@^2.11.0:
|
||||
version "2.11.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.1.tgz#90e22bccb8ca57ea4cd37cc83d3819b52eea6766"
|
||||
integrity sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
|
@ -1796,13 +1796,6 @@ type-check@~0.3.2:
|
|||
dependencies:
|
||||
prelude-ls "~1.1.2"
|
||||
|
||||
typedarray-to-buffer@^3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
||||
integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typescript@^3.4.5:
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99"
|
||||
|
@ -1837,16 +1830,6 @@ verror@1.10.0:
|
|||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
websocket@^1.0.28:
|
||||
version "1.0.28"
|
||||
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3"
|
||||
integrity sha512-00y/20/80P7H4bCYkzuuvvfDvh+dgtXi5kzDf3UcZwN6boTYaKvsrtZ5lIYm1Gsg48siMErd9M4zjSYfYFHTrA==
|
||||
dependencies:
|
||||
debug "^2.2.0"
|
||||
nan "^2.11.0"
|
||||
typedarray-to-buffer "^3.1.5"
|
||||
yaeti "^0.0.6"
|
||||
|
||||
which@^1.2.9:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
|
@ -1871,7 +1854,9 @@ write@1.0.3:
|
|||
dependencies:
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
yaeti@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
||||
integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=
|
||||
ws@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.0.1.tgz#1a04e86cc3a57c03783f4910fdb090cf31b8e165"
|
||||
integrity sha512-ILHfMbuqLJvnSgYXLgy4kMntroJpe8hT41dOVWM8bxRuw6TK4mgMp9VJUNsZTEc5Bh+Mbs0DJT4M0N+wBG9l9A==
|
||||
dependencies:
|
||||
async-limiter "^1.0.0"
|
||||
|
|
Loading…
Reference in a new issue