Skip to content

Commit 237d0c0

Browse files
authored
Merge pull request #25 from Bolado/feat/clean-unused-jars
Add cleanup procedure for unused Microbot clients
2 parents 6426d43 + 2d5fbd6 commit 237d0c0

6 files changed

Lines changed: 211 additions & 4 deletions

File tree

libs/dir-module.js

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,164 @@ async function openLocation(key) {
7575
}
7676
}
7777

78+
/**
79+
* Check for the existence of the clients_jar_ttl.json file in the microbot directory.
80+
* Creating with an empty object if it does not exist.
81+
* @return {Object} The parsed JSON object from the file.
82+
*/
83+
async function getClientsJarTTL() {
84+
const fs = require('fs').promises;
85+
const filePath = path.join(microbotDir, 'clients_jar_ttl.json');
86+
try {
87+
await fs.mkdir(microbotDir, { recursive: true });
88+
try {
89+
const data = await fs.readFile(filePath, 'utf8');
90+
return { success: true, data: JSON.parse(data) };
91+
} catch (readErr) {
92+
if (readErr.code === 'ENOENT') {
93+
await fs.writeFile(filePath, JSON.stringify({}));
94+
return { success: true, data: {} };
95+
}
96+
return { success: false, error: readErr.message };
97+
}
98+
} catch (err) {
99+
return { success: false, error: err.message };
100+
}
101+
}
102+
103+
/**
104+
* Cleanup routines for clients jar using the TTL data.
105+
* Delete any jar files that haven't been used in the past 3 days, with the exception
106+
* of the latest version.
107+
* @param {string} latestVersion The latest version string to exclude from deletion.
108+
* @return {Promise<{success: boolean, error?: string}>} Result object indicating success or failure.
109+
*/
110+
async function cleanupUnusedClientsJar(latestVersion) {
111+
const fs = require('fs').promises;
112+
const result = await getClientsJarTTL();
113+
if (!result.success) {
114+
return { success: false, error: result.error };
115+
}
116+
117+
if (!latestVersion) {
118+
return { success: false, error: 'Latest version not provided' };
119+
}
120+
121+
let ttlData = result.data || {};
122+
let updated = false;
123+
try {
124+
const files = await fs.readdir(microbotDir);
125+
const jarFiles = files.filter(
126+
(f) =>
127+
f.endsWith('.jar') &&
128+
f.startsWith('microbot-') &&
129+
!f.includes('launcher')
130+
);
131+
const now = Date.now();
132+
for (const jarFile of jarFiles) {
133+
let version = jarFile.replace('.jar', '');
134+
version = version.replace('microbot-', '');
135+
if (!(version in ttlData)) {
136+
ttlData[version] = now;
137+
updated = true;
138+
}
139+
}
140+
} catch (err) {
141+
return { success: false, error: err.message };
142+
}
143+
144+
const now = Date.now();
145+
const threeDays = 3 * 24 * 60 * 60 * 1000;
146+
let deletedAny = false;
147+
148+
for (const [version, lastUsed] of Object.entries(ttlData)) {
149+
if (version === latestVersion) {
150+
continue;
151+
}
152+
if (now - lastUsed > threeDays) {
153+
const jarPath = path.join(microbotDir, `microbot-${version}.jar`);
154+
try {
155+
await fs.unlink(jarPath);
156+
delete ttlData[version];
157+
deletedAny = true;
158+
updated = true;
159+
} catch (err) {
160+
if (err.code === 'ENOENT') {
161+
// File already gone: drop TTL entry and continue.
162+
delete ttlData[version];
163+
updated = true;
164+
continue;
165+
}
166+
return { success: false, error: err.message };
167+
}
168+
}
169+
}
170+
171+
// we return if nothing changed
172+
if (!updated) {
173+
return { success: true };
174+
}
175+
176+
const filePath = path.join(microbotDir, 'clients_jar_ttl.json');
177+
try {
178+
await fs.writeFile(filePath, JSON.stringify(ttlData, null, 2), 'utf8');
179+
} catch (err) {
180+
return { success: false, error: err.message };
181+
}
182+
183+
return { success: true };
184+
}
185+
186+
/**
187+
* Update the last used timestamp for a specific client version.
188+
* @param {string} version The client version to update.
189+
* @return {Promise<{success: boolean, error?: string}>} Result object indicating success or failure.
190+
*/
191+
async function updateClientJarTTL(version) {
192+
const fs = require('fs').promises;
193+
if (!version) {
194+
return { success: false, error: 'Version not provided' };
195+
}
196+
197+
if (!/^[a-zA-Z0-9._-]+$/.test(version)) {
198+
return { success: false, error: 'Invalid version format' };
199+
}
200+
201+
const jarPath = path.join(microbotDir, `microbot-${version}.jar`);
202+
try {
203+
await fs.stat(jarPath);
204+
} catch (e) {
205+
if (e.code === 'ENOENT') {
206+
return {
207+
success: false,
208+
error: `Jar not found for version: ${version}`
209+
};
210+
}
211+
return { success: false, error: e.message };
212+
}
213+
214+
const result = await getClientsJarTTL();
215+
if (!result.success) {
216+
return { success: false, error: result.error };
217+
}
218+
219+
const ttlData = result.data;
220+
ttlData[version] = Date.now();
221+
222+
const filePath = path.join(microbotDir, 'clients_jar_ttl.json');
223+
try {
224+
await fs.writeFile(filePath, JSON.stringify(ttlData, null, 2), 'utf8');
225+
} catch (err) {
226+
return { success: false, error: err.message };
227+
}
228+
229+
return { success: true };
230+
}
231+
78232
module.exports = {
79233
microbotDir,
80-
openLocation
234+
openLocation,
235+
getClientsJarTTL,
236+
cleanupUnusedClientsJar,
237+
updateClientJarTTL
81238
};

libs/ipc-handlers.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,4 +286,37 @@ module.exports = async function (deps) {
286286
}
287287
}
288288
);
289+
290+
/**
291+
* Cleans up unused client jar files in the microbotDir that haven't been used
292+
* in the past 3 days, except for the latest version which is always kept.
293+
* Uses a JSON file to track the last used timestamps for each version.
294+
* @param {string} latestVersion - The latest version string to exclude from deletion.
295+
* @returns {Object} Result object with success status or error message.
296+
*/
297+
ipcMain.handle(
298+
'cleanup-unused-clients-jar',
299+
async (event, latestVersion) => {
300+
const { cleanupUnusedClientsJar } = require(path.join(
301+
projectDir,
302+
'libs',
303+
'dir-module.js'
304+
));
305+
return await cleanupUnusedClientsJar(latestVersion);
306+
}
307+
);
308+
309+
/**
310+
* Updates the last used timestamp for a specific client version.
311+
* @param {string} version - The client version to update.
312+
* @returns {Object} Result object indicating success or failure.
313+
*/
314+
ipcMain.handle('update-client-jar-ttl', async (event, version) => {
315+
const { updateClientJarTTL } = require(path.join(
316+
projectDir,
317+
'libs',
318+
'dir-module.js'
319+
));
320+
return await updateClientJarTTL(version);
321+
});
289322
};

libs/properties.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ module.exports = async function (deps) {
55

66
// Define your default values here
77
const defaultProperties = {
8-
launcher: '0.0.0',
98
client: '0.0.0',
10-
launcher_html: '0.0.0',
119
version_pref: '0.0.0'
1210
};
1311

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "microbot-launcher",
3-
"version": "3.2.2",
3+
"version": "3.2.3",
44
"description": "Launcher for the microbot client",
55
"main": "main.js",
66
"scripts": {

preload.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ contextBridge.exposeInMainWorld('electron', {
4848
),
4949
openLocation: (locationKey) =>
5050
ipcRenderer.invoke('open-location', locationKey),
51+
cleanUnusedClients: (latestVersion) =>
52+
ipcRenderer.invoke('cleanup-unused-clients-jar', latestVersion),
53+
updateClientJarTTL: (version) =>
54+
ipcRenderer.invoke('update-client-jar-ttl', version),
5155
ipcRenderer: {
5256
send: (channel, data) => ipcRenderer.send(channel, data),
5357
receive: (channel, func) =>

renderer.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ async function openClient() {
3939
);
4040
if (selectedAccount) {
4141
try {
42+
const result = await window.electron.updateClientJarTTL(version);
43+
if (result?.error) {
44+
window.electron.logError(result.error);
45+
}
4246
await window.electron.overwriteCredentialProperties(
4347
selectedAccount
4448
);
@@ -234,6 +238,11 @@ window.addEventListener('load', async () => {
234238

235239
await window.electron.writeProperties(properties);
236240

241+
const result = await window.electron.cleanUnusedClients(clientVersion);
242+
if (result?.error) {
243+
window.electron.logError(result.error);
244+
}
245+
237246
/*
238247
* Whenever the profile select changes, we set the "preferred" profile on accounts.json
239248
* for the current selected account, if no jagex account is selected, we set on
@@ -509,6 +518,12 @@ function playNoJagexAccount() {
509518
const result = await downloadClientIfNotExist(version);
510519
if (result?.exists) {
511520
try {
521+
const result = await window.electron.updateClientJarTTL(
522+
version
523+
);
524+
if (result?.error) {
525+
window.electron.logError(result.error);
526+
}
512527
const playResult = await window.electron.playNoJagexAccount(
513528
version,
514529
proxy

0 commit comments

Comments
 (0)