Skip to content

Commit

Permalink
Merge pull request #38 from Arcoz0308/feature/primes
Browse files Browse the repository at this point in the history
Feature/primes and update to discord.js v14.12
  • Loading branch information
Arcoz0308 authored Aug 3, 2023
2 parents b8f5296 + a018885 commit 44d8b8d
Show file tree
Hide file tree
Showing 21 changed files with 470 additions and 21 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@
"vitest": "^0.31.4"
},
"dependencies": {
"@google-cloud/local-auth": "2.1.0",
"@prisma/client": "^4.15.0",
"@swc-node/register": "^1.6.5",
"cron": "^2.4.0",
"dayjs": "^1.11.8",
"discord.js": "^14.11.0",
"discord.js": "^14.12.1",
"dotenv": "^16.1.4",
"google-auth-library": "^9.0.0",
"googleapis": "105",
"i": "^0.3.7",
"npm": "^9.7.1",
"rustic-error": "^0.2.1",
Expand Down
5 changes: 4 additions & 1 deletion src/commands/globals/other/admin/admin.builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ export const builder = new SlashCommandBuilder()
.setDescription(config.subcmds.reverseXpMovement.description)
.addIntegerOption(option => option.setName(config.subcmds.reverseXpMovement.options.id.name)
.setDescription(config.subcmds.reverseXpMovement.options.id.description)
.setRequired(true)));
.setRequired(true)))
.addSubcommand(subCommand => subCommand
.setName(config.subcmds.primeStaff.name)
.setDescription(config.subcmds.primeStaff.description));
4 changes: 3 additions & 1 deletion src/commands/globals/other/admin/admin.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import {builder} from './admin.builder';
import {commandsConfig} from '$core/config/message/command';
import {ReverseXpMovement} from '$core/commands/globals/other/admin/reverse_xp_movement/reverse_xp_movement.class';
import {Dev} from '$core/utils/dev';
import {PrimeStaff} from '$core/commands/globals/other/admin/prime_staff/prime_staff.class';

@Dev
export default class Admin extends BaseCommand {
builder = builder.toJSON();

getSubCommands(): SubCommandOptions {
return {
[commandsConfig.admin.subcmds.reverseXpMovement.name]: new ReverseXpMovement()
[commandsConfig.admin.subcmds.reverseXpMovement.name]: new ReverseXpMovement(),
[commandsConfig.admin.subcmds.primeStaff.name]: new PrimeStaff()
};
}
}
18 changes: 18 additions & 0 deletions src/commands/globals/other/admin/admin.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type {ChatInputCommandInteraction} from 'discord.js';
import type {Result} from 'rustic-error';
import {ok} from 'rustic-error';
import type {CommandError} from '$core/utils/error';
import {globalConfig} from '$core/config/global';
import {sendCommandReply} from '$core/handlers/commands';
import {errorEmbed} from '$core/utils/discord';
import {commandsConfig} from '$core/config/message/command';

/**
* If is owner, return false for code facility
*/
export const ownerOnly = async (interaction: ChatInputCommandInteraction, defer = true): Promise<Result<boolean, CommandError>> => {
if (!globalConfig.owners.includes(interaction.user.id)) {
return sendCommandReply(interaction, {embeds: [errorEmbed(commandsConfig.admin.exec.ownerOnly)]}, defer);
}
return ok(false);
};
89 changes: 89 additions & 0 deletions src/commands/globals/other/admin/prime_staff/prime_staff.class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {SubCommand} from '$core/handlers/commands';
import {commandsConfig} from '$core/config/message/command';
import type {Result} from 'rustic-error';
import {error, ok, resultify} from 'rustic-error';
import {CommandError} from '$core/utils/error';
import type {ChatInputCommandInteraction, EmbedBuilder} from 'discord.js';
import {userMention} from 'discord.js';
import {ownerOnly} from '$core/commands/globals/other/admin/admin.util';
import {getPrimes} from '$core/commands/globals/other/admin/prime_staff/prime_staff.util';
import {errorEmbed, simpleEmbed} from '$core/utils/discord';
import {msgParams} from '$core/utils/function/string';
import {confirmIds, getConfirmButtons} from '$core/handlers/buttons/confirm';

const config = commandsConfig.admin.exec.primeStaff;

export class PrimeStaff extends SubCommand {
name = commandsConfig.admin.subcmds.primeStaff.name;
preReply = {
enable: true,
ephemeral: false
};

async run(interaction: ChatInputCommandInteraction): Promise<Result<boolean, CommandError>> {
const ownerOnlyResult = await ownerOnly(interaction);
if (!ownerOnlyResult.ok) {
return error(ownerOnlyResult.error);
}
if (ownerOnlyResult.value) {
return ok(false);
}

const primeResult = await getPrimes();
if (!primeResult.ok) {
return error(new CommandError('failed to get staff primes', interaction, primeResult.error));
}

if (typeof primeResult.value === 'string') {
return this.sendReply(interaction, {embeds: [errorEmbed(primeResult.value)]});
}

const infos: string[] = [];
for (const prime of primeResult.value) {
infos.push(msgParams(config.primeInfo, [
userMention(prime.userId),
prime.role,
prime.username,
prime.totalPrime,
prime.prime,
prime.associationPrime
]));
}

const embeds: EmbedBuilder[] = [];


// thx https://stackoverflow.com/a/8495740
const chunkSize = 30;
for (let i = 0; i < infos.length; i += chunkSize) {
const chunk = infos.slice(i, i + chunkSize);
embeds.push(simpleEmbed(chunk.join('\n')));
}

const replyResult = await this.sendReply(interaction, {
embeds: [embeds[0].setTitle(config.primeInfoTitle)]
});
if (!replyResult.ok) {
return error(replyResult.error);
}

for (const embed of embeds.slice(1)) {
const result = await resultify(() => interaction.followUp({
embeds: [embed]
}));
if (!result.ok) {
return error(new CommandError('failed to followUp interaction', interaction, result.error));
}
}

const result = await resultify(() => interaction.followUp({
embeds: [simpleEmbed(config.primeDescription)],
components: getConfirmButtons(confirmIds.primeStaff)
}));
if (!result.ok) {
return error(new CommandError('failed to followUp interaction', interaction, result.error));
}
return ok(true);

}
}
10 changes: 10 additions & 0 deletions src/commands/globals/other/admin/prime_staff/prime_staff.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type {Snowflake} from 'discord-api-types/globals';

export type PrimeInfos = {
username: string;
role: string;
prime: number;
associationPrime: number;
totalPrime: number;
userId: Snowflake;
}
95 changes: 95 additions & 0 deletions src/commands/globals/other/admin/prime_staff/prime_staff.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {google} from 'googleapis';
import {env} from '$core/config/env';
import type {PrimeInfos} from '$core/commands/globals/other/admin/prime_staff/prime_staff.type';
import {msgParams} from '$core/utils/function/string';
import {commandsConfig} from '$core/config/message/command';
import type {Result} from 'rustic-error';
import {error, ok} from 'rustic-error';
import {anyToError} from '$core/utils/error';

let lastSavedPrimes: PrimeInfos[] | undefined;

export const getSavedPrimes = () => lastSavedPrimes;

const getIndexes = (header: string[]): Record<keyof PrimeInfos, number> | string => {
const baseIndexes = {
'Adhesion Asso': header.findIndex(v => v === 'Adhésion Asso'),
'Primes': header.findIndex(v => v === 'Primes'),
'Poste': header.findIndex(v => v === 'Poste'),
'TOTAL Sans Bonus': header.findIndex(v => v === 'TOTAL Sans Bonus'),
'Idd': header.findIndex(v => v === 'Idd'),
'Pseudo': header.findIndex(v => v === 'Pseudo')
};
let msg = '';
for (const [key, value] of Object.entries(baseIndexes)) {
if (value < 0) {
msg += `${msgParams(commandsConfig.admin.exec.primeStaff.columnNotFound, [key])}\n`;
}
}
if (msg !== '') {
return msg;
}

return {
associationPrime: baseIndexes['Adhesion Asso'],
prime: baseIndexes.Primes,
role: baseIndexes.Poste,
totalPrime: baseIndexes['TOTAL Sans Bonus'],
userId: baseIndexes.Idd,
username: baseIndexes.Pseudo
};
};

export const getPrimes = async (): Promise<Result<PrimeInfos[] | string, Error>> => {
try {
const auth = new google.auth.GoogleAuth({
scopes: [
'https://www.googleapis.com/auth/spreadsheets'
],
credentials: {
private_key: env.GOOGLE_PRIVATE_KEY.split(String.raw`\n`).join('\n'),
client_email: env.GOOGLE_MAIL
}
});
const sheets = google.sheets({
version: 'v4',
auth: auth
});
const res = await sheets.spreadsheets.values.get({
spreadsheetId: env.GOOGLE_SHEETS_ID,
range: 'Registre Staff!A:I'
});

if (res.data.values === null || typeof res.data.values === 'undefined' ||
res.data.values.length === 0 || res.data.values[0].length === 0) {

return error(new Error('No rows gets'));
}
const indexes = getIndexes(res.data.values[0]);
if (typeof indexes === 'string') {
return ok(indexes);
}

const primes: PrimeInfos[] = [];

for (const row of res.data.values.slice(1)) {
const prime: PrimeInfos = {
associationPrime: parseInt(row[indexes.associationPrime], 10),
prime: parseInt(row[indexes.prime], 10),
role: row[indexes.role],
totalPrime: parseInt(row[indexes.totalPrime], 10),
userId: row[indexes.userId],
username: row[indexes.username]
};
if (!Object.values(prime).includes('')) {
primes.push(prime);
}
}
lastSavedPrimes = primes;
return ok(primes);
} catch (e) {
return error(new Error(`failed to get prime staff infos, error : ${anyToError(e).message}`));
}
};


Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import type {ChatInputCommandInteraction} from 'discord.js';
import {time, userMention} from 'discord.js';
import {CommandError} from '$core/utils/error';
import type {Result} from 'rustic-error';
import {error} from 'rustic-error';
import {error, ok} from 'rustic-error';
import {commandsConfig} from '$core/config/message/command';
import {getXpMovement} from '$core/handlers/database/xp_movement/xp_movement.func';
import {errorEmbed, simpleEmbed} from '$core/utils/discord';
import {msgParams} from '$core/utils/function/string';
import {confirmIds, getConfirmButtons} from '$core/handlers/buttons/confirm';
import {ownerOnly} from '$core/commands/globals/other/admin/admin.util';

const config = commandsConfig.admin;

Expand All @@ -21,6 +22,15 @@ export class ReverseXpMovement extends SubCommand {
};

async run(interaction: ChatInputCommandInteraction): Promise<Result<boolean, CommandError>> {

const ownerOnlyResult = await ownerOnly(interaction);
if (!ownerOnlyResult.ok) {
return error(ownerOnlyResult.error);
}
if (ownerOnlyResult.value) {
return ok(false);
}

const id = interaction.options.getInteger(config.subcmds.reverseXpMovement.options.id.name);
if (!id) {
return error(new CommandError('No value in option id', interaction));
Expand Down
5 changes: 4 additions & 1 deletion src/config/env/env.z.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ import {z} from 'zod';
export const envDTO = z.object({
TOKEN: z.string().nonempty(),
BRAWL_STARS_TOKEN: z.string().nonempty(),
GOOGLE_MAIL: z.string().email().nonempty(),
GOOGLE_PRIVATE_KEY: z.string().nonempty(),
GOOGLE_SHEETS_ID: z.string().nonempty(),
WEBHOOK_DISCORD_URL: z.string().nonempty()
});
});
10 changes: 9 additions & 1 deletion src/config/global/global.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ export const globalConfig = {
bumpBot: '302050872383242240',
bumpCommand: 'bump',
maxBump: 5,
xpPerBump: 100
xpPerBump: 100,
owners: [
'457144873859022858',
'712579966373724222',
'622389865765404684',
'615664538423001118',
'364010473559031811',
'276817823639273472'
]
};


Expand Down
7 changes: 4 additions & 3 deletions src/config/guilds/_dev/dev.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {BaseGuild, BrawlStarsGuild} from '$core/config/guilds/guild.type';
import type {BaseGuild, BrawlStarsGuild, GlobalGuild} from '$core/config/guilds/guild.type';
import {LevelUpRoleType} from '$core/config/guilds/guild.type';
import type {Overwrite} from '$core/utils/type/type';
import type {GuildAlias} from '$core/handlers/commands';
Expand Down Expand Up @@ -36,6 +36,7 @@ const devGuilds = {
}
]
},
primeChannel: '1096122859178426423'
},
guildSection: {
name: 'global',
Expand Down Expand Up @@ -93,9 +94,9 @@ const devGuilds = {
}
}
]
},
}
}
} satisfies Record<'guildMain' | 'guildSection', Overwrite<BrawlStarsGuild, {
} satisfies Record<'guildMain' | 'guildSection', Overwrite<BrawlStarsGuild & GlobalGuild, {
name: GuildAlias
}> | Overwrite<BaseGuild, { name: GuildAlias }>>;

Expand Down
4 changes: 4 additions & 0 deletions src/config/guilds/guild.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ export type BrawlStarsGuild = BaseGuild & {
clubs: BrawlStarsClub[];
}

export type GlobalGuild = BaseGuild & {
primeChannel: Snowflake;
};

export enum BrawlStarsClubType {
LADDER = 'Leader',
LDC = 'Ldc',
Expand Down
5 changes: 3 additions & 2 deletions src/config/guilds/guilds_configs/global/base.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {BaseGuild} from '../../guild.type';
import type {GlobalGuild} from '../../guild.type';
import {xpConfig} from './xp.config';
import {pubMessage} from './pub_message.config';

export const globalGuildConfig: BaseGuild = {
export const globalGuildConfig: GlobalGuild = {
name: 'global',
guildId: '346989473143455744',
eventAnnouncements: {
Expand All @@ -14,4 +14,5 @@ export const globalGuildConfig: BaseGuild = {
xp: xpConfig,
bumpChannel: '979400972315000852',
pubMessages: pubMessage,
primeChannel: '1083522288441368596'
};
Loading

0 comments on commit 44d8b8d

Please sign in to comment.