UNPKG

12.4 kBJavaScriptView Raw
1'use strict';
2
3function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4require("core-js/modules/es.symbol.js");
5require("core-js/modules/es.symbol.description.js");
6require("core-js/modules/es.symbol.iterator.js");
7require("core-js/modules/es.symbol.to-primitive.js");
8require("core-js/modules/es.array.iterator.js");
9require("core-js/modules/es.date.to-primitive.js");
10require("core-js/modules/es.number.constructor.js");
11require("core-js/modules/es.object.create.js");
12require("core-js/modules/es.object.define-property.js");
13require("core-js/modules/es.object.get-prototype-of.js");
14require("core-js/modules/es.reflect.construct.js");
15require("core-js/modules/es.string.iterator.js");
16require("core-js/modules/web.dom-collections.iterator.js");
17require("core-js/modules/es.array.for-each.js");
18require("core-js/modules/es.array.slice.js");
19require("core-js/modules/es.date.now.js");
20require("core-js/modules/es.date.to-string.js");
21require("core-js/modules/es.function.bind.js");
22require("core-js/modules/es.object.set-prototype-of.js");
23require("core-js/modules/es.object.to-string.js");
24require("core-js/modules/es.regexp.to-string.js");
25require("core-js/modules/web.dom-collections.for-each.js");
26require("core-js/modules/web.timers.js");
27function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
28function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
29function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
30function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
31function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
32function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
33function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
34function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
35function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
36function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
37function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
38function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
39var _ = {
40 pull: require('lodash/pull')
41};
42var EventEmitter = require('eventemitter3');
43var ircLineParser = require('./irclineparser');
44module.exports = /*#__PURE__*/function (_EventEmitter) {
45 function Connection(options) {
46 var _this;
47 _classCallCheck(this, Connection);
48 _this = _callSuper(this, Connection);
49 _this.options = options || {};
50 _this.connected = false;
51 _this.requested_disconnect = false;
52 _this.reconnect_attempts = 0;
53
54 // When an IRC connection was successfully registered.
55 _this.registered = false;
56 _this.transport = null;
57 _this._timers = [];
58 return _this;
59 }
60 _inherits(Connection, _EventEmitter);
61 return _createClass(Connection, [{
62 key: "debugOut",
63 value: function debugOut(out) {
64 this.emit('debug', out);
65 }
66 }, {
67 key: "registeredSuccessfully",
68 value: function registeredSuccessfully() {
69 this.registered = Date.now();
70 }
71 }, {
72 key: "connect",
73 value: function connect(options) {
74 var that = this;
75 if (options) {
76 this.options = options;
77 }
78 options = this.options;
79 this.auto_reconnect = options.auto_reconnect || false;
80 this.auto_reconnect_max_retries = options.auto_reconnect_max_retries || 3;
81 this.auto_reconnect_max_wait = options.auto_reconnect_max_wait || 300000;
82 if (this.transport) {
83 this.clearTimers();
84 this.transport.removeAllListeners();
85 this.transport.disposeSocket();
86 }
87 this.transport = new options.transport(options);
88 if (!options.encoding || !this.setEncoding(options.encoding)) {
89 this.setEncoding('utf8');
90 }
91 bindTransportEvents(this.transport);
92 this.registered = false;
93 this.requested_disconnect = false;
94 this.emit('connecting');
95 this.transport.connect();
96 function bindTransportEvents(transport) {
97 transport.on('open', socketOpen);
98 transport.on('line', socketLine);
99 transport.on('close', socketClose);
100 transport.on('debug', transportDebug);
101 transport.on('extra', transportExtra);
102 }
103 function transportDebug(out) {
104 that.debugOut(out);
105 }
106 function transportExtra() {
107 // Some transports may emit extra events
108 that.emit.apply(that, arguments);
109 }
110
111 // Called when the socket is connected and ready to start sending/receiving data.
112 function socketOpen() {
113 that.debugOut('Socket fully connected');
114 that.reconnect_attempts = 0;
115 that.connected = true;
116 that.emit('socket connected');
117 }
118 function socketLine(line) {
119 that.addReadBuffer(line);
120 }
121 function socketClose(err) {
122 var was_connected = that.connected;
123 var should_reconnect = false;
124 var safely_registered = false;
125 var registered_ms_ago = Date.now() - that.registered;
126
127 // Some networks use aKills which kill a user after succesfully
128 // registering instead of a ban, so we must wait some time after
129 // being registered to be sure that we are connected properly.
130 safely_registered = that.registered !== false && registered_ms_ago > 5000;
131 that.debugOut('Socket closed. was_connected=' + was_connected + ' safely_registered=' + safely_registered + ' requested_disconnect=' + that.requested_disconnect);
132 that.connected = false;
133 that.clearTimers();
134 that.emit('socket close', err);
135 if (that.requested_disconnect || !that.auto_reconnect) {
136 should_reconnect = false;
137
138 // If trying to reconnect, continue with it
139 } else if (that.reconnect_attempts && that.reconnect_attempts < that.auto_reconnect_max_retries) {
140 should_reconnect = true;
141
142 // If we were originally connected OK, reconnect
143 } else if (was_connected && safely_registered) {
144 should_reconnect = true;
145 } else {
146 should_reconnect = false;
147 }
148 if (should_reconnect) {
149 var reconnect_wait = that.calculateExponentialBackoff();
150 that.reconnect_attempts++;
151 that.emit('reconnecting', {
152 attempt: that.reconnect_attempts,
153 max_retries: that.auto_reconnect_max_retries,
154 wait: reconnect_wait
155 });
156 that.debugOut('Scheduling reconnect. Attempt: ' + that.reconnect_attempts + '/' + that.auto_reconnect_max_retries + ' Wait: ' + reconnect_wait + 'ms');
157 that.setTimeout(function () {
158 return that.connect();
159 }, reconnect_wait);
160 } else {
161 that.transport.removeAllListeners();
162 that.emit('close', !!err);
163 that.reconnect_attempts = 0;
164 }
165 }
166 }
167 }, {
168 key: "calculateExponentialBackoff",
169 value: function calculateExponentialBackoff() {
170 var jitter = 1000 + Math.floor(Math.random() * 5000);
171 var attempts = Math.min(this.reconnect_attempts, 30);
172 var time = 1000 * Math.pow(2, attempts);
173 return Math.min(time, this.auto_reconnect_max_wait) + jitter;
174 }
175 }, {
176 key: "addReadBuffer",
177 value: function addReadBuffer(line) {
178 if (!line) {
179 // Empty line
180 return;
181 }
182 this.emit('raw', {
183 line: line,
184 from_server: true
185 });
186 var message = ircLineParser(line);
187 if (!message) {
188 return;
189 }
190 this.emit('message', message, line);
191 }
192 }, {
193 key: "write",
194 value: function write(data, callback) {
195 if (!this.connected || this.requested_disconnect) {
196 this.debugOut('write() called when not connected');
197 if (callback) {
198 setTimeout(callback, 0); // fire in next tick
199 }
200 return false;
201 }
202 this.emit('raw', {
203 line: data,
204 from_server: false
205 });
206 return this.transport.writeLine(data, callback);
207 }
208
209 /**
210 * Create and keep track of all timers so they can be easily removed
211 */
212 }, {
213 key: "setTimeout",
214 value: function (_setTimeout) {
215 function setTimeout() {
216 return _setTimeout.apply(this, arguments);
217 }
218 setTimeout.toString = function () {
219 return _setTimeout.toString();
220 };
221 return setTimeout;
222 }(function /* fn, length, argN */
223 () {
224 var that = this;
225 var tmr = null;
226 var args = Array.prototype.slice.call(arguments, 0);
227 var callback = args[0];
228 args[0] = function () {
229 _.pull(that._timers, tmr);
230 callback.apply(null, args);
231 };
232 tmr = setTimeout.apply(null, args);
233 this._timers.push(tmr);
234 return tmr;
235 })
236 }, {
237 key: "clearTimeout",
238 value: function (_clearTimeout) {
239 function clearTimeout(_x) {
240 return _clearTimeout.apply(this, arguments);
241 }
242 clearTimeout.toString = function () {
243 return _clearTimeout.toString();
244 };
245 return clearTimeout;
246 }(function (tmr) {
247 clearTimeout(tmr);
248 _.pull(this._timers, tmr);
249 })
250 }, {
251 key: "clearTimers",
252 value: function clearTimers() {
253 this._timers.forEach(function (tmr) {
254 clearTimeout(tmr);
255 });
256 this._timers = [];
257 }
258
259 /**
260 * Close the connection to the IRCd after forcing one last line
261 */
262 }, {
263 key: "end",
264 value: function end(data, had_error) {
265 var that = this;
266 this.debugOut('Connection.end() connected=' + this.connected + ' with data=' + !!data + ' had_error=' + !!had_error);
267 if (this.connected && data) {
268 // Once the last bit of data has been sent, then re-run this function to close the socket
269 this.write(data, function () {
270 that.end(null, had_error);
271 });
272 return;
273 }
274
275 // Shutdowns of the connection may be caused by errors like ping timeouts, which
276 // are not requested by the user so we leave requested_disconnect as false to make sure any
277 // reconnects happen.
278 if (!had_error) {
279 this.requested_disconnect = true;
280 this.clearTimers();
281 }
282 if (this.transport) {
283 this.transport.close(!!had_error);
284 }
285 }
286 }, {
287 key: "setEncoding",
288 value: function setEncoding(encoding) {
289 this.debugOut('Connection.setEncoding() encoding=' + encoding);
290 if (this.transport) {
291 return this.transport.setEncoding(encoding);
292 }
293 }
294 }]);
295}(EventEmitter);
\No newline at end of file