Skip to content

Commit

Permalink
Optimize giveaway code
Browse files Browse the repository at this point in the history
  • Loading branch information
gc committed Dec 27, 2024
1 parent 0aee214 commit a143015
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 29 deletions.
14 changes: 13 additions & 1 deletion src/lib/cache.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import type { PerkTier } from '@oldschoolgg/toolkit/util';
import type { User } from '@prisma/client';
import type { Giveaway, Guild, User } from '@prisma/client';
import { Time } from 'e';
import { LRUCache } from 'lru-cache';

export const perkTierCache = new Map<string, 0 | PerkTier>();

export type PartialUser = Pick<User, 'bitfield' | 'badges' | 'minion_hasBought'>;
export const partialUserCache = new Map<string, PartialUser>();

type CachedGuild = Pick<Guild, 'disabledCommands' | 'id' | 'petchannel' | 'staffOnlyChannels'>;
export const untrustedGuildSettingsCache = new LRUCache<string, CachedGuild>({ max: 1000 });

export const giveawayCache = new LRUCache<number, Giveaway>({
max: 10,
ttl: Time.Second * 10,
ttlAutopurge: true,
ttlResolution: Time.Second
});
2 changes: 1 addition & 1 deletion src/lib/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { Items } from 'oldschooljs';

import { UserError } from '@oldschoolgg/toolkit/structures';
import { command_name_enum } from '@prisma/client';
import { untrustedGuildSettingsCache } from '../mahoji/guildSettings';
import { minionStatusCommand } from '../mahoji/lib/abstracted_commands/minionStatusCommand';
import { untrustedGuildSettingsCache } from './cache.js';
import { BitField, Channel, Emoji, globalConfig } from './constants';
import pets from './data/pets';
import type { ItemBank } from './types';
Expand Down
45 changes: 31 additions & 14 deletions src/lib/util/giveaway.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import type { Giveaway } from '@prisma/client';
import type { MessageEditOptions } from 'discord.js';
import { time, userMention } from 'discord.js';
import { Time, debounce, noOp, randArrItem } from 'e';
import { Bank } from 'oldschooljs';
import type { ItemBank } from 'oldschooljs/dist/meta/types';
import { type MessageEditOptions, time, userMention } from 'discord.js';
import { Time, debounce, noOp } from 'e';
import { Bank, type ItemBank } from 'oldschooljs';

import { Events } from '../constants';

