Skip to content

Commit

Permalink
Merge branch 'main' into ban-duration
Browse files Browse the repository at this point in the history
  • Loading branch information
theimperious1 authored Dec 23, 2024
2 parents 52491fa + b2c986c commit 175581d
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 70 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/PullRequestOpenAll.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ jobs:
NODE_OPTIONS: --max-old-space-size=7168
steps:
- uses: actions/checkout@v3
- name: Use Node.js '21.1.0'
- name: Use Node.js '22.9.0'
uses: actions/setup-node@v3
with:
node-version: '21.1.0'
node-version: '22.9.0'
cache: 'npm'
- name: Update npm
run: npm install -g npm && npm --version
Expand All @@ -43,10 +43,10 @@ jobs:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - name: Use Node.js '20.5.0'
# - name: Use Node.js '22.9.0'
# uses: actions/setup-node@v3
# with:
# node-version: '20.5.0'
# node-version: '22.9.0'
# cache: 'npm'
# - name: Update npm
# run: npm install -g npm && npm --version
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/PushToMain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:
NODE_OPTIONS: --max-old-space-size=7168
steps:
- uses: actions/checkout@v3
- name: Use Node.js '21.1.0'
- name: Use Node.js '22.9.0'
uses: actions/setup-node@v3
with:
node-version: '21.1.0'
node-version: '22.9.0'
cache: 'npm'
- name: Update npm
run: npm install -g npm && npm --version
Expand All @@ -37,10 +37,10 @@ jobs:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - name: Use Node.js '20.5.0'
# - name: Use Node.js '22.9.0'
# uses: actions/setup-node@v3
# with:
# node-version: '20.5.0'
# node-version: '22.9.0'
# cache: 'npm'
# - name: Update npm
# run: npm install -g npm && npm --version
Expand Down
54 changes: 48 additions & 6 deletions src/discord/commands/global/d.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,8 +652,22 @@ export async function helperButton(
}

// Do everything else

const role = await interaction.guild?.roles.fetch(guildData.role_helper);

const user = await db.users.findUnique({
where: {
discord_id: target.user.id,
},
});

const userHasBeenAHelper = user?.last_was_helper !== null;

if (!user) {
log.error(F, `No user found for discord_id: ${target.user.id}`);
return;
}

