UNPKG

@cutls/megalodon

Version:

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

382 lines (381 loc) 14.6 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 __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; 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 dayjs_1 = __importDefault(require("dayjs")); 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 Streaming = (function (_super) { __extends(Streaming, _super); function Streaming(url, stream, params, accessToken, userAgent) { var _this = _super.call(this) || this; _this.channelSubscriptions = []; _this._heartbeatInterval = 60000; _this._pongWaiting = false; _this.url = url; _this.stream = stream; if (params === undefined) { _this.params = null; } else { _this.params = params; } _this.parser = new Parser(); _this.headers = { 'User-Agent': userAgent }; _this._accessToken = accessToken; _this._reconnectInterval = 10000; _this._reconnectMaxAttempts = Infinity; _this._reconnectCurrentAttempts = 0; _this._connectionClosed = false; _this._client = null; _this._pongReceivedTimestamp = (0, dayjs_1.default)(); return _this; } Streaming.prototype.start = function () { try { this._connectionClosed = false; this._resetRetryParams(); this._startWebSocketConnection(); } catch (err) { console.error(err); } }; Streaming.prototype._startWebSocketConnection = function () { this._resetConnection(); this._setupParser(); var client = this._connect(this.url, this.stream, this.params, this._accessToken, this.headers); this._client = client; if (client) this._bindSocket(client); }; Streaming.prototype.stop = function () { this._connectionClosed = true; this._resetConnection(); this._resetRetryParams(); }; Streaming.prototype.subscribe = function (name, _stream, add) { var _a; (_a = this._client) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(__assign({ type: 'subscribe', stream: name }, add))); this.channelSubscriptions.push(__assign({ type: 'subscribe', stream: name }, add)); }; Streaming.prototype.unsubscribe = function (stream) { var _a; (_a = this._client) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({ type: 'unsubscribe', stream: stream })); this.channelSubscriptions = this.channelSubscriptions.filter(function (ch) { return !(ch.type === 'subscribe' && ch.stream === stream); }); }; Streaming.prototype._resetConnection = function () { if (this._client) { this._client.close(1000); this._clearBinding(); this._client = null; this.channelSubscriptions = []; } if (this.parser) { this.parser.removeAllListeners(); } }; Streaming.prototype._resetRetryParams = function () { this._reconnectCurrentAttempts = 0; }; Streaming.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_js_1.isBrowser)()) { _this._client.close(); } else { _this._client.terminate(); } } console.log('Reconnecting'); var client = _this._connect(_this.url, _this.stream, _this.params, _this._accessToken, _this.headers); _this._client = client; if (client) _this._bindSocket(client); } }, this._reconnectInterval); }; Streaming.prototype.reconnect = function () { this._reconnect(); }; Streaming.prototype._connect = function (url, stream, params, accessToken, headers) { var parameter = stream ? ["stream=".concat(stream)] : []; if (params) { parameter.push(params); } if (accessToken !== null) { parameter.push("access_token=".concat(accessToken)); headers.Authorization = "Bearer ".concat(accessToken); } var requestURL = "".concat(url, "?").concat(parameter.join('&')); if ((0, default_js_1.isBrowser)()) { try { var cli = new isomorphic_ws_1.default(requestURL); return cli; } catch (_a) { return null; } } else { var options = { headers: headers }; try { var cli = new isomorphic_ws_1.default(requestURL, options); return cli; } catch (_b) { return null; } } }; Streaming.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'); } }; Streaming.prototype._bindSocket = function (client) { var _this = this; client.onclose = function (event) { if (event.code === 1000) { _this.emit('close', {}); } else { console.log("Closed connection with ".concat(event.code)); if (!_this._connectionClosed) { _this._reconnect(); } } }; client.onopen = function (_event) { var e_1, _a; var chsRaw = structuredClone(_this.channelSubscriptions); var chs = __spreadArray([], __read(new Set(chsRaw.map(function (e) { return JSON.stringify(e); }))), false).map(function (e) { return JSON.parse(e); }); _this.channelSubscriptions = []; if (!chs.length) _this.emit('connect', {}); if (!(0, default_js_1.isBrowser)()) { setTimeout(function () { client.ping(''); }, 10000); } try { for (var chs_1 = __values(chs), chs_1_1 = chs_1.next(); !chs_1_1.done; chs_1_1 = chs_1.next()) { var ch = chs_1_1.value; client.send(JSON.stringify(ch)); _this.channelSubscriptions.push(ch); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (chs_1_1 && !chs_1_1.done && (_a = chs_1.return)) _a.call(chs_1); } finally { if (e_1) throw e_1.error; } } if (chs.length) { _this.emit('connect', {}); } }; client.onmessage = function (event) { _this.parser.parse(event); }; client.onerror = function (event) { _this.emit('error', event.error); }; if (!(0, default_js_1.isBrowser)()) { client.on('pong', function () { _this._pongWaiting = false; _this.emit('pong', {}); _this._pongReceivedTimestamp = (0, dayjs_1.default)(); setTimeout(function () { return _this._checkAlive(_this._pongReceivedTimestamp); }, _this._heartbeatInterval); }); } }; Streaming.prototype._setupParser = function () { var _this = this; this.parser.on('update', function (status, ch) { _this.emit('update', api_client_js_1.default.Converter.status(status), ch); }); this.parser.on('notification', function (notification, ch) { 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, ch); } }); this.parser.on('delete', function (id, ch) { _this.emit('delete', id, ch); }); this.parser.on('conversation', function (conversation, ch) { _this.emit('conversation', api_client_js_1.default.Converter.conversation(conversation), ch); }); this.parser.on('status_update', function (status, ch) { _this.emit('status_update', api_client_js_1.default.Converter.status(status), ch); }); this.parser.on('error', function (err) { _this.emit('parser-error', err); }); this.parser.on('heartbeat', function (_) { _this.emit('heartbeat', 'heartbeat'); }); }; Streaming.prototype._checkAlive = function (timestamp) { var _this = this; 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(function () { if (_this._pongWaiting) { _this._pongWaiting = false; _this._reconnect(); } }, 10000); } } }; return Streaming; }(events_1.EventEmitter)); exports.default = Streaming; var Parser = (function (_super) { __extends(Parser, _super); function Parser() { return _super !== null && _super.apply(this, arguments) || this; } Parser.prototype.parse = function (ev) { var data = ev.data; var message = data.toString(); if (typeof message !== 'string') { this.emit('heartbeat', {}); return; } if (message === '') { this.emit('heartbeat', {}); return; } var ch = []; var event = ''; var payload = ''; var mes = {}; try { var obj = JSON.parse(message); event = obj.event; payload = obj.payload; ch = obj.stream || []; mes = JSON.parse(payload); } catch (err) { if (event !== 'delete') { this.emit('error', new Error("Error parsing websocket reply: ".concat(message, ", error message: ").concat(err))); return; } } switch (event) { case 'update': this.emit('update', mes, ch); break; case 'notification': this.emit('notification', mes, ch); break; case 'conversation': this.emit('conversation', mes, ch); break; case 'delete': this.emit('delete', payload); break; case 'status.update': this.emit('status_update', mes, ch); break; default: this.emit('error', new Error("Unknown event has received: ".concat(message))); } }; return Parser; }(events_1.EventEmitter)); exports.Parser = Parser;