UNPKG

@cutls/megalodon

Version:

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

313 lines (312 loc) 8.06 kB
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 __()) } })() import WS from 'isomorphic-ws' import { EventEmitter } from 'events' import MisskeyAPI from './api_client' import { isBrowser } from '../default' var WebSocket = ((_super) => { __extends(WebSocket, _super) function WebSocket(url, channel, accessToken, listId, userAgent) { var _this = _super.call(this) || this _this.listId = null _this._client = null _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 = 'user' return _this } WebSocket.prototype.start = function () { this._connectionClosed = false this._resetRetryParams() this._startWebSocketConnection() } WebSocket.prototype.baseUrlToHost = (baseUrl) => baseUrl.replace('https://', '').replace('wss://', '') 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 = function (channelID, stream, add) { var _a ;(_a = this._client) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({ type: 'connect', body: { channel: stream, id: channelID, params: add } })) } 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 (isBrowser()) { var cli = new WS(requestURL) return cli } else { var options = { headers: this.headers } var cli = new WS(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 } }) ) break case 'list': this._client.send( JSON.stringify({ type: 'connect', body: { channel: 'userList', id: this._channelID, params: { listId: this.listId } } }) ) break default: this._client.send( JSON.stringify({ type: 'connect', body: { channel: this.channel, id: this._channelID } }) ) break } } WebSocket.prototype._reconnect = function () { setTimeout(() => { if (this._client && this._client.readyState === WS.CONNECTING) { return } if (this._reconnectCurrentAttempts < this._reconnectMaxAttempts) { this._reconnectCurrentAttempts++ this._clearBinding() if (this._client) { if (isBrowser()) { this._client.close() } else { this._client.terminate() } } console.log('Reconnecting') this._client = this._connect() this._bindSocket(this._client) } }, this._reconnectInterval) } WebSocket.prototype._clearBinding = function () { if (this._client && !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 = (_a) => { var code = _a.code if (code === 1000) { this.emit('close', {}) } else { console.log('Closed connection with '.concat(code)) if (!this._connectionClosed) { this._reconnect() } } } client.onopen = (_event) => { this.emit('connect', {}) this._channel() if (!isBrowser()) { setTimeout(() => { client.ping('') }, 10000) } } client.onmessage = (event) => { this.parser.parse(event.data, false, this._channelID) } client.onerror = (event) => { this.emit('error', event.error) } } WebSocket.prototype._setupParser = function () { this.parser.on('update', (note, ch) => { var _a if (note.id) (_a = this._client) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({ type: 's', body: { id: note.id } })) this.emit('update', MisskeyAPI.Converter.note(note, this.baseUrlToHost(this.url)), ch) }) this.parser.on('notification', (notification, ch) => { this.emit('notification', MisskeyAPI.Converter.notification(notification, this.baseUrlToHost(this.url)), ch) }) this.parser.on('conversation', (note, ch) => { this.emit('conversation', MisskeyAPI.Converter.noteToConversation(note, this.baseUrlToHost(this.url)), ch) }) this.parser.on('delete', (noteId, ch) => { this.emit('delete', noteId, ch) }) this.parser.on('error', (err) => { this.emit('parser-error', err) }) } return WebSocket })(EventEmitter) export default WebSocket var Parser = ((_super) => { __extends(Parser, _super) function Parser() { return (_super !== null && _super.apply(this, arguments)) || this } Parser.prototype.parse = function (data, isBinary, _channelID) { var message = isBinary ? data : 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 === 'noteUpdated') { if (obj.body.type !== 'deleted') return var noteId = obj.body.id this.emit('delete', noteId) } if (obj.type !== 'channel') { return } if (!obj.body) { return } body = obj.body } 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, [body.id]) break case 'notification': this.emit('notification', body.body, [body.id]) break case 'mention': { var note = body.body if (note.visibility === 'specified') { this.emit('conversation', note, [body.id]) } 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 })(EventEmitter) export { Parser }