mixer-client-node
Version:
A node client for connecting to mixer and the mixer services
241 lines (240 loc) • 8.25 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events");
const HelperFunctions_1 = require("../Util/HelperFunctions");
let WebSocket;
if (typeof window === 'undefined')
WebSocket = require('ws');
else
WebSocket = window.WebSocket;
class ConstellationService extends events_1.EventEmitter {
constructor(clientid) {
super();
this.clientid = clientid;
this.events = [];
this.subscribingTo = {};
this.unsubscribingTo = {};
this.currentId = 0;
this.shouldClose = false;
this.createSocket();
}
/*
* Create the constellation socket
*/
createSocket() {
this.shouldClose = false;
if (this.socket && this.socket.readyState === 1)
return;
this.currentId = 0;
this.socket = new WebSocket('wss://constellation.mixer.com', 'cnstl-gzip', {
headers: {
'client-id': this.clientid,
'x-is-bot': true,
'User-Agent': 'mixer-client-node / I dont do versions tbh',
},
});
this.eventListener();
if (this.events.length > 0) {
const subTo = this.events;
this.events = [];
this.subscribe(subTo);
}
}
start() {
this.createSocket();
}
stop() {
if (this.socket) {
this.shouldClose = true;
this.socket.close();
}
}
ping() {
if (this.timeout)
clearTimeout(this.timeout);
if (this.ensurePingTimeout)
clearTimeout(this.ensurePingTimeout);
this.timeout = setTimeout(() => {
if (this.socket.readyState !== 1)
this.emit('error', { socket: 'Closed', from: 'Ping' });
else {
if (this.currentId > 100000000)
this.currentId = 0;
this.pingId = ++this.currentId;
this.sendPacket('ping', null, this.pingId);
this.ensurePingTimeout = setTimeout(() => {
this.createSocket();
}, 1500); // ensure ping recieved in 1.5s
}
}, 1000 * 5); // send next ping in 5s from last revieved
}
/*
* Get the event's that you are subscribed to
* Returns a string array
*/
get subscribedEvents() {
return [...this.events];
}
/*
* Setup the socket event listener to emit events
*/
eventListener() {
this.socket.addEventListener('open', this.ping);
this.socket.addEventListener('error', (e) => {
if (this.listenerCount('error') > 0)
this.emit('error', e);
});
this.socket.addEventListener('close', () => {
if (this.shouldClose === false)
setTimeout(() => this.createSocket(), 500);
});
this.socket.addEventListener('message', ({ data: response }) => {
const data = HelperFunctions_1.toJSON(response);
if (data.event === 'hello')
return this.emit('open', data.data);
if (data.type === 'reply') {
if (data.id in this.subscribingTo) {
const events = this.subscribingTo[data.id];
delete this.subscribingTo[data.id];
if (data.error)
this.emit(data.type, {
result: data.result,
error: data.error,
events,
});
else {
// subscribe success
this.events = [...this.events, ...events];
this.emit('subscribe', { events });
}
}
else if (data.id in this.unsubscribingTo) {
const events = this.unsubscribingTo[data.id];
delete this.unsubscribingTo[data.id];
if (data.error)
this.emit(data.type, {
result: data.result,
error: data.error,
events,
});
else {
// unsubscribe success
this.events = this.events.filter((event) => !events.includes(event));
this.emit('unsubscribe', { events });
}
}
else if (data.id === this.pingId) {
if (data.error)
this.createSocket();
else
this.ping();
}
else
this.emit(data.type, data);
}
else if (data.data && data.data.payload) {
this.emit(data.type, data.data.payload, data.data.channel);
}
else {
this.emit(data.type, data);
}
});
}
/*
* Send the socket data
*/
sendPacket(method, params, id = 0) {
const packet = {
id,
method,
params,
type: 'method',
};
if (this.socket && this.socket.readyState === 1)
this.socket.send(JSON.stringify(packet));
else {
this.emit('warning', {
code: 2000,
events: params,
id: 1,
method,
reason: 'Socket Closed or No Socket Found',
warning: "Can't Send Packet",
});
}
}
/*
* Subscribe to an event
* Emits a subscribe event if successful
*/
subscribe(event) {
if (this.socket.readyState !== 1) {
this.once('open', () => {
this.subscribe(event);
});
}
else {
event = typeof event === 'string' ? [event] : event;
const originalEvents = event;
event = event.filter((name) => {
if (this.events.includes(name))
return false;
else {
for (const id in this.subscribingTo) {
if (this.subscribingTo.hasOwnProperty(id) &&
this.subscribingTo[id].includes(name))
return false;
}
return true;
}
});
if (event.length > 0) {
const id = ++this.currentId;
this.subscribingTo[id] = event;
this.sendPacket('livesubscribe', { events: event }, id);
}
else {
this.emit('warning', {
code: 2001,
events: originalEvents,
id: 1,
reason: 'You are already subscribed to the event(s) you said to subscribe to',
warning: "Can't Subscribe",
});
}
}
}
/*
* UbSubscribe to an event
* Emits an unsubscribe event if successful
*/
unsubscribe(event) {
event = typeof event === 'string' ? [event] : event;
event = event.filter((name) => this.events.includes(name));
if (event.length > 0 && this.socket.readyState !== 1) {
this.createSocket();
this.emit('error', {
code: 404,
events: event,
id: 1,
reason: 'The socket was closed',
warning: "Can't Send Packet",
});
}
else if (event.length === 0) {
this.emit('warning', {
code: 2000,
events: event,
id: 2,
reason: 'You are not subscribed to any of the events you listed to unsubscribe to',
warning: "Can't Send Packet",
});
}
else {
const id = ++this.currentId;
this.unsubscribingTo[id] = event;
this.sendPacket('liveunsubscribe', { events: event }, id);
}
}
}
exports.default = ConstellationService;