UNPKG

@cutls/megalodon

Version:

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

312 lines (311 loc) 11.3 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (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) || function (mod) { return (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 events_1 = require("events"); var api_client_1 = __importDefault(require("./api_client")); var default_1 = require("../default"); var WebSocket = (function (_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 = function (baseUrl) { return 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 ((0, default_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 } })); 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 () { var _this = this; setTimeout(function () { 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_1.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 && !(0, default_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) { var _this = this; client.onclose = function (_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 = function (_event) { _this.emit('connect', {}); _this._channel(); if (!(0, default_1.isBrowser)()) { setTimeout(function () { client.ping(''); }, 10000); } }; client.onmessage = function (event) { _this.parser.parse(event.data, false, _this._channelID); }; client.onerror = function (event) { _this.emit('error', event.error); }; }; WebSocket.prototype._setupParser = function () { var _this = this; this.parser.on('update', function (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', api_client_1.default.Converter.note(note, _this.baseUrlToHost(_this.url)), ch); }); this.parser.on('notification', function (notification, ch) { _this.emit('notification', api_client_1.default.Converter.notification(notification, _this.baseUrlToHost(_this.url)), ch); }); this.parser.on('conversation', function (note, ch) { _this.emit('conversation', api_client_1.default.Converter.noteToConversation(note, _this.baseUrlToHost(_this.url)), ch); }); this.parser.on('delete', function (noteId, ch) { _this.emit('delete', noteId, ch); }); this.parser.on('error', function (err) { _this.emit('parser-error', err); }); }; return WebSocket; }(events_1.EventEmitter)); exports.default = WebSocket; var Parser = (function (_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; }(events_1.EventEmitter)); exports.Parser = Parser;