import { sql } from '../postgres.js';
import { channelIsSendable } from '../util';
import { logError } from './logError';
import { sendToChannelID } from './webhook';
Expand Down Expand Up @@ -38,6 +36,27 @@ export function generateGiveawayContent(host: string, finishDate: Date, usersEnt
There are ${usersEntered.length} users entered in this giveaway.`;
}

async function pickRandomGiveawayWinner(giveaway: Giveaway): Promise<MUser | null> {
if (giveaway.users_entered.length === 0) return null;
const result: { id: string }[] = await sql`WITH giveaway_users AS (
SELECT unnest(users_entered) AS user_id
FROM giveaway
WHERE id = ${giveaway.id}
)
SELECT id
FROM users
WHERE id IN (SELECT user_id FROM giveaway_users)
AND "minion.ironman" = false
AND id != ${giveaway.user_id}
ORDER BY random()
LIMIT 1;
`;
const id = result[0]?.id;
if (!id) return null;
const user = await mUserFetch(id);
return user;
}

export const updateGiveawayMessage = debounce(async (_giveaway: Giveaway) => {
const giveaway = await prisma.giveaway.findFirst({ where: { id: _giveaway.id } });
if (!giveaway) return;
Expand Down Expand Up @@ -72,19 +91,17 @@ export async function handleGiveawayCompletion(_giveaway: Giveaway) {
}
});

await updateGiveawayMessage(giveaway);

const creator = await mUserFetch(giveaway.user_id);

const users = (await Promise.all(giveaway.users_entered.map(i => mUserFetch(i)))).filter(
u => !u.isIronman && u.id !== giveaway.user_id
);
await updateGiveawayMessage(giveaway);
const winner = await pickRandomGiveawayWinner(giveaway);

if (users.length === 0) {
if (winner === null) {
await refundGiveaway(creator, loot);
return;
}

const winner = randArrItem(users);
await transactItems({ userID: winner.id, itemsToAdd: loot });
await prisma.economyTransaction.create({
data: {
Expand All @@ -99,10 +116,10 @@ export async function handleGiveawayCompletion(_giveaway: Giveaway) {

globalClient.emit(
Events.EconomyLog,
`${winner.mention}[${winner.id}] won ${loot} in a giveaway of ${users.length} made by ${creator.mention}[${creator.id}].`
`${winner.mention}[${winner.id}] won ${loot} in a giveaway of ${giveaway.users_entered.length} made by ${creator.mention}[${creator.id}].`
);

const str = `<@${giveaway.user_id}> **Giveaway finished:** ${users.length} users joined, the winner is... **${winner.mention}**
const str = `<@${giveaway.user_id}> **Giveaway finished:** ${giveaway.users_entered.length} users joined, the winner is... **${winner.mention}**
They received these items: ${loot}`;

Expand Down
16 changes: 11 additions & 5 deletions src/lib/util/globalInteractions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import { shootingStarsCommand, starCache } from '../../mahoji/lib/abstracted_com
import type { ClueTier } from '../clues/clueTiers';
import { BitField, PerkTier } from '../constants';

import type { Giveaway } from '@prisma/client';
import { RateLimitManager } from '@sapphire/ratelimits';
import { InteractionID } from '../InteractionID';
import { giveawayCache } from '../cache.js';
import { runCommand } from '../settings/settings';
import { toaHelpCommand } from '../simulation/toa';
import type { ItemBank } from '../types';
Expand Down Expand Up @@ -132,11 +134,15 @@ async function giveawayButtonHandler(user: MUser, customID: string, interaction:
const split = customID.split('_');
if (split[0] !== 'GIVEAWAY') return;
const giveawayID = Number(split[2]);
const giveaway = await prisma.giveaway.findFirst({
where: {
id: giveawayID
}
});
let giveaway: Giveaway | null = giveawayCache.get(giveawayID) ?? null;
if (!giveaway) {
giveaway = await prisma.giveaway.findFirst({
where: {
id: giveawayID
}
});
if (giveaway) giveawayCache.set(giveawayID, giveaway);
}
if (!giveaway) {
return interactionReply(interaction, { content: 'Invalid giveaway.', ephemeral: true });
}
Expand Down
5 changes: 3 additions & 2 deletions src/mahoji/commands/giveaway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import { Time, randInt } from 'e';
import { Bank } from 'oldschooljs';
import type { ItemBank } from 'oldschooljs/dist/meta/types';

import { giveawayCache } from '../../lib/cache.js';
import { Emoji, patronFeatures } from '../../lib/constants';
import { marketPriceOfBank } from '../../lib/marketPrices';

import { channelIsSendable, isModOrAdmin, makeComponents, toKMB } from '../../lib/util';
import { generateGiveawayContent } from '../../lib/util/giveaway';
import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation';
Expand Down Expand Up @@ -193,7 +193,7 @@ export const giveawayCommand: OSBMahojiCommand = {
}

try {
await prisma.giveaway.create({
const giveaway = await prisma.giveaway.create({
data: {
id: giveawayID,
channel_id: channelID.toString(),
Expand All @@ -207,6 +207,7 @@ export const giveawayCommand: OSBMahojiCommand = {
users_entered: []
}
});
giveawayCache.set(giveaway.id, giveaway);
} catch (err: any) {
logError(err, {
user_id: user.id,
Expand Down
6 changes: 2 additions & 4 deletions src/mahoji/guildSettings.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { Guild, Prisma } from '@prisma/client';
import type { Prisma } from '@prisma/client';
import type { Guild as DJSGuild } from 'discord.js';
import { LRUCache } from 'lru-cache';

type CachedGuild = Pick<Guild, 'disabledCommands' | 'id' | 'petchannel' | 'staffOnlyChannels'>;
export const untrustedGuildSettingsCache = new LRUCache<string, CachedGuild>({ max: 1000 });
import { untrustedGuildSettingsCache } from '../lib/cache.js';

export async function mahojiGuildSettingsFetch(guild: string | DJSGuild) {
const id = typeof guild === 'string' ? guild : guild.id;
Expand Down
4 changes: 2 additions & 2 deletions src/mahoji/lib/inhibitors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import type { DMChannel, Guild, GuildMember, InteractionReplyOptions, TextChanne
import { ComponentType, PermissionsBitField } from 'discord.js';

import { BLACKLISTED_GUILDS, BLACKLISTED_USERS } from '../../lib/blacklists';
import { type PartialUser, partialUserCache, perkTierCache } from '../../lib/cache';
import { type PartialUser, partialUserCache, perkTierCache, untrustedGuildSettingsCache } from '../../lib/cache';
import { BadgesEnum, BitField, Channel, DISABLED_COMMANDS, globalConfig } from '../../lib/constants';
import { minionBuyButton } from '../../lib/sharedComponents';
import type { CategoryFlag } from '../../lib/types';
import { minionIsBusy } from '../../lib/util/minionIsBusy';
import { mahojiGuildSettingsFetch, untrustedGuildSettingsCache } from '../guildSettings';
import { mahojiGuildSettingsFetch } from '../guildSettings';
import { Cooldowns } from './Cooldowns';

export interface AbstractCommandAttributes {
Expand Down

0 comments on commit a143015

Please sign in to comment.