Skip to content

Instantly share code, notes, and snippets.

@iczero
Last active January 4, 2019 00:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iczero/48f985c9e2f2c14bb3a487132d5a098f to your computer and use it in GitHub Desktop.
Save iczero/48f985c9e2f2c14bb3a487132d5a098f to your computer and use it in GitHub Desktop.
const repl = require('repl');
const LeagueClientAPI = require('./lol-client-api');
const http = require('http');
const util = require('util');
// set this to the path to LeagueClient.exe (example for linux)
const LEAGUE_EXE_PATH = process.env.HOME + '/Games/league-of-legends/drive_c/Riot Games/League of Legends/LeagueClient.exe';
// options for util.inspect when formatting responses
const INSPECT_OPTS = {
depth: Infinity,
colors: true
};
let api = new LeagueClientAPI(LEAGUE_EXE_PATH);
api
.on('connect', () => console.log('Found lockfile'))
.on('disconnect', () => console.log('Lockfile removed'))
.on('wsConnect', () => console.log('WebSocket events connected'))
.on('wsDisconnect', () => console.log('WebSocket events disconnected'))
.on('event', ev => {
console.log(`===== Event: ${ev.eventType} ${ev.uri}`);
console.log(util.inspect(ev.data, INSPECT_OPTS));
console.log('');
})
.on('error', err => console.error(err));
let replServer = repl.start({
breakEvalOnSigint: true
});
replServer.on('exit', () => process.exit(0));
replServer.context.api = api;
let request = function replDoRequest(method, endpoint, options) {
api.doRequest(
method,
endpoint,
Object.assign({}, {
transform(body, response) {
return { response, body };
},
simple: false
}, options)
)
.then(result => {
console.log(`===== Response: ${method} ${endpoint}`);
let res = result.response;
console.log(`HTTP/${res.httpVersion} ${res.statusCode} ${res.statusMessage || ''}`);
for (let [header, content] of Object.entries(res.headers)) {
console.log(`${header}: ${content}`);
}
console.log('');
console.log(util.inspect(result.body, INSPECT_OPTS));
console.log('');
});
};
for (let method of http.METHODS) {
request[method.toLowerCase()] = Function.prototype.bind.call(request, null, method);
}
replServer.context.request = request;
const LCUConnector = require('lcu-connector');
const WebSocket = require('ws');
const EventEmitter = require('events');
const request = require('request-promise');
const messageTypes = {
WELCOME: 0,
PREFIX: 1,
CALL: 2,
CALLRESULT: 3,
CALLERROR: 4,
SUBSCRIBE: 5,
UNSUBSCRIBE: 6,
PUBLISH: 7,
EVENT: 8
};
/** Represents an API client for the League of Legneds client API */
class LeagueClientAPI extends EventEmitter {
/**
* The constructor
* @param {String} exePath Path to LeagueClient.exe
*/
constructor(exePath) {
super();
this.exePath = exePath;
this.connector = new LCUConnector(exePath);
this.connectionData = null;
this.wsConnection = null;
this._init();
}
/**
* Initializes async stuff
*/
_init() {
this.connector.start();
this.connector.on('connect', this._onLockfileExists.bind(this));
this.connector.on('disconnect', this._onLockfileGone.bind(this));
}
/**
* Called internally when lcu-connector finds a lockfile
* @param {Object} data
*/
_onLockfileExists(data) {
this.emit('connect');
this.connectionData = data;
this.urlPrefix = `${data.protocol}://127.0.0.1:${data.port}`;
this.connectWs(data);
}
/**
* Called internally when the lockfile is deleted
*/
_onLockfileGone() {
this.connectionData = null;
this.urlPrefix = null;
this.emit('disconnect');
}
/**
* Attempt to connect to the websocket server
* @param {Object} data Connection data
*/
connectWs(data) {
if (!this.connectionData) return;
this.wsConnection = new WebSocket(`ws${data.protocol === 'https' ? 's' : ''}://127.0.0.1:${data.port}`, {
headers: {
'Authorization': 'Basic ' +
Buffer.from('riot:' + data.password).toString('base64')
},
rejectUnauthorized: false
});
this.wsConnection
.on('open', () => {
this.emit('wsConnect');
// subscribe to events
this.wsConnection.send(JSON.stringify([5, 'OnJsonApiEvent']));
})
.on('close', code => {
this.emit('wsDisconnect', code);
this.wsConnection = null;
})
.on('error', err => {
// because the server isn't up yet
this.emit('wsError', err);
// retry
setTimeout(() => this.connectWs(data), 1000);
})
.on('message', this._handleWsMessage.bind(this));
}
/**
* Handles a websocket message
* @param {Object} message
*/
_handleWsMessage(message) {
try {
message = JSON.parse(message);
} catch (err) {
// the first message sent is an empty string
}
let [type, ...data] = message;
if (!type) return;
switch (type) {
// TODO: deal with other types
case messageTypes.EVENT: {
let event = data[1];
this.emit('event', event);
this.emit('event-' + event.uri, event.eventType, event.data);
break;
}
}
}
/**
* Make an API request
* @param {String} method HTTP method
* @param {String} endpoint API endpoint
* @param {Object} options Additional options to request()
* @return {Object} The result of the request
*/
async doRequest(method, endpoint, options) {
return await request({
method,
url: this.urlPrefix + endpoint,
json: true,
headers: {
'Accept': 'application/json'
},
auth: {
user: 'riot',
pass: this.connectionData.password
},
rejectUnauthorized: false,
...options
});
}
}
LeagueClientAPI.messageTypes = messageTypes;
module.exports = LeagueClientAPI;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment