Skip to content

Commit 420db75

Browse files
committed
level edit command
1 parent 679d825 commit 420db75

File tree

2 files changed

+486
-2
lines changed

2 files changed

+486
-2
lines changed

commands/list/list.js

Lines changed: 258 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ module.exports = {
5656
.addIntegerOption(option =>
5757
option.setName('percent')
5858
.setDescription('The minimum percent players need to get a record on this level (list percent)')))
59-
6059
.addSubcommand(subcommand =>
6160
subcommand
6261
.setName('submit')
@@ -127,7 +126,52 @@ module.exports = {
127126
option.setName('level2')
128127
.setDescription('The name of the second level')
129128
.setAutocomplete(true)
130-
.setRequired(true))),
129+
.setRequired(true)))
130+
.addSubcommand(subcommand =>
131+
subcommand
132+
.setName('edit')
133+
.setDescription('Edit a level\'s info')
134+
.addStringOption(option =>
135+
option.setName('level')
136+
.setDescription('The name of the level to edit')
137+
.setAutocomplete(true)
138+
.setRequired(true))
139+
.addStringOption(option =>
140+
option.setName('levelname')
141+
.setDescription('The name of the level to place'))
142+
.addIntegerOption(option =>
143+
option.setName('position')
144+
.setDescription('The position to place the level at'))
145+
.addIntegerOption(option =>
146+
option.setName('difficulty')
147+
.setDescription('The tier the level is in (1-10, see the list website for details)'))
148+
.addIntegerOption(option =>
149+
option.setName('id')
150+
.setDescription('The GD ID of the level to place'))
151+
.addStringOption(option =>
152+
option.setName('uploader')
153+
.setDescription('The name of the person who uploaded the level on GD'))
154+
.addStringOption(option =>
155+
option.setName('verifier')
156+
.setDescription('The name of the verifier'))
157+
.addStringOption(option =>
158+
option.setName('verification')
159+
.setDescription('The link to the level\'s verification video'))
160+
.addStringOption(option =>
161+
option.setName('songname')
162+
.setDescription('The name of this level\'s song'))
163+
.addStringOption(option =>
164+
option.setName('songlink')
165+
.setDescription('The NONG link for this level, if any.'))
166+
.addStringOption(option =>
167+
option.setName('creators')
168+
.setDescription('The list of the creators of the level, each separated by a comma'))
169+
.addStringOption(option =>
170+
option.setName('password')
171+
.setDescription('The GD password of the level to place'))
172+
.addIntegerOption(option =>
173+
option.setName('percent')
174+
.setDescription('The minimum percent players need to get a record on this level (list percent)'))),
131175
async autocomplete(interaction) {
132176
const focused = interaction.options.getFocused();
133177
const { cache } = require('../../index.js');
@@ -224,6 +268,218 @@ module.exports = {
224268
return;
225269
} else if (interaction.options.getSubcommand() === 'submit') {
226270
return await interaction.editReply('Not implemented yet');
271+
} else if (interaction.options.getSubcommand() === 'edit') {
272+
const { db, cache } = require('../../index.js');
273+
const { octokit } = require('../../index.js');
274+
const level = interaction.options.getString('level') || null;
275+
const levelname = interaction.options.getString('levelname') || null;
276+
const id = interaction.options.getInteger('id') || null;
277+
const uploaderName = interaction.options.getString('uploader') || null;
278+
const verifierName = interaction.options.getString('verifier') || null;
279+
const verification = interaction.options.getString('verification') || null;
280+
const password = interaction.options.getString('password') || null;
281+
const rawCreators = interaction.options.getString('creators') || null;
282+
const creatorNames = rawCreators ? rawCreators.split(',') : [];
283+
const percent = interaction.options.getInteger('percent') || null;
284+
const difficulty = interaction.options.getInteger('difficulty') || null;
285+
const songName = interaction.options.getString('songname') || null;
286+
const songLink = interaction.options.getString('songlink') || null;
287+
288+
const levelToEdit = await cache.levels.findOne({ where: { filename: level } });
289+
const filename = levelToEdit.filename;
290+
let fileResponse;
291+
try {
292+
fileResponse = await octokit.rest.repos.getContent({
293+
owner: githubOwner,
294+
repo: githubRepo,
295+
path: githubDataPath + `/${filename}.json`,
296+
branch: githubBranch,
297+
});
298+
} catch (fetchError) {
299+
logger.info(`Couldn't fetch ${filename}.json: \n${fetchError}`);
300+
return await interaction.editReply(`:x: Couldn't fetch ${filename}.json: \n${fetchError}`);
301+
}
302+
303+
let parsedData;
304+
try {
305+
parsedData = JSON.parse(Buffer.from(fileResponse.data.content, 'base64').toString('utf-8'));
306+
} catch (parseError) {
307+
logger.info(`Unable to parse data fetched from ${filename}:\n${parseError}`);
308+
return await interaction.editReply(`:x: Unable to parse data fetched from ${filename}:\n${parseError}`);
309+
}
310+
311+
if (levelname !== null) parsedData.name = levelname;
312+
if (id !== null) parsedData.id = id;
313+
if (uploaderName !== null) parsedData.author = uploaderName;
314+
if (verifierName !== null) parsedData.verifier = verifierName;
315+
if (verification !== null) parsedData.verification = verification;
316+
if (password !== null) parsedData.password = password;
317+
if (creatorNames.length > 0) parsedData.creators = creatorNames;
318+
if (percent !== null) parsedData.percentToQualify = percent;
319+
if (difficulty !== null) parsedData.difficulty = difficulty;
320+
if (songName !== null) parsedData.song = songName;
321+
if (songLink !== null) parsedData.songLink = songLink;
322+
323+
let existing = true;
324+
325+
if (levelname !== null || id !== null || uploaderName !== null || verifierName !== null || verification !== null || password !== null || creatorNames.length > 0 || percent !== null || difficulty !== null || songName !== null || songLink !== null) existing = false;
326+
327+
if (existing) {
328+
return await interaction.editReply('You didn\'t change anything');
329+
}
330+
await interaction.editReply("Committing...");
331+
332+
// not sure why it needs to be done this way but :shrug:
333+
let changes = [];
334+
changes.push({
335+
path: githubDataPath + `/${filename}.json`,
336+
content: JSON.stringify(parsedData, null, '\t'),
337+
})
338+
339+
const changePath = githubDataPath + `/${filename}.json`
340+
const content = JSON.stringify(parsedData);
341+
342+
const debugStatus = await db.infos.findOne({ where: { name: 'commitdebug' } });
343+
if (!debugStatus || !debugStatus.status) {
344+
let commitSha;
345+
try {
346+
// Get the SHA of the latest commit from the branch
347+
const { data: refData } = await octokit.git.getRef({
348+
owner: githubOwner,
349+
repo: githubRepo,
350+
ref: `heads/${githubBranch}`,
351+
});
352+
commitSha = refData.object.sha;
353+
} catch (getRefError) {
354+
logger.info(`Something went wrong while fetching the latest commit SHA:\n${getRefError}`);
355+
await db.messageLocks.destroy({ where: { discordid: interaction.message.id } });
356+
return await interaction.editReply(':x: Something went wrong while commiting the records to github, please try again later (getRefError)');
357+
}
358+
let treeSha;
359+
try {
360+
// Get the commit using its SHA
361+
const { data: commitData } = await octokit.git.getCommit({
362+
owner: githubOwner,
363+
repo: githubRepo,
364+
commit_sha: commitSha,
365+
});
366+
treeSha = commitData.tree.sha;
367+
} catch (getCommitError) {
368+
logger.info(`Something went wrong while fetching the latest commit:\n${getCommitError}`);
369+
await db.messageLocks.destroy({ where: { discordid: interaction.message.id } });
370+
return await interaction.editReply(':x: Something went wrong while commiting the records to github, please try again later (getCommitError)');
371+
}
372+
373+
let newTree;
374+
try {
375+
// Create a new tree with the changes
376+
newTree = await octokit.git.createTree({
377+
owner: githubOwner,
378+
repo: githubRepo,
379+
base_tree: treeSha,
380+
tree: changes.map(change => ({
381+
path: change.path,
382+
mode: '100644',
383+
type: 'blob',
384+
content: change.content,
385+
})),
386+
});
387+
} catch (createTreeError) {
388+
logger.info(`Something went wrong while creating a new tree:\n${createTreeError}`);
389+
await db.messageLocks.destroy({ where: { discordid: interaction.message.id } });
390+
return await interaction.editReply(':x: Something went wrong while commiting the records to github, please try again later (createTreeError)');
391+
}
392+
393+
let newCommit;
394+
try {
395+
// Create a new commit with this tree
396+
newCommit = await octokit.git.createCommit({
397+
owner: githubOwner,
398+
repo: githubRepo,
399+
message: `Updated info for ${levelToEdit.name})`,
400+
tree: newTree.data.sha,
401+
parents: [commitSha],
402+
});
403+
} catch (createCommitError) {
404+
logger.info(`Something went wrong while creating a new commit:\n${createCommitError}`);
405+
await db.messageLocks.destroy({ where: { discordid: interaction.message.id } });
406+
return await interaction.editReply(':x: Something went wrong while commiting the records to github, please try again later (createCommitError)');
407+
}
408+
409+
try {
410+
// Update the branch to point to the new commit
411+
await octokit.git.updateRef({
412+
owner: githubOwner,
413+
repo: githubRepo,
414+
ref: `heads/${githubBranch}`,
415+
sha: newCommit.data.sha,
416+
});
417+
} catch (updateRefError) {
418+
logger.info(`Something went wrong while updating the branch :\n${updateRefError}`);
419+
await db.messageLocks.destroy({ where: { discordid: interaction.message.id } });
420+
return await interaction.editReply(':x: Something went wrong while commiting the records to github, please try again later (updateRefError)');
421+
}
422+
logger.info(`Successfully created commit on ${githubBranch} (record addition): ${newCommit.data.sha}`);
423+
await interaction.editReply("This record has been added!");
424+
} else {
425+
let updatedFiles = 0;
426+
let i = 1;
427+
// Get file SHA
428+
let fileSha;
429+
try {
430+
const response = await octokit.repos.getContent({
431+
owner: githubOwner,
432+
repo: githubRepo,
433+
path: changePath,
434+
});
435+
fileSha = response.data.sha;
436+
} catch (error) {
437+
logger.info(`Error fetching ${changePath} SHA:\n${error}`);
438+
erroredRecords.push(`All from ${changePath}`);
439+
return await interaction.editReply(`:x: Couldn't fetch data from ${changePath}`);
440+
i++;
441+
442+
}
443+
444+
try {
445+
await octokit.repos.createOrUpdateFileContents({
446+
owner: githubOwner,
447+
repo: githubRepo,
448+
path: changePath,
449+
message: `Updated ${changePath} (${interaction.user.tag})`,
450+
content: Buffer.from(content).toString('base64'),
451+
sha: fileSha,
452+
});
453+
logger.info(`Updated ${changePath} (${interaction.user.tag}`);
454+
} catch (error) {
455+
logger.info(`Failed to update ${changePath} (${interaction.user.tag}):\n${error}`);
456+
erroredRecords.push(`All from ${changePath}`);
457+
await interaction.editReply(`:x: Couldn't update the file ${changePath}, skipping...`);
458+
}
459+
updatedFiles++;
460+
i++;
461+
462+
let detailedErrors = '';
463+
for (const err of erroredRecords) detailedErrors += `\n${err}`;
464+
465+
const replyEmbed = new EmbedBuilder()
466+
.setColor(0x8fce00)
467+
.setTitle(':white_check_mark: Commit successful')
468+
.setDescription(`Successfully updated ${updatedFiles}/ files`)
469+
.addFields(
470+
{ name: 'Duplicates found:', value: `**${duplicateRecords}**`, inline: true },
471+
{ name: 'Errors:', value: `${erroredRecords.length}`, inline: true },
472+
{ name: 'Detailed Errors:', value: (detailedErrors.length == 0 ? 'None' : detailedErrors) },
473+
)
474+
.setTimestamp();
475+
await interaction.message.delete();
476+
await interaction.editReply(':white_check_mark: The record has been accepted');
477+
}
478+
479+
logger.info(`${interaction.user.tag} (${interaction.user.id}) submitted ${interaction.options.getString('levelname')} for ${interaction.options.getString('username')}`);
480+
// Reply
481+
await interaction.editReply(`:white_check_mark: ${levelToEdit.name} has been edited successfully`);
482+
return;
227483
} else if (interaction.options.getSubcommand() === 'move') {
228484
const { db, octokit } = require('../../index.js');
229485

0 commit comments

Comments
 (0)