if (!role) {
await interaction.reply({
content: stripIndents`It looks like the guilds helper role was deleted, talk to the server admin about this! They may need to re-run \`/setup tripsit\``,
Expand All @@ -662,17 +676,32 @@ export async function helperButton(
return;
}

// If the role being requested is the Helper or Contributor role, check if they have been banned first
if (role.id === guildData.role_helper && userData.helper_role_ban) {
await interaction.editReply({ content: 'Unable to add this role. If you feel this is an error, please talk to the team!' });
return;
}

if (target.roles.cache.has(role.id)) {
await target.roles.remove(role);
await interaction.reply({
content: stripIndents`You already have the helper role!`,
content: stripIndents`Your helper role has been removed. If you ever want to re-apply it, just click the button again!`,
ephemeral: true,
});
return;
}

// If the role being requested is the Helper or Contributor role, check if they have been banned first
if (role.id === guildData.role_helper && userData.helper_role_ban) {
await interaction.editReply({ content: 'Unable to add this role. If you feel this is an error, please talk to the team!' });
if (userHasBeenAHelper && !target.roles.cache.has(role.id)) {
await target.roles.add(role);
if (interaction.guild.id === env.DISCORD_GUILD_ID) {
const channelTripsitters = await interaction.guild?.channels.fetch(env.CHANNEL_TRIPSITTERS) as TextChannel;
await channelTripsitters.send(stripIndents`${target.displayName} has re-joined as a ${role.name}.`);
}
const metaChannel = await interaction.guild?.channels.fetch(guildData.channel_tripsitmeta) as TextChannel;
await interaction.reply({
content: stripIndents`Welcome back, go check out ${metaChannel}!`,
ephemeral: true,
});
return;
}

Expand Down Expand Up @@ -746,8 +775,22 @@ export async function helperButton(
// log.debug(F, `introMessage: ${introMessage}`);

await target.roles.add(role);

// Update the last date when they were given the helper role
await db.users.upsert({
where: {
discord_id: interaction.user.id,
},
create: {
discord_id: interaction.user.id,
},
update: {
last_was_helper: new Date(),
},
});

const metaChannel = await i.guild?.channels.fetch(guildData.channel_tripsitmeta) as TextChannel;
await i.editReply({ content: `Added role ${role.name}, go check out ${metaChannel}!` });
await i.editReply({ content: `Added role ${role.name}, go check out ${metaChannel}! If you ever want to remove it, just click the button again.` });

if (metaChannel.id === guildData.channel_tripsitmeta) {
const introString = `
Expand Down Expand Up @@ -801,7 +844,6 @@ export async function helperButton(
**If you have any questions, please reach out!**
`);
}

if (i.guild.id === env.DISCORD_GUILD_ID) {
const channelTripsitters = await i.guild?.channels.fetch(env.CHANNEL_TRIPSITTERS) as TextChannel;
const roleTripsitter = await i.guild?.roles.fetch(guildData.role_tripsitter) as Role;
Expand Down
77 changes: 29 additions & 48 deletions src/discord/commands/guild/d.rpg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1359,9 +1359,8 @@ function getLastMonday(d:Date) {
export async function rpgBounties(
interaction: MessageComponentInteraction | ChatInputCommandInteraction,
command: 'quest' | 'dungeon' | 'raid' | null,
):Promise<InteractionEditReplyOptions | InteractionUpdateOptions> {
): Promise<InteractionEditReplyOptions | InteractionUpdateOptions> {
// Check if the user has a persona

const userData = await db.users.upsert({
where: {
discord_id: interaction.user.id,
Expand Down Expand Up @@ -1445,54 +1444,40 @@ export async function rpgBounties(
},
};

const allResetTimes: { [key: string]: Date } = {
quest: new Date(new Date().setHours(new Date().getHours() + 1, 0, 0, 0)),
dungeon: new Date(new Date(new Date().setDate(new Date().getDate() + 1)).setHours(0, 0, 0, 0)),
raid: new Date(new Date(getLastMonday(new Date()).getTime() + 7 * 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0)),
};

// If the command is not null, we need to check the respective reset time
if (command !== null) {
const dbKey = `last_${command}`;
const lastBounties = personaData[dbKey as 'last_quest' | 'last_dungeon' | 'last_raid'] as Date;
// log.debug(F, `lastBounties: ${lastBounties}`);

let resetTime = {} as Date;
let timeout = false;
if (command === 'quest') {
const currentHour = new Date().getHours();
// log.debug(F, `currentHour: ${currentHour}`);

resetTime = new Date(new Date().setHours(currentHour + 1, 0, 0, 0));
if (lastBounties) {
// Check if the user has already completed the bounty type today, hourly, or weekly
const currentDate = new Date();

if (lastBounties) {
const lastBountiesHour = lastBounties.getHours();
// log.debug(F, `lastBountiesHour: ${lastBountiesHour}`);
if (lastBountiesHour === currentHour) {
timeout = true;
}
}
} else if (command === 'dungeon') {
const currentDay = new Date().getDate();
// log.debug(F, `currentDay: ${currentDay}`);
resetTime = new Date(new Date(new Date().setDate(currentDay + 1)).setHours(0, 0, 0, 0));

if (lastBounties) {
const lastBountiesDay = lastBounties.getDate();
// log.debug(F, `lastBountiesDay: ${lastBountiesDay}`);

// log.debug(F, `personaData1: ${JSON.stringify(personaData, null, 2)}`);
// if (lastBounties && (lastBounties.getTime() + interval > new Date().getTime())) {
if (lastBountiesDay === currentDay) {
timeout = true;
}
}
} else if (command === 'raid') {
const lastMonday = getLastMonday(new Date());
// log.debug(F, `lastMonday: ${lastMonday}`);
resetTime = new Date(new Date(lastMonday.getTime() + 7 * 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0));
const timeComparison = {
quest: () => lastBounties.getHours() === currentDate.getHours(),
dungeon: () => lastBounties.getDate() === currentDate.getDate(),
raid: () => lastBounties.getTime() > getLastMonday(currentDate).getTime(),
};

// Check if the last bounties was done after the last monday
if (lastBounties && lastBounties.getTime() > lastMonday.getTime()) {
if (timeComparison[command] && timeComparison[command]()) {
timeout = true;
}
}

// log.debug(F, `resetTime: ${resetTime}`);
// log.debug(F, `timeout: ${timeout}`);
// Including all reset times in the response
const resetTimesMessage = stripIndents`
**Reset Times:**
- Quest: ${time(allResetTimes.quest, 'R')}
- Dungeon: ${time(allResetTimes.dungeon, 'R')}
- Raid: ${time(allResetTimes.raid, 'R')}
`;

if (timeout) {
return {
Expand All @@ -1501,42 +1486,38 @@ export async function rpgBounties(
.setFooter({ text: `${(interaction.member as GuildMember).displayName}'s TripSit RPG (BETA)`, iconURL: (interaction.member as GuildMember).displayAvatarURL() })
.setTitle(contracts[command].fail.title)
.setDescription(stripIndents`${contracts[command].fail.description}
You can try again ${time(resetTime, 'R')}
${resetTimesMessage}
${emojiGet('buttonBetSmall')} **Wallet:** ${personaData.tokens}`)
.setColor(contracts[command].fail.color)],
components: [rowBounties],
};
}

// Process tokens and other logic here...
let tokens = 10;
if (command === 'dungeon') { tokens = 50; } else if (command === 'raid') { tokens = 100; }

let tokenMultiplier = inventoryData
.filter(item => item.effect === 'tokenMultiplier')
.reduce((acc, item) => acc + parseFloat(item.effect_value), 1);
// log.debug(F, `tokenMultiplier (before donor): ${tokenMultiplier}`);

// CHeck if the user who started this interaction has the patreon or booster roles
// Check for roles and adjust multiplier
const member = await interaction.guild?.members.fetch(interaction.user.id);
if (member?.roles.cache.has(env.ROLE_BOOSTER) || member?.roles.cache.has(env.ROLE_PATRON)) {
tokenMultiplier += 0.1;
}

// Round token multiplier to 1 decimal place
tokenMultiplier = Math.round(tokenMultiplier * 10) / 10;
// log.debug(F, `tokenMultiplier: ${tokenMultiplier}`);

tokens *= tokenMultiplier;

if (env.NODE_ENV === 'development') { tokens *= 10; }

tokens = Math.round(tokens);

// Award the user tokens
// Award tokens to the user
personaData.tokens += tokens;
personaData[dbKey as 'last_quest' | 'last_dungeon' | 'last_raid'] = new Date();

// log.debug(F, `personaData2: ${JSON.stringify(personaData, null, 2)}`);
await db.personas.upsert({
where: {
id: personaData.id,
Expand All @@ -1551,7 +1532,7 @@ export async function rpgBounties(
.setFooter({ text: `${(interaction.member as GuildMember).displayName}'s TripSit RPG (BETA)`, iconURL: (interaction.member as GuildMember).displayAvatarURL() })
.setTitle(contracts[command].success.title)
.setDescription(stripIndents`${contracts[command].success.description.replace('{tokens}', tokens.toString())}
You can try again ${time(resetTime, 'R')}.
${resetTimesMessage}
${emojiGet('buttonBetSmall')} **Wallet:** ${personaData.tokens}`)
.setColor(contracts[command].success.color)],
components: [rowBounties],
Expand Down
48 changes: 48 additions & 0 deletions src/discord/utils/commandCooldown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { User, GuildMember } from 'discord.js';

// Map to store cooldowns for users and their commands
const cooldowns = new Map<string, Map<string, number>>();

/**
* commandCooldown
* @param {User | GuildMember} user The user or guild member
* @param {string} commandName The name of the command being executed
* @param {number} cooldownAmount The cooldown duration in milliseconds (default is 30 seconds)
* @return {Promise<{ success: boolean; message?: string }>}
*/
async function commandCooldown(
user: User | GuildMember,
commandName: string,
cooldownAmount: number = 30000,
): Promise<{ success: boolean; message?: string }> {
const now = Date.now();

// Ensure there's a map for the user in the cooldowns map
if (!cooldowns.has(user.id)) {
cooldowns.set(user.id, new Map());
}

const userCooldowns = cooldowns.get(user.id) as Map<string, number>;

// Check if the user has a cooldown for the specific command
const commandExpiration = userCooldowns.get(commandName);
if (commandExpiration) {
const expirationTime = commandExpiration + cooldownAmount;

// If the cooldown is still active, inform the user
if (now < expirationTime) {
const timeLeft = (expirationTime - now) / 1000; // Time left in seconds
return {
success: false,
message: `Please wait ${timeLeft.toFixed(1)} more seconds before using this command or button again.`,
};
}
}

// Set or reset the cooldown for the specific command
userCooldowns.set(commandName, now);

return { success: true };
}

export default commandCooldown;
21 changes: 21 additions & 0 deletions src/discord/utils/tripsitme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { ticket_status, user_tickets } from '@prisma/client';
import commandContext from './context';
import { embedTemplate } from './embedTemplate';
import { checkChannelPermissions, checkGuildPermissions } from './checkPermissions';
import commandCooldown from './commandCooldown';

const F = f(__filename);

Expand Down Expand Up @@ -842,6 +843,13 @@ export async function tripsitmeUserClose(
if (!interaction.channel) return;
log.info(F, await commandContext(interaction));

const cooldown = await commandCooldown(interaction.user, interaction.customId);

if (!cooldown.success && cooldown.message) {
await interaction.reply({ content: cooldown.message, ephemeral: true });
return;
}

await interaction.deferReply({ ephemeral: false });

const targetId = interaction.customId.split('~')[1];
Expand Down Expand Up @@ -1187,6 +1195,12 @@ export async function tripSitMe(
return null;
}

const cooldown = await commandCooldown(interaction.user, interaction.customId);

if (!cooldown.success && cooldown.message) {
await interaction.editReply(cooldown.message);
}

// const actor = interaction.member;
const guildData = await db.discord_guilds.upsert({
where: {
Expand Down Expand Up @@ -1467,6 +1481,13 @@ export async function tripsitmeButton(
log.info(F, await commandContext(interaction));
const target = interaction.member as GuildMember;

const cooldown = await commandCooldown(interaction.user, interaction.customId);

if (!cooldown.success && cooldown.message) {
await interaction.reply({ content: cooldown.message, ephemeral: true });
return;
}

// log.debug(F, `target: ${JSON.stringify(target, n ull, 2)}`);

// const actorIsAdmin = target.permissions.has(PermissionsBitField.Flags.Administrator);
Expand Down
4 changes: 2 additions & 2 deletions src/docker/Dockerfile.tripbot
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,5 @@ RUN npx prisma generate --schema=./src/prisma/moodle/schema.prisma
USER node

# Start the bot using the production build
# CMD npx prisma migrate deploy && pm2-runtime build/src/start.js
CMD pm2-runtime build/src/start.js
CMD npx prisma migrate deploy && pm2-runtime build/src/start.js
# CMD pm2-runtime build/src/start.js
Loading

0 comments on commit 175581d

Please sign in to comment.