-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
208 lines (180 loc) · 10.2 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
const fs = require("fs");
const path = require("path");
const readline = require("readline");
const https = require("https");
const { exec } = require("child_process");
const readLastLines = require("read-last-lines");
const DiscordRPC = require("discord-rpc");
const zones = require(path.join(__dirname, "./zones.json"));
const settings = require(path.join(__dirname, "./settings.json"));
const package = require(path.join(__dirname, "./package.json"));
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
let rpc;
console.log(" _ _ __ ___ __ \n (_) | /_ |/ _ \\/_ | \n __ ___ ______ _ _ __ __| || | | | || |______ _ __ _ __ ___ \n \\ \\ /\\ / / |_ / _` | \'__/ _` || | | | || |______| \'__| \'_ \\ / __|\n \\ V V /| |/ / (_| | | | (_| || | |_| || | | | | |_) | (__ \n \\_/\\_/ |_/___\\__,_|_| \\__,_||_|\\___/ |_| |_| | .__/ \\___|\n | | \n ");
console.log("https://github.com/Bacon1661/wizard101-rpc\n");
function setPresence() {
let oldLineCount, zone, world, health, activity, currentTime;
(async () => { oldLineCount = await countLines(); })();
fs.watchFile(settings.path, { interval: 100 }, async (_curr, _prev) => {
let currentLineCount = await countLines();
readLastLines.read(settings.path, currentLineCount - oldLineCount).then(async (logChunk) => {
// Get zone
const zoneRegex = new RegExp(/zone = (.+),|CHARACTER LIST/gm);
let tempZone = logChunk.match(zoneRegex);
if(tempZone) {
tempZone = zoneRegex.exec(tempZone[tempZone.length - 1]);
zone = tempZone[1] || tempZone[0];
world = /= (.+?)\/(.+)/.exec(tempZone);
world = world && zones.zoneNames[world[1]] ? world[1] : "WizardCity";
}
// Get health
const healthRegex = new RegExp(/Updating health globe \(new health: (\d+), new health max: (\d+)\)\r\n(?!(.+)called for a player that is not this client's!)/gm); // /Updating health globe \(new health: (\d+), new health max: (\d+)\)\n(?!(.+)called for a player that is not this client's!)/gm
let tempHealth = logChunk.match(healthRegex);
if(tempHealth) {
health = healthRegex.exec(tempHealth[tempHealth.length - 1]);
health = health[2] < health[1] ? [health[1], health[1]] : [health[1], health[2]];
}
// Check if the game has been quit
if(logChunk.match(/GameClient::HandleQuit\(\)|Logout due to Away From Keyboard/) && activity && settings.startup !== "none") {
rpc.destroy();
process.exit();
}
}).catch((err) => { console.log(err); });
oldLineCount = currentLineCount;
if((!activity && zone) || activity && (activity.details !== zones[zone] || health && activity.state !== `Health: ${health[0]}/${health[1]}`)) {
if(!activity || activity.details !== zones[zone]) currentTime = new Date();
if(zone === "CHARACTER LIST") health = null;
activity = {
details: zones[zone],
state: zone === "CHARACTER LIST" ? undefined : `Health: ${health ? `${health[0]}/${health[1]}` : "unknown"}`,
startTimestamp: currentTime,
largeImageKey: world.toLowerCase(),
largeImageText: zones.zoneNames[world],
instance: false
};
rpc.setActivity(activity);
}
});
}
function countLines() {
return new Promise((resolve, reject) => {
let lineCount = 0;
fs.createReadStream(settings.path).on("data", (buffer) => {
let index = -1;
lineCount--;
do {
index = buffer.indexOf(10, index + 1);
lineCount++;
} while(index !== -1);
}).on("end", () => resolve(lineCount)).on("error", reject);
});
}
(function updates() {
console.log("Checking for updates...");
getJSON("https://raw.githubusercontent.com/Bacon1661/wizard101-rpc/master/package.json");
function getJSON(url, runConf) {
https.get(url, (res) => {
let body = "";
res.on("data", (data) => body += data);
res.on("end", async () => {
try {
let file;
file = JSON.parse(body);
if(runConf) {
console.log("Updating zones.json...");
if(file.zoneNames) await fs.writeFile(path.join(__dirname, "./zones.json"), JSON.stringify(file, null, 4), () => { /* do nothing */ });
configureSettings();
} else {
if(file.version !== package.version) console.log(`Version ${file.version} is available! Go here to download it: https://github.com/Bacon1661/wizard101-rpc/releases\n`);
getJSON("https://raw.githubusercontent.com/Bacon1661/wizard101-rpc/master/zones.json", true);
}
} catch(err) {
console.error(`Error accessing the GitHub repository: ${err.message}`);
if(runConf) return configureSettings();
getJSON("https://raw.githubusercontent.com/Bacon1661/wizard101-rpc/master/zones.json", true);
}
});
}).on("error", (err) => {
console.error(`Error accessing the GitHub repository: ${err.message}`);
if(runConf) return configureSettings();
getJSON("https://raw.githubusercontent.com/Bacon1661/wizard101-rpc/master/zones.json", true);
});
}
}());
async function configureSettings() {
const defaultPath = "C:\\ProgramData\\KingsIsle Entertainment\\Wizard101\\Bin\\WizardClient.log";
const defaultSteamPath = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Wizard101\\Bin\\WizardClient.log";
if(!settings.path || !fs.existsSync(settings.path)) {
settings.path === null;
const defaultValidPath = fs.existsSync(defaultPath) ? defaultPath : fs.existsSync(defaultSteamPath) ? defaultSteamPath : null;
if(defaultValidPath) {
settings.path = defaultValidPath;
await fs.writeFile(path.join(__dirname, "./settings.json"), JSON.stringify(settings), () => { /* do nothing */ });
console.log(`Fetching data for RPC from ${defaultValidPath.slice(0, defaultValidPath.length - 21)}\n`);
}
}
// handle command inputs
if(!settings.startup) {
console.log("No startup settings detected, using the 'quick' startup setting!\nIn the future, run this program instead and Wizard101-RPC will quick launch Wizard101 for you.\n\nYou can change this by using the 'startup' command.\nUsage: startup <quick|normal|none>\nExample Usage:\nstartup quick - Set Wizard101-RPC to quick launch (skip patching, instant login) Wizard101 on startup.\nstartup normal - Set Wizard101-RPC to launch Wizard101 normally on startup.\nstartup none - Set Wizard101-RPC to not launch Wizard101 on startup.\n");
settings.startup = "quick";
await fs.writeFile(path.join(__dirname, "./settings.json"), JSON.stringify(settings), () => { /* do nothing */ });
}
if(!settings.path) console.log("Could not find Wizard101 at a default path! Please enter a valid path to continue.\nExample path (does not have to be steam): D:\\Steam\\steamapps\\common\\Wizard101");
rl.on("line", async (input) => {
if(!settings.path) await pathCommand(null, input);
if(!rpc && settings.path) return connectDiscord();
let args = input.split(/\s+/g);
if(!args[0] || typeof args[0] !== "string") return;
args[0] = args[0].toLowerCase();
if(args[0] === "path") pathCommand(args);
if(args[0] === "startup") startupCommand(args);
});
async function pathCommand(args, init) {
if(args && !args[1]) return console.log("\nChange the path of where Wizard101-RPC will read information from if valid.\nUsage: path <path>\nExample Usage: path D:\\Steam\\steamapps\\common\\Wizard101");
if(!fs.existsSync(`${init || args[1]}\\Bin\\WizardClient.log`)) return console.log(init ? `\nCould not find Wizard101 at the path "${init || args[1]}".\nA valid path will need to be supplied to the Wizard101 folder before Wizard101-RPC can run properly. Make sure you've ran the game at least once. Please try again.\nExample path (does not have to be steam): D:\\Steam\\steamapps\\common\\Wizard101` : `\nCould not find Wizard101 at the path "${init || args[1]}".\nUsage: path <path>\nExample usage: path D:\\Steam\\steamapps\\common\\Wizard101`);
settings.path = `${init || args[1]}\\Bin\\WizardClient.log`;
await fs.writeFile(path.join(__dirname, "./settings.json"), JSON.stringify(settings), () => { /* do nothing */ });
console.log(`\nNew path set to "${init || args[1]}". If a path was already set, Wizard101-RPC won't read from the new path until its has been restarted.`);
}
async function startupCommand(args) {
if(!args[1]) return console.log("\nSets how Wizard101-RPC will start Wizard101, if at all.\nUsage: startup <quick|normal|none>\n\nExample Usage:\nstartup quick - Set Wizard101-RPC to quick launch Wizard101 on startup.\nstartup normal - Set Wizard101-RPC to launch Wizard101 normally on startup.\nstartup none - Set Wizard101-RPC to not launch Wizard101 on startup.");
args[1] = args[1].toLowerCase();
if(args[1] === "quick" || args[1] === "normal" || args[1] === "none") {
settings.startup = args[1];
await fs.writeFile(path.join(__dirname, "./settings.json"), JSON.stringify(settings), () => { /* do nothing */ });
console.log(`\nStartup settings set to '${args[1]}'.`);
} else {
console.log(`\nInvalid argument '${args[1]}'. Please use 'quick', 'normal', or 'none'.`);
}
}
if(settings.path) connectDiscord();
}
let reconnect;
function connectDiscord() {
console.log("\nConnecting to your Discord app...");
rl.pause();
rpc = new DiscordRPC.Client({ transport: "ipc" });
rpc.once("ready", () => {
console.log("Successfully connected to Discord!\nWizard101-RPC is ready! Information will be shown on your Discord profile when the game starts, or when the zone changes.");
clearInterval(reconnect);
setPresence();
rl.resume();
// Launch wiz
if(settings.startup === "none") return;
process.chdir(settings.path.slice(0, settings.path.length - (settings.startup === "quick" ? 17 : 21)));
settings.startup === "quick" ? exec("WizardGraphicalClient.exe -L 165.193.63.4 12000", (err) => console.log(`\nQuick launched Wizard101!\nIf you encounter an issue, try running Wizard101 normally.\n${err ? err : ""}`)) : settings.startup === "normal" ? exec("Wizard101.exe", _err /* an error will always be made, even when successful*/ => console.log("\nLaunched Wizard101!")) : null;
});
rpc.login({ clientId: "799404226081849345" }).then(() => {
clearInterval(reconnect);
}).catch((err) => {
console.log(`${err}\nMake sure Discord is running. You might need to restart Discord (Ctrl+R on Discord).`);
if(!reconnect && !rpc.user) {
reconnect = setInterval(connectDiscord, 10050);
connectDiscord();
}
});
}