UNPKG

@cutls/megalodon

Version:

Mastodon, Pleroma, Misskey API client for node.js and browser

352 lines (351 loc) 9.24 kB
'use strict' var __extends = (this && this.__extends) || (() => { var extendStatics = (d, b) => { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && ((d, b) => { d.__proto__ = b })) || ((d, b) => { for (var p in b) if (Object.hasOwn(b, p)) d[p] = b[p] }) return extendStatics(d, b) } return (d, b) => { if (typeof b !== 'function' && b !== null) throw new TypeError('Class extends value ' + String(b) + ' is not a constructor or null') extendStatics(d, b) function __() { this.constructor = d } d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()) } })() var __importDefault = (this && this.__importDefault) || ((mod) => (mod && mod.__esModule ? mod : { default: mod })) Object.defineProperty(exports, '__esModule', { value: true }) exports.Parser = void 0 var isomorphic_ws_1 = __importDefault(require('isomorphic-ws')) var dayjs_1 = __importDefault(require('dayjs')) var uuid_1 = require('uuid') var events_1 = require('events') var api_client_js_1 = __importDefault(require('./api_client.js')) var notification_js_1 = require('../notification.js') var default_js_1 = require('../default.js') var WebSocket = ((_super) => { __extends(WebSocket, _super) function WebSocket(url, channel, accessToken, listId, userAgent) { var _this = _super.call(this) || this _this.listId = null _this.channelSubscriptions = [] _this._client = null _this._heartbeatInterval = 60000 _this._pongWaiting = false _this.url = url _this.parser = new Parser() _this.channel = channel _this.headers = { 'User-Agent': userAgent } if (listId === undefined) { _this.listId = null } else { _this.listId = listId } _this._accessToken = accessToken _this._reconnectInterval = 10000 _this._reconnectMaxAttempts = Infinity _this._reconnectCurrentAttempts = 0 _this._connectionClosed = false _this._channelID = (0, uuid_1.v4)() _this._pongReceivedTimestamp = (0, dayjs_1.default)() return _this } WebSocket.prototype.start = function () { this._connectionClosed = false this._resetRetryParams() this._startWebSocketConnection() } WebSocket.prototype._startWebSocketConnection = function () { this._resetConnection() this._setupParser() this._client = this._connect() this._bindSocket(this._client) } WebSocket.prototype.stop = function () { this._connectionClosed = true this._resetConnection() this._resetRetryParams() } WebSocket.prototype.subscribe = (_name, _stream, _add) => {} WebSocket.prototype.unsubscribe = (_stream) => {} WebSocket.prototype._resetConnection = function () { if (this._client) { this._client.close(1000) this._clearBinding() this._client = null } if (this.parser) { this.parser.removeAllListeners() } } WebSocket.prototype._resetRetryParams = function () { this._reconnectCurrentAttempts = 0 } WebSocket.prototype._connect = function () { var requestURL = ''.concat(this.url, '?i=').concat(this._accessToken) if ((0, default_js_1.isBrowser)()) { var cli = new isomorphic_ws_1.default(requestURL) return cli } else { var options = { headers: this.headers } var cli = new isomorphic_ws_1.default(requestURL, options) return cli } } WebSocket.prototype._channel = function () { if (!this._client) { return } switch (this.channel) { case 'conversation': this._client.send( JSON.stringify({ type: 'connect', body: { channel: 'main', id: this._channelID } }) ) break case 'user': this._client.send( JSON.stringify({ type: 'connect', body: { channel: 'main', id: this._channelID } }) ) this._client.send( JSON.stringify({ type: 'connect', body: { channel: 'homeTimeline', id: this._channelID, params: { withReplies: false } } }) ) break case 'list': this._client.send( JSON.stringify({ type: 'connect', body: { channel: 'userList', id: this._channelID, params: { listId: this.listId, withReplies: false } } }) ) break default: this._client.send( JSON.stringify({ type: 'connect', body: { channel: this.channel, id: this._channelID, params: { withReplies: false } } }) ) break } } WebSocket.prototype._reconnect = function () { setTimeout(() => { if (this._client && this._client.readyState === isomorphic_ws_1.default.CONNECTING) { return } if (this._reconnectCurrentAttempts < this._reconnectMaxAttempts) { this._reconnectCurrentAttempts++ this._clearBinding() if (this._client) { if ((0, default_js_1.isBrowser)()) { this._client.close() } else { this._client.terminate() } } console.log('Reconnecting') this._client = this._connect() this._bindSocket(this._client) } }, this._reconnectInterval) } WebSocket.prototype.reconnect = function () { this._reconnect() } WebSocket.prototype._clearBinding = function () { if (this._client && !(0, default_js_1.isBrowser)()) { this._client.removeAllListeners('close') this._client.removeAllListeners('pong') this._client.removeAllListeners('open') this._client.removeAllListeners('message') this._client.removeAllListeners('error') } } WebSocket.prototype._bindSocket = function (client) { client.onclose = (event) => { if (event.code === 1000) { this.emit('close', {}) } else { console.log('Closed connection with '.concat(event.code)) if (!this._connectionClosed) { this._reconnect() } } } client.onopen = (_event) => { this.emit('connect', {}) this._channel() if (!(0, default_js_1.isBrowser)()) { setTimeout(() => { client.ping('') }, 10000) } } client.onmessage = (event) => { this.parser.parse(event, this._channelID) } client.onerror = (event) => { this.emit('error', event.error) } if (!(0, default_js_1.isBrowser)()) { client.on('pong', () => { this._pongWaiting = false this.emit('pong', {}) this._pongReceivedTimestamp = (0, dayjs_1.default)() setTimeout(() => this._checkAlive(this._pongReceivedTimestamp), this._heartbeatInterval) }) } } WebSocket.prototype._setupParser = function () { this.parser.on('update', (note) => { this.emit('update', api_client_js_1.default.Converter.note(note)) }) this.parser.on('notification', (notification) => { var n = api_client_js_1.default.Converter.notification(notification) if (n instanceof notification_js_1.UnknownNotificationTypeError) { console.warn('Unknown notification event has received: '.concat(notification)) } else { this.emit('notification', n) } }) this.parser.on('conversation', (note) => { this.emit('conversation', api_client_js_1.default.Converter.noteToConversation(note)) }) this.parser.on('error', (err) => { this.emit('parser-error', err) }) } WebSocket.prototype._checkAlive = function (timestamp) { var now = (0, dayjs_1.default)() if (now.diff(timestamp) > this._heartbeatInterval - 1000 && !this._connectionClosed) { if (this._client && this._client.readyState !== isomorphic_ws_1.default.CONNECTING) { this._pongWaiting = true this._client.ping('') setTimeout(() => { if (this._pongWaiting) { this._pongWaiting = false this._reconnect() } }, 10000) } } } return WebSocket })(events_1.EventEmitter) exports.default = WebSocket var Parser = ((_super) => { __extends(Parser, _super) function Parser() { return (_super !== null && _super.apply(this, arguments)) || this } Parser.prototype.parse = function (ev, channelID) { var data = ev.data var message = data.toString() if (typeof message !== 'string') { this.emit('heartbeat', {}) return } if (message === '') { this.emit('heartbeat', {}) return } var obj var body try { obj = JSON.parse(message) if (obj.type !== 'channel') { return } if (!obj.body) { return } body = obj.body if (body.id !== channelID) { return } } catch (err) { this.emit('error', new Error('Error parsing websocket reply: '.concat(message, ', error message: ').concat(err))) return } switch (body.type) { case 'note': this.emit('update', body.body) break case 'notification': this.emit('notification', body.body) break case 'mention': { var note = body.body if (note.visibility === 'specified') { this.emit('conversation', note) } break } case 'renote': case 'followed': case 'follow': case 'unfollow': case 'receiveFollowRequest': case 'meUpdated': case 'readAllNotifications': case 'readAllUnreadSpecifiedNotes': case 'readAllAntennas': case 'readAllUnreadMentions': case 'unreadNotification': break default: this.emit('error', new Error('Unknown event has received: '.concat(JSON.stringify(body)))) break } } return Parser })(events_1.EventEmitter) exports.Parser = Parser