From eedf25b99e41c59a323c4c9516eb26cbcd927c48 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 31 Jan 2021 00:44:58 +0100 Subject: [PATCH 01/23] Port potato commands to shall commands --- slash_potato.ts | 704 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 704 insertions(+) create mode 100644 slash_potato.ts diff --git a/slash_potato.ts b/slash_potato.ts new file mode 100644 index 0000000..435d330 --- /dev/null +++ b/slash_potato.ts @@ -0,0 +1,704 @@ +const potato_slash = discord.interactions.commands.registerGroup({ + name: 'potato', + description: 'Potato commands' +}); + +const ALLOW_DAILY = true; +const SHOP_ITEMS = { + 'potato farmer': { + price: 1, + description: 'gives you the potato farmer role for 24h', + async onPurchase(user: discord.User) { + const guild = await discord.getGuild(); + const member = await guild.getMember(user.id); + const role = await guild + .getRoles() + .then((roles) => roles.find((role) => role.name === 'potato farmer')); + if (!member || !role) throw new Error('invalid role or member'); + await member.addRole(role.id); + }, + async onExpire(user: discord.User) { + const guild = await discord.getGuild(); + const member = await guild.getMember(user.id); + const role = await guild + .getRoles() + .then((roles) => roles.find((role) => role.name === 'potato farmer')); + if (!member || !role || !member.roles.includes(role.id)) return; + + await member.removeRole(role.id); + }, + enabled: true, + duration: 24 * 60 * 60 * 1000 // 24 hours, checked in 5 minute intervals + } +} as { + [key: string]: { + price: number; + duration: number | undefined; + description: string; + enabled: boolean; + onPurchase: Function; + onExpire: Function; + }; +}; +const MEDALS = [ + discord.decor.Emojis.FIRST_PLACE_MEDAL, + discord.decor.Emojis.SECOND_PLACE_MEDAL, + discord.decor.Emojis.THIRD_PLACE_MEDAL +]; + +const potatoKV = new pylon.KVNamespace('potato'); +const randomTimeBetween = (min: number, max: number) => + Math.round(Math.random() * (max - min) + min); +const nextDrawText = () => { + const nextDraw = + (Math.ceil(Date.now() / 1000 / 60 / POTATO_LOTTERY_TIME_MINUTES) * + 1000 * + 60 * + POTATO_LOTTERY_TIME_MINUTES - + Date.now()) / + 1000 / + 60; + + const minutes = Math.floor(nextDraw); + const seconds = Math.floor((nextDraw % 1) * 60); + return `next draw is in ${minutes} ${ + minutes === 1 ? 'minute' : 'minutes' + } and ${seconds} ${seconds === 1 ? 'second' : 'seconds'}`; +}; + +const setDefaultReply = (commandGroup: discord.command.CommandGroup) => { + commandGroup.default( + () => ({}), + async (message) => + await message.reply( + `${discord.decor.Emojis.NO_ENTRY} unknown potato command, try \`/potato help\`` + ) + ); +}; + +discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { + if (!message.author || message.author.bot) return; + + if (await potatoKV.get('cooldown')) { + if (message.content === discord.decor.Emojis.POTATO) { + const [lastChannel, potatoId] = + (await potatoKV.get('lastPotato'))?.split('-') || []; + if (lastChannel !== message.channelId) return; + + try { + await message + .getChannel() + .then((c) => c.getMessage(potatoId)) + .then((m) => m?.delete()) + .catch(() => {}); + + await message.delete().catch(() => {}); + } catch (nothing) {} + + const poisonous = Math.random() < 0.01; + + const oldCount = (await potatoKV.get(message.author.id)) || 0; + const newCount = Math.max( + 0, + oldCount + + (poisonous + ? -Math.max( + 1, + Math.min(10, Math.floor((Math.random() * oldCount) / 4)) + ) + : 1) + ); + + await potatoKV.put(message.author.id, newCount); + await potatoKV.delete('lastPotato'); + await message.reply( + new discord.Embed({ + title: `${ + poisonous ? discord.decor.Emojis.SKULL : discord.decor.Emojis.POTATO + } potato claimed ${discord.decor.Emojis.POTATO}`, + description: `${message.author.getTag()} ${ + poisonous + ? `tried to pick up a poisonous potato, poisoning ${oldCount - + newCount} potatos in the process` + : 'has claimed a potato' + }, and now holds onto ${newCount} potato${ + newCount === 1 ? '' : 'es' + }.`, + color: 0x11111c, + thumbnail: { url: message.author.getAvatarUrl() }, + footer: { + text: poisonous + ? '' + : "to the rest of you, can't catch em all, right?" + } + }) + ); + } + + return; + } else { + const [lastChannel, potatoId] = + (await potatoKV.get('lastPotato'))?.split('-') || []; + + await discord + .getGuild() + .then( + (g) => + g.getChannel(lastChannel) as Promise< + discord.GuildTextChannel | undefined + > + ) + .then((c) => c?.getMessage(potatoId)) + .then((m) => m?.delete()) + .catch(() => {}); + } + + if (Math.random() > 0.3) return; + + const reply = await message.reply(discord.decor.Emojis.POTATO); + + const cooldown = randomTimeBetween(3 * 60 * 1000, 20 * 60 * 1000); + + await potatoKV.put('cooldown', true, { ttl: cooldown }); + await potatoKV.put('lastPotato', `${message.channelId}-${reply.id}`); +}); + +potato_slash.register( + { + name: 'help', + description: 'Displays usable potato commands' + }, + async (message) => { + await message.respondEphemeral(`${discord.decor.Emojis.POTATO} help ${discord.decor.Emojis.POTATO} + +when a ${discord.decor.Emojis.POTATO} is dropped, be the first to pick it up by posting a ${discord.decor.Emojis.POTATO} too. + +**commands**: +- \`/potato help\` - shows this help message +- \`/potato inspect [user]\` - inspect another [user]s potato balance or your own +- \`/potato top [count]\` - top n potato collectors +- \`/potato gamble \` - gamble potatoes +- \`/potato steal \` - steal potatoes from other people +- \`/potato give \` - give your potatoes to other people - if you're feeling kind. +- \`/potato drop\` - drop one of your potatoes. the fastest to pick it up gets it +- \`/potato daily\` - claim your daily potato + +- \`/potato shop list\` - list all available shop items +- \`/potato shop buy \` - buy from the shop +`); + } +); + +potato_slash.register( + { + name: 'inspect', + description: 'inspect how many potatoes someone has', + options: (opt) => ({ + who: opt.guildMember({ + name: 'who', + description: "who's potatoes to inspect", + required: false + }) + }) + }, + async (message, { who }) => { + var user: discord.User; + if (who) { + user = who.user; + } else { + user = message.member.user; + } + //Had to add that as default subcommands are not possible => No /potato + const currentCount = (await potatoKV.get(user.id)) || 0; + await message.respond( + `${discord.decor.Emojis.POTATO} potato count ${ + discord.decor.Emojis.POTATO + } + +${user.getTag()} has ${currentCount} potato${ + currentCount === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat(Math.min(currentCount, 100))}` + ); + } +); + +potato_slash.register( + { + name: 'gamble', + description: 'gamble with your potatoes', + options: (opt) => ({ + amount: opt.integer({ + name: 'amount', + description: 'how many potatoes you want to gamble', + required: true + }) + }) + }, + async (message, { amount }) => { + if (await potatoKV.get(`gamble-${message.member?.user.id}`)) + return await message.respond( + `${discord.decor.Emojis.NO_ENTRY_SIGN} ${discord.decor.Emojis.POTATO} gambling addiction is a serious problem. Regulations require a wait.` + ); + + const currentCount = + (await potatoKV.get(message.member?.user.id)) || 0; + + if (amount > currentCount) + return await message.respond( + 'You can only gamble as many potatoes as you have!' + ); + + if (amount > 10 || amount < 1) + return await message.respond( + 'You can only gamble between 1 and 10 potatoes.' + ); + + await potatoKV.put(`gamble-${message.member?.user.id}`, true, { + ttl: randomTimeBetween(2 * 60 * 1000, 5 * 60 * 1000) + }); + + const won = Math.random() > 0.5; + const newCount = currentCount + amount * (won ? 1 : -1); + await potatoKV.put(message.member?.user.id, newCount); + + await message.respond(`${discord.decor.Emojis.GAME_DIE} ${ + discord.decor.Emojis.POTATO + } ${discord.decor.Emojis.GAME_DIE} + +Your gambling ${won ? 'paid off' : 'sucked'}, you ${ + won ? 'won' : 'lost' + } ${amount} potato${amount === 1 ? '' : 'es'}, ${ + won ? 'giving you' : 'leaving you with' + } a total of ${newCount} potato${ + newCount === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat(newCount)} ${ + won + ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND + : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND + }`); + } +); +potato_slash.register( + { + name: 'steal', + description: 'Steal potatoes from a fellow collector', + options: (opt) => ({ + who: opt.guildMember({ + name: 'who', + description: 'who to steal from', + required: true + }), + count: opt.integer({ + name: 'amount', + description: ' How much you want to steal' + }) + }) + }, + async (message, { who, count }) => { + if (message.member?.user.id === who.user.id) + return await message.respond("You can't steal from yourself!"); + if (await potatoKV.get(`steal-${message.member?.user.id}`)) + return await message.respond( + `${discord.decor.Emojis.POLICE_OFFICER} Your potato thief actions are being currently scrutinized. Lay low for a while.` + ); + const success = Math.random() < 0.25; + const userPotatos = + (await potatoKV.get(message.member?.user.id)) || 0; + const targetPotatos = (await potatoKV.get(who.user.id)) || 0; + + if (count > userPotatos) + return await message.respond( + 'You can only steal as many potatoes as you have!' + ); + + if (count > targetPotatos) + return await message.respond('That user doesnt have that many potatoes!'); + + if (count < 1) + return await message.respond('You need to steal at least one potato.'); + + if (count > 5) + return await message.respond( + 'Your small hands can only carry 5 potatos!' + ); + + await potatoKV.put(`steal-${message.member?.user.id}`, true, { + ttl: randomTimeBetween(3 * 60 * 1000, 10 * 60 * 1000) + }); + + const newUserPotatos = userPotatos + count * (success ? 1 : -1); + const newTargetPotatos = targetPotatos + count * (success ? -1 : 1); + + await potatoKV.put(message.member?.user.id, newUserPotatos); + await potatoKV.put(who.user.id, newTargetPotatos); + + await message.respond(`${discord.decor.Emojis.GLOVES} ${ + discord.decor.Emojis.POTATO + } ${discord.decor.Emojis.GLOVES} +Your thievery ${success ? 'paid off' : 'sucked'}, you ${ + success ? 'stole' : 'gave' + } ${count} potato${count === 1 ? '' : 'es'} ${ + success ? 'from' : 'to' + } ${who.user.getTag()}, ${ + success ? 'giving you a total of' : 'leaving you with' + } ${newUserPotatos} potato${ + newUserPotatos === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat(newUserPotatos)} ${ + success + ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND + : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND + }`); + } +); + +potato_slash.register( + { + name: 'give', + description: 'Give potatoes to a fellow collector', + options: (opt) => ({ + who: opt.guildMember({ + name: 'who', + description: 'who to bless with more potatoes', + required: true + }), + count: opt.integer({ + name: 'amount', + description: ' How much you want to give' + }) + }) + }, + async (message, { who, count }) => { + if (message.member?.user.id === who.user.id) + return await message.respond("You can't give potatos to yourself!"); + if (who.user.bot) + return await message.respond("You can't give potatos to bots!"); + const userPotatos = + (await potatoKV.get(message.member?.user.id)) || 0; + const targetPotatos = (await potatoKV.get(who.user.id)) || 0; + + if (!count && count !== 0) count = 1; + + if (count > userPotatos) + return await message.respond( + 'You can only give as many potatos as you have!' + ); + + if (count < 1) + return await message.respond('You need to send at least one potato.'); + + const newUserPotatos = userPotatos - count; + const newTargetPotatos = targetPotatos + count; + + await potatoKV.put(message.member?.user.id, newUserPotatos); + await potatoKV.put(who.user.id, newTargetPotatos); + + await message.respond( + `You gave ${count} potato${ + count === 1 ? '' : 'es' + } to ${who.user.getTag()}, how nice of you.` + ); + } +); + +potato_slash.register( + { + name: 'top', + description: 'View the top potato collector', + options: (opt) => ({ + count: opt.integer({ + name: 'count', + description: 'top x collectors', + required: false + }) + }) + }, + async (message, { count }) => { + count = Math.min(Math.max(3, count || 10), 20); + const items = await potatoKV.items(); + const filtered = items.filter( + (entry) => + !isNaN((entry.key as unknown) as number) && + ((entry.value as unknown) as number) > 0 + ); + const sorted = filtered.sort( + (a, b) => (b.value as number) - (a.value as number) + ); + const top = sorted.slice(0, count); + count = top.length; + const userMap = await Promise.all( + top.map((entry) => + discord + .getUser(entry.key) + .then((user) => ({ user, potatos: entry.value as number })) + ) + ); + + let description = `${discord.decor.Emojis.POTATO} **${filtered + .reduce((a, b) => a + (b.value as number), 0) + .toLocaleString()}**\n`; + description += `${discord.decor.Emojis.MAN_FARMER} **${filtered.length}**\n\n`; + description += `${discord.decor.Emojis.CHART_WITH_UPWARDS_TREND} **Ranks** ${discord.decor.Emojis.MUSCLE}\n`; + + for (const entry of userMap.slice(0, Math.max(3, count - 1))) { + const { user, potatos } = entry; + const place = userMap.indexOf(entry); + description += `\` ${MEDALS[place] || + `${(place + 1).toString().padStart(2, ' ')} `} \` **${ + user?.username + }**#${user?.discriminator} - ${potatos.toLocaleString()} potatos\n`; + } + + const ownIndex = sorted.findIndex( + (item) => item.key === message.member.user.id + ); + + if (ownIndex >= count) { + description += `\` ... \` *${ownIndex - count + 1}* other farmers\n`; + description += `\` ${(ownIndex + 1).toString().padStart(2, ' ')} \` **${ + message.member.user.username + }**#${message.member.user.discriminator} - ${ + sorted[ownIndex].value + } potato${sorted[ownIndex].value === 1 ? '' : 'es'}`; + } else if (count > 3) { + const { user, potatos } = userMap[count - 1]; + description += `\` ${count.toString().padStart(2, ' ')} \` **${ + user?.username + }**#${user?.discriminator} - ${potatos.toLocaleString()} potatos\n`; + } + + await message.respond(`${discord.decor.Emojis.TROPHY} Leaderboard​ ${discord.decor.Emojis.CROWN} + +${description}`); + } +); + +potato_slash.register( + { name: 'drop', description: 'drop a potato in the chat' }, + async (message) => { + const userPotatos = + (await potatoKV.get(message.member?.user.id)) || 0; + + if (!userPotatos) + return await message.respond("you don't have any potatos!"); + + const lastPotato = await potatoKV.get('lastPotato'); + if (lastPotato) + return await message.respond( + `there is already an active potato waiting to be picked up in <#${ + lastPotato.split('-')[0] + }>!` + ); + + await potatoKV.put(message.member?.user.id, userPotatos - 1); + + const reply = await message.respond(discord.decor.Emojis.POTATO); + + const cooldown = randomTimeBetween(3 * 60 * 1000, 20 * 60 * 1000); + + await potatoKV.put('cooldown', true, { ttl: cooldown }); + await potatoKV.put( + 'lastPotato', + `${message.channelId}-No id because slash command...`, + { + ttl: cooldown + } + ); + } +); + +potato_slash.register( + { + name: 'modify', + description: 'Modify the potatoes of a fellow collector', + showSourceMessage: false, + options: (opt) => ({ + who: opt.guildMember({ + name: 'who', + description: "who's potatoes to modify", + required: true + }), + count: opt.string({ + name: 'amount', + description: ' How much you want to give/take' + }) + }) + }, + async (message, { who, count }) => { + if (!message.member.can(discord.Permissions.ADMINISTRATOR)) + return await message.respondEphemeral('missing permissions'); + if (who.user.bot) + return await message.respondEphemeral( + 'thats a.. bot. you wanna modify a bots potatos??' + ); + const oldCount = (await potatoKV.get(who.user.id)) || 0; + + let newCount = oldCount; + if (count.startsWith('+')) newCount += parseInt(count.replace('+', '')); + else if (count.startsWith('-')) + newCount -= parseInt(count.replace('-', '')); + else newCount = parseInt(count); + + if (isNaN(newCount as number)) + return await message.respond('invalid count'); + + await potatoKV.put(who.user.id, newCount as number); + await message.respondEphemeral( + `Ok, updated ${who.user.getTag()}'s potatoes to ${newCount}` + ); + } +); + +if (ALLOW_DAILY) + potato_slash.register( + { + name: 'daily', + description: 'Get your daily potato!' + }, + async (message) => { + if (await potatoKV.get(`daily-${message.member.user.id}`)) + return await message.respond('you already claimed your daily potato!'); + + await potatoKV.put(`daily-${message.member.user.id}`, true, { + ttl: + Math.ceil(Date.now() / 1000 / 60 / 60 / 24) * 24 * 60 * 60 * 1000 - + Date.now() + }); + const newCount = await potatoKV.transact( + message.member.user.id, + (prev: number | undefined) => (prev || 0) + 1 + ); + await message.respond( + `you claimed your daily potato, and now hold onto ${newCount} potatos.` + ); + } + ); + +const shop = potato_slash.registerGroup({ + name: 'shop', + description: 'Buy stuff with potatoes in the potato shop' +}); + +shop.register( + { name: 'list', description: 'list all potato shop items' }, + async (message) => { + if (!Object.keys(SHOP_ITEMS).length) + return await message.respond('no items currently available, sorry!'); + + const fields = await Promise.all( + Object.entries(SHOP_ITEMS) + .filter(([, item]) => item.enabled) + .map(async ([name, item]) => ({ + name: `${name} - ${item.price} ${discord.decor.Emojis.POTATO}`, + value: item.description, + inline: true + })) + ); + + await message.respond(`Potato Shop +**Available Items**\nuse \`/potato shop buy \` to purchase an item listed here + +${fields}`); + } +); + +shop.register( + { + name: 'buy', + description: 'purchase a potato shop item', + options: (opt) => ({ + item: opt.string({ + name: 'item', + description: 'What item to buy', + required: true + }) + }) + }, + async (message, { item }) => { + const itemObj = SHOP_ITEMS[item]; + if (!itemObj || !itemObj.enabled) + return await message.respond( + `invalid potato item. use \`/potato shop list\` to get a list of all available items` + ); + + const purchases = ((await potatoKV.get('shop')) || []) as { + user: string; + item: string; + expiresAt: number | undefined; + }[]; + const purchase = purchases.find( + (purchase) => + purchase.user === message.member.user.id && purchase.item === item + ); + if (purchase) + return message.respond( + `You already bought this item!${ + purchase.expiresAt + ? ` You can buy it again on ${new Date( + purchase.expiresAt + ).toUTCString()}` + : '' + }` + ); + + const userPotatos = + (await potatoKV.get(message.member.user.id)) || 0; + if (userPotatos < itemObj.price) + return await message.respond( + "you don't have enough potatos for that item!" + ); + + try { + await itemObj.onPurchase(message.member.user); + } catch (err) { + return await message.respond( + `There was an error processing your order: ${err.message}` + ); + } + + await potatoKV.transact( + message.member.user.id, + (prev: number | undefined) => (prev || 0) - itemObj.price + ); + + await potatoKV.transact( + 'shop', + (prev: pylon.JsonArray | undefined) => + [ + ...(prev || []), + { + user: message.member.user.id, + item, + expiresAt: itemObj.duration + ? Date.now() + itemObj.duration + : undefined + } + ] as pylon.JsonArray + ); + + await message.respond(`You successfully bought \`${item}\`!`); + } +); + +pylon.tasks.cron('shop', '0 0/5 * * * * *', async () => { + const purchases = ((await potatoKV.get('shop')) || []) as { + user: string; + item: string; + expiresAt: number | undefined; + }[]; + + const newPurchases = []; + + for (const purchase of purchases) { + if (purchase.expiresAt && purchase.expiresAt <= Date.now()) { + const item = SHOP_ITEMS[purchase.item]; + if (!item) continue; + + discord + .getUser(purchase.user) + .then((user) => item.onExpire(user)) + .catch((err) => console.error(err)); + } else newPurchases.push(purchase); + } + + await potatoKV.put('shop', newPurchases as pylon.JsonArray); +}); From 5cef1f12f215589958877fd1d7569e95e927461f Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 31 Jan 2021 00:53:27 +0100 Subject: [PATCH 02/23] Update README.md --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 43fb637..10ff1d4 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,15 @@ import './potato'; If you intend to use the potato lottery system. you also have to define a lottery channel (by its channel ID) in your `main.ts`: ```ts global.POTATO_LOTTERY_CHANNEL = '693621234365366302'; -``` \ No newline at end of file +``` + +# Port to slash command +The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato:slash.ts` + +**Changes** + +Besides all commands being slash commands there are a few differnces + +1) Potato lottery has been disabled as it was just causing trouble and Pylon cvan only have up to 10 slash commands so it had to go +2) `/potato drop` now doesn't delete the potato as Pylon can't delete a slash command response +3) `/potato`doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user From 58ef35509289194167d4b604a62da251cad56bc5 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 31 Jan 2021 00:53:36 +0100 Subject: [PATCH 03/23] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10ff1d4..2a2a30b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ If you intend to use the potato lottery system. you also have to define a lotter global.POTATO_LOTTERY_CHANNEL = '693621234365366302'; ``` -# Port to slash command +# Port to slash commands The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato:slash.ts` **Changes** From 55210d4d8e1fee9ada7766db68a6300e57e2a140 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 31 Jan 2021 01:00:39 +0100 Subject: [PATCH 04/23] Remove unnecessary lines --- slash_potato.ts | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/slash_potato.ts b/slash_potato.ts index 435d330..1a3faa8 100644 --- a/slash_potato.ts +++ b/slash_potato.ts @@ -47,34 +47,6 @@ const MEDALS = [ ]; const potatoKV = new pylon.KVNamespace('potato'); -const randomTimeBetween = (min: number, max: number) => - Math.round(Math.random() * (max - min) + min); -const nextDrawText = () => { - const nextDraw = - (Math.ceil(Date.now() / 1000 / 60 / POTATO_LOTTERY_TIME_MINUTES) * - 1000 * - 60 * - POTATO_LOTTERY_TIME_MINUTES - - Date.now()) / - 1000 / - 60; - - const minutes = Math.floor(nextDraw); - const seconds = Math.floor((nextDraw % 1) * 60); - return `next draw is in ${minutes} ${ - minutes === 1 ? 'minute' : 'minutes' - } and ${seconds} ${seconds === 1 ? 'second' : 'seconds'}`; -}; - -const setDefaultReply = (commandGroup: discord.command.CommandGroup) => { - commandGroup.default( - () => ({}), - async (message) => - await message.reply( - `${discord.decor.Emojis.NO_ENTRY} unknown potato command, try \`/potato help\`` - ) - ); -}; discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { if (!message.author || message.author.bot) return; From c6a16fb1933cda20e9746a5c4da547757ca1ae79 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 31 Jan 2021 01:03:36 +0100 Subject: [PATCH 05/23] Deleted two more lines than I should've --- slash_potato.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slash_potato.ts b/slash_potato.ts index 1a3faa8..9955f82 100644 --- a/slash_potato.ts +++ b/slash_potato.ts @@ -47,6 +47,8 @@ const MEDALS = [ ]; const potatoKV = new pylon.KVNamespace('potato'); +const randomTimeBetween = (min: number, max: number) => + Math.round(Math.random() * (max - min) + min); discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { if (!message.author || message.author.bot) return; From ba14d85a332502db54340b20219ce965bc50b25c Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 31 Jan 2021 01:13:37 +0100 Subject: [PATCH 06/23] Fix typos and add disclaimer --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2a2a30b..9df6bf6 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,14 @@ global.POTATO_LOTTERY_CHANNEL = '693621234365366302'; ``` # Port to slash commands -The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato:slash.ts` +The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato_slash.ts` **Changes** -Besides all commands being slash commands there are a few differnces +Besides all commands being slash commands there are a few differences 1) Potato lottery has been disabled as it was just causing trouble and Pylon cvan only have up to 10 slash commands so it had to go 2) `/potato drop` now doesn't delete the potato as Pylon can't delete a slash command response -3) `/potato`doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user +3) `/potato` doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user + +## DO NOT USE BOTH CODES!!! From 8d3300f4d4db002d5ed99aa2442727d98ae890ea Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Tue, 9 Feb 2021 13:04:56 +0100 Subject: [PATCH 07/23] Fix /potato shop list --- slash_potato.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/slash_potato.ts b/slash_potato.ts index 9955f82..569aeb6 100644 --- a/slash_potato.ts +++ b/slash_potato.ts @@ -568,10 +568,14 @@ shop.register( })) ); - await message.respond(`Potato Shop -**Available Items**\nuse \`/potato shop buy \` to purchase an item listed here + let embed = new discord.Embed({ + title: 'Potato Shop', + description: + '**Available Items**\nuse `/potato shop buy ` to purchase an item listed here', + fields: fields + }); -${fields}`); + await message.respond({ embeds: [embed] }); } ); From bcb7b627cbcbd9287cd2a763f3a04d8f0d916e70 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 00:34:58 +0100 Subject: [PATCH 08/23] Fix Typo (Thanks Evan) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9df6bf6..c9a3533 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ The potato economy has also been ported to slash commands, a new discord feature Besides all commands being slash commands there are a few differences -1) Potato lottery has been disabled as it was just causing trouble and Pylon cvan only have up to 10 slash commands so it had to go +1) Potato lottery has been disabled as it was just causing trouble and Pylon can only have up to 10 slash commands so it had to go 2) `/potato drop` now doesn't delete the potato as Pylon can't delete a slash command response 3) `/potato` doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user From 13cc09e703abee122249fc671f0534549cf33f50 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 00:37:21 +0100 Subject: [PATCH 09/23] Create disboard.ts --- addons/disboard.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 addons/disboard.ts diff --git a/addons/disboard.ts b/addons/disboard.ts new file mode 100644 index 0000000..0730a9f --- /dev/null +++ b/addons/disboard.ts @@ -0,0 +1,25 @@ +discord.on(discord.Event.MESSAGE_CREATE, async (message) => { + if (message.author.id !== '302050872383242240') return;//The disboard bot id + const [embed] = message.embeds; + if (!embed) return; + + if (!embed.description?.includes('Bump done')) return; + const [mentionString] = embed.description.match(//g) || []; + if (!mentionString) return; + const [user] = mentionString.match(/\d+/g) || []; + if (!user) return; +//Checking if the bump was a success + + + const kv = new pylon.KVNamespace('potato'); + const POTATO_REWARD = 2; //Define how many potatoes there are as a reward for bumping + const newCount = await kv.transact( + user, + (prev: number | undefined) => (prev || 0) + POTATO_REWARD + ); + await message.reply( + `${( + await discord.getUser(user) + )?.getTag()} just got ${POTATO_REWARD} potatos for bumping the server! They now have ${newCount}` + ); +}); From 073f15ee5c5a50c67995e5f1b7cbdea10da4980c Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 00:38:35 +0100 Subject: [PATCH 10/23] Create autodelete.ts --- addons/autodelete.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 addons/autodelete.ts diff --git a/addons/autodelete.ts b/addons/autodelete.ts new file mode 100644 index 0000000..42901d6 --- /dev/null +++ b/addons/autodelete.ts @@ -0,0 +1,8 @@ +discord.on(discord.Event.GUILD_MEMBER_REMOVE, async (user) => { + const general = await discord.getTextChannel('LOG_CHANNEL_ID');//Only necessary if you want to send a message when a user leaves + const kv = new pylon.KVNamespace('potato'); + await kv.delete(user.user.id); + await general?.sendMessage( + `${user.user.toMention()} has left the server and their potatoes have been reset` //Only necessary if you want to send a message when a user leaves + ); +}); From 77f35aa3d37155931410604372942c473176738d Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 00:43:06 +0100 Subject: [PATCH 11/23] Update README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index c9a3533..2723904 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,13 @@ If you intend to use the potato lottery system. you also have to define a lotter global.POTATO_LOTTERY_CHANNEL = '693621234365366302'; ``` +# Addons + +If you use the code in `disboard.ts`, users will get a specified amount of potatoes for bumping your server with the popular bot disboard. +Simple specify how many they should get, put the code in a seperate file and import it + +If you use the code in `autodelete.ts` Pylon will autodelete potatoes of a user when they leave your server to save the limited amount of kv keys (256). Again put this in a seperate file and import it if you want to use. It also has the option to log when someone leaves, to remove that feature remove the lines that have been marked as "Only necessary if you want to send a message when a user leaves" + # Port to slash commands The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato_slash.ts` From cc073fd0a5f6eeeea3db3a981f4a697b6dab8a37 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 01:27:41 +0100 Subject: [PATCH 12/23] Bye dis board :wave: --- addons/disboard.ts | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 addons/disboard.ts diff --git a/addons/disboard.ts b/addons/disboard.ts deleted file mode 100644 index 0730a9f..0000000 --- a/addons/disboard.ts +++ /dev/null @@ -1,25 +0,0 @@ -discord.on(discord.Event.MESSAGE_CREATE, async (message) => { - if (message.author.id !== '302050872383242240') return;//The disboard bot id - const [embed] = message.embeds; - if (!embed) return; - - if (!embed.description?.includes('Bump done')) return; - const [mentionString] = embed.description.match(//g) || []; - if (!mentionString) return; - const [user] = mentionString.match(/\d+/g) || []; - if (!user) return; -//Checking if the bump was a success - - - const kv = new pylon.KVNamespace('potato'); - const POTATO_REWARD = 2; //Define how many potatoes there are as a reward for bumping - const newCount = await kv.transact( - user, - (prev: number | undefined) => (prev || 0) + POTATO_REWARD - ); - await message.reply( - `${( - await discord.getUser(user) - )?.getTag()} just got ${POTATO_REWARD} potatos for bumping the server! They now have ${newCount}` - ); -}); From 75c45e70e36b6e9d3b5a6c7cb164e316db1bcba0 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 01:28:09 +0100 Subject: [PATCH 13/23] Bye disboard :wave: --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 2723904..41a09de 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,6 @@ global.POTATO_LOTTERY_CHANNEL = '693621234365366302'; # Addons -If you use the code in `disboard.ts`, users will get a specified amount of potatoes for bumping your server with the popular bot disboard. -Simple specify how many they should get, put the code in a seperate file and import it - If you use the code in `autodelete.ts` Pylon will autodelete potatoes of a user when they leave your server to save the limited amount of kv keys (256). Again put this in a seperate file and import it if you want to use. It also has the option to log when someone leaves, to remove that feature remove the lines that have been marked as "Only necessary if you want to send a message when a user leaves" # Port to slash commands From 60698178e533102be17c90ba24adf34c5a3e2b49 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 01:39:28 +0100 Subject: [PATCH 14/23] replacing this with something more "normal" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41a09de..28c4dd3 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,4 @@ Besides all commands being slash commands there are a few differences 2) `/potato drop` now doesn't delete the potato as Pylon can't delete a slash command response 3) `/potato` doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user -## DO NOT USE BOTH CODES!!! +## DO NOT BOTH THE SLASH AND NORMAL POTATO CODE IN ONE GUILD!!! From 295e3d63b89301cea43fc29ff504ace1be7bd961 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 01:46:17 +0100 Subject: [PATCH 15/23] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 28c4dd3..8501f72 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ global.POTATO_LOTTERY_CHANNEL = '693621234365366302'; If you use the code in `autodelete.ts` Pylon will autodelete potatoes of a user when they leave your server to save the limited amount of kv keys (256). Again put this in a seperate file and import it if you want to use. It also has the option to log when someone leaves, to remove that feature remove the lines that have been marked as "Only necessary if you want to send a message when a user leaves" # Port to slash commands -The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato_slash.ts` +The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato_slash.ts`. mAke sure to not use the normal potato system if you decide to use the slash command version **Changes** @@ -23,5 +23,3 @@ Besides all commands being slash commands there are a few differences 1) Potato lottery has been disabled as it was just causing trouble and Pylon can only have up to 10 slash commands so it had to go 2) `/potato drop` now doesn't delete the potato as Pylon can't delete a slash command response 3) `/potato` doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user - -## DO NOT BOTH THE SLASH AND NORMAL POTATO CODE IN ONE GUILD!!! From 243ad69cece4c42aa583abad3204061eebcbe48a Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Sun, 14 Feb 2021 01:50:42 +0100 Subject: [PATCH 16/23] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8501f72..2405a76 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ global.POTATO_LOTTERY_CHANNEL = '693621234365366302'; If you use the code in `autodelete.ts` Pylon will autodelete potatoes of a user when they leave your server to save the limited amount of kv keys (256). Again put this in a seperate file and import it if you want to use. It also has the option to log when someone leaves, to remove that feature remove the lines that have been marked as "Only necessary if you want to send a message when a user leaves" # Port to slash commands -The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato_slash.ts`. mAke sure to not use the normal potato system if you decide to use the slash command version +The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato_slash.ts` **Changes** @@ -23,3 +23,5 @@ Besides all commands being slash commands there are a few differences 1) Potato lottery has been disabled as it was just causing trouble and Pylon can only have up to 10 slash commands so it had to go 2) `/potato drop` now doesn't delete the potato as Pylon can't delete a slash command response 3) `/potato` doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user + +**Make sure to only use one of the potato systems** From 7e925860c38d520b662f1365584b1747ce17af3b Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Wed, 24 Feb 2021 15:33:29 +0100 Subject: [PATCH 17/23] Putting it all in one file with boolean to toggle versions --- potato.ts | 1394 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 980 insertions(+), 414 deletions(-) diff --git a/potato.ts b/potato.ts index 4999cc1..c510f90 100644 --- a/potato.ts +++ b/potato.ts @@ -1,15 +1,20 @@ -const POTATO_LOTTERY_TIME_MINUTES = 5; -const ALLOW_DAILY = true; +const potatoCommands = new discord.command.CommandGroup({ + defaultPrefix: '!' +}); + +const slash_commands: boolean = true; //Define here to either use slash commands (use true) or normal commands (use false) +const POTATO_LOTTERY_TIME_MINUTES = 5; //Define here how long potato lottery should take +const ALLOW_DAILY = true; //Define here if people should be able to claim daily potatoes or not const SHOP_ITEMS = { - 'potato farmer': { - price: 1, - description: 'gives you the potato farmer role for 24h', + Premium: { + price: 200, + description: 'gives you the premium role for 24h', async onPurchase(user: discord.User) { const guild = await discord.getGuild(); const member = await guild.getMember(user.id); const role = await guild .getRoles() - .then((roles) => roles.find((role) => role.name === 'potato farmer')); + .then((roles) => roles.find((role) => role.name === 'Premium')); if (!member || !role) throw new Error('invalid role or member'); await member.addRole(role.id); }, @@ -18,7 +23,7 @@ const SHOP_ITEMS = { const member = await guild.getMember(user.id); const role = await guild .getRoles() - .then((roles) => roles.find((role) => role.name === 'potato farmer')); + .then((roles) => roles.find((role) => role.name === 'Premium')); if (!member || !role || !member.roles.includes(role.id)) return; await member.removeRole(role.id); @@ -42,39 +47,13 @@ const MEDALS = [ discord.decor.Emojis.THIRD_PLACE_MEDAL ]; -const potatoCommands = new discord.command.CommandGroup({ - defaultPrefix: '!' -}); const potatoKV = new pylon.KVNamespace('potato'); const randomTimeBetween = (min: number, max: number) => Math.round(Math.random() * (max - min) + min); -const nextDrawText = () => { - const nextDraw = - (Math.ceil(Date.now() / 1000 / 60 / POTATO_LOTTERY_TIME_MINUTES) * - 1000 * - 60 * - POTATO_LOTTERY_TIME_MINUTES - - Date.now()) / - 1000 / - 60; - - const minutes = Math.floor(nextDraw); - const seconds = Math.floor((nextDraw % 1) * 60); - return `next draw is in ${minutes} ${ - minutes === 1 ? 'minute' : 'minutes' - } and ${seconds} ${seconds === 1 ? 'second' : 'seconds'}`; -}; -const setDefaultReply = (commandGroup: discord.command.CommandGroup) => { - commandGroup.default( - () => ({}), - async (message) => - await message.reply( - `${discord.decor.Emojis.NO_ENTRY} unknown potato command, try \`${potatoCommands.commandPrefixes[0]}potato help\`` - ) - ); -}; discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { + if (!['710873588646936576', '739525325267927082'].includes(message.channelId)) + return; if (!message.author || message.author.bot) return; if (await potatoKV.get('cooldown')) { @@ -83,13 +62,15 @@ discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { (await potatoKV.get('lastPotato'))?.split('-') || []; if (lastChannel !== message.channelId) return; - await message - .getChannel() - .then((c) => c.getMessage(potatoId)) - .then((m) => m?.delete()) - .catch(() => {}); + try { + await message + .getChannel() + .then((c) => c.getMessage(potatoId)) + .then((m) => m?.delete()) + .catch(() => {}); - await message.delete().catch(() => {}); + await message.delete().catch(() => {}); + } catch (nothing) {} const poisonous = Math.random() < 0.01; @@ -118,7 +99,7 @@ discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { newCount} potatos in the process` : 'has claimed a potato' }, and now holds onto ${newCount} potato${ - newCount === 1 ? '' : 's' + newCount === 1 ? '' : 'es' }.`, color: 0x11111c, thumbnail: { url: message.author.getAvatarUrl() }, @@ -153,239 +134,269 @@ discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { const reply = await message.reply(discord.decor.Emojis.POTATO); - const cooldown = randomTimeBetween(3 * 60 * 1000, 20 * 60 * 1000); + const cooldown = randomTimeBetween(30 * 60 * 1000, 60 * 60 * 1000); await potatoKV.put('cooldown', true, { ttl: cooldown }); await potatoKV.put('lastPotato', `${message.channelId}-${reply.id}`); }); -potatoCommands.subcommand('potato', (potatoSubcommands) => { - setDefaultReply(potatoSubcommands); +if (slash_commands === true) { + const potato_slash = discord.interactions.commands.registerGroup({ + name: 'potato', + description: 'Potato commands' + }); - potatoSubcommands.on( - { name: 'help', description: 'potato help' }, - () => ({}), + potato_slash.register( + { + name: 'help', + description: 'Displays usable potato commands' + }, async (message) => { - await message.reply( - new discord.Embed({ - title: `${discord.decor.Emojis.POTATO} help ${discord.decor.Emojis.POTATO}`, - description: [ - `when a ${discord.decor.Emojis.POTATO} is dropped, be the first to pick it up by posting a ${discord.decor.Emojis.POTATO} too.`, - '', - '**commands**:', - '- `!potato help` - shows this help message', - '- `!potato` - show off your potato balance', - '- `!potato inspect [user]` - inspect another [user]s potato balance', - '- `!potato top [count]` - top n potato collectors', - '- `!potato gamble ` - gamble potatos', - '- `!potato steal ` - steal potatos from other people', - "- `!potato give ` - give your potatos to other people - if you're feeling kind.", - '- `!potato drop` - drop one of your potatos. the fastest to pick it up gets it', - '- `!potato daily` - claim your daily potato', - '', - '- `!potato lottery` - info about the current lottery pool', - '- `!potato lottery deposit ` - deposit potatos into the lottery pool', - '', - '- `!potato shop list` - list all available shop items', - '- `!potato shop buy ` - buy from the shop' - ].join('\n') - }) - ); + await message.respondEphemeral(`${discord.decor.Emojis.POTATO} help ${discord.decor.Emojis.POTATO} + +when a ${discord.decor.Emojis.POTATO} is dropped, be the first to pick it up by posting a ${discord.decor.Emojis.POTATO} too. + +**commands**: +- \`/potato help\` - shows this help message +- \`/potato inspect [user]\` - inspect another [user]s potato balance or your own +- \`/potato top [count]\` - top n potato collectors +- \`/potato gamble \` - gamble potatoes +- \`/potato steal \` - steal potatoes from other people +- \`/potato give \` - give your potatoes to other people - if you're feeling kind. +- \`/potato drop\` - drop one of your potatoes. the fastest to pick it up gets it +- \`/potato daily\` - claim your daily potato + +- \`/potato shop list\` - list all available shop items +- \`/potato shop buy \` - buy from the shop +`); } ); - potatoSubcommands.on( - { name: '', description: 'potato count' }, - (args) => ({}), - async (message, {}) => { - const target = message.author; - - const currentCount = (await potatoKV.get(target.id)) || 0; - await message.reply( - new discord.Embed({ - title: `${discord.decor.Emojis.POTATO} potato count ${discord.decor.Emojis.POTATO}`, - description: `${message.author.getTag()} has ${currentCount} potato${ - currentCount === 1 ? '' : 's' - }. ${discord.decor.Emojis.POTATO.repeat( - Math.min(currentCount, 100) - )}`, - color: 0x11111c, - thumbnail: { url: message.author.getAvatarUrl() } + potato_slash.register( + { + name: 'inspect', + description: 'inspect how many potatoes someone has', + options: (opt) => ({ + who: opt.guildMember({ + name: 'who', + description: "who's potatoes to inspect", + required: false }) - ); - } - ); - - potatoSubcommands.on( - { name: 'inspect', description: 'potato count' }, - (args) => ({ who: args.user() }), + }) + }, async (message, { who }) => { - const currentCount = (await potatoKV.get(who.id)) || 0; - await message.reply( - new discord.Embed({ - title: `${discord.decor.Emojis.POTATO} potato count ${discord.decor.Emojis.POTATO}`, - description: `${who.getTag()} has ${currentCount} potato${ - currentCount === 1 ? '' : 's' - }. ${discord.decor.Emojis.POTATO.repeat( - Math.min(currentCount, 100) - )}`, - color: 0x11111c, - thumbnail: { url: who.getAvatarUrl() } - }) + var user: discord.User; + if (who) { + user = who.user; + } else { + user = message.member.user; + } + //Had to add that as default subcommands are not possible => No /potato + const currentCount = (await potatoKV.get(user.id)) || 0; + await message.respond( + `${discord.decor.Emojis.POTATO} potato count ${ + discord.decor.Emojis.POTATO + } + +${user.getTag()} has ${currentCount} potato${ + currentCount === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat(Math.min(currentCount, 100))}` ); } ); - potatoSubcommands.on( - { name: 'gamble', description: 'gamble potatos' }, - (args) => ({ count: args.integer() }), - async (message, { count }) => { - if (await potatoKV.get(`gamble-${message.author?.id}`)) - return await message.reply( + potato_slash.register( + { + name: 'gamble', + description: 'gamble with your potatoes', + options: (opt) => ({ + amount: opt.integer({ + name: 'amount', + description: 'how many potatoes you want to gamble', + required: true + }) + }) + }, + async (message, { amount }) => { + if (await potatoKV.get(`gamble-${message.member?.user.id}`)) + return await message.respond( `${discord.decor.Emojis.NO_ENTRY_SIGN} ${discord.decor.Emojis.POTATO} gambling addiction is a serious problem. Regulations require a wait.` ); const currentCount = - (await potatoKV.get(message.author?.id)) || 0; + (await potatoKV.get(message.member?.user.id)) || 0; - if (count > currentCount) - return await message.reply( - 'You can only gamble as many potatos as you have!' + if (amount > currentCount) + return await message.respond( + 'You can only gamble as many potatoes as you have!' ); - if (count > 10 || count < 1) - return await message.reply( - 'You can only gamble between 1 and 10 potatos.' + if (amount > 10 || amount < 1) + return await message.respond( + 'You can only gamble between 1 and 10 potatoes.' ); - await potatoKV.put(`gamble-${message.author?.id}`, true, { - ttl: randomTimeBetween(2 * 60 * 1000, 5 * 60 * 1000) + await potatoKV.put(`gamble-${message.member?.user.id}`, true, { + ttl: randomTimeBetween(20 * 60 * 1000, 40 * 60 * 1000) }); const won = Math.random() > 0.5; - const newCount = currentCount + count * (won ? 1 : -1); - await potatoKV.put(message.author?.id, newCount); - - await message.reply( - new discord.Embed({ - title: `${discord.decor.Emojis.GAME_DIE} ${discord.decor.Emojis.POTATO} ${discord.decor.Emojis.GAME_DIE}`, - description: `Your gambling ${won ? 'paid off' : 'sucked'}, you ${ - won ? 'won' : 'lost' - } ${count} potato${count === 1 ? '' : 's'}, ${ - won ? 'giving you' : 'leaving you with' - } a total of ${newCount} potato${ - newCount === 1 ? '' : 's' - }. ${discord.decor.Emojis.POTATO.repeat(newCount)} ${ - won - ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND - : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND - }`, - color: 0x11111c - }) - ); + const newCount = currentCount + amount * (won ? 1 : -1); + await potatoKV.put(message.member?.user.id, newCount); + + await message.respond(`${discord.decor.Emojis.GAME_DIE} ${ + discord.decor.Emojis.POTATO + } ${discord.decor.Emojis.GAME_DIE} + +Your gambling ${won ? 'paid off' : 'sucked'}, you ${ + won ? 'won' : 'lost' + } ${amount} potato${amount === 1 ? '' : 'es'}, ${ + won ? 'giving you' : 'leaving you with' + } a total of ${newCount} potato${ + newCount === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat(newCount)} ${ + won + ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND + : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND + }`); } ); - - potatoSubcommands.on( - { name: 'steal', description: 'steal potatos' }, - (args) => ({ who: args.user(), count: args.integer() }), + potato_slash.register( + { + name: 'steal', + description: 'Steal potatoes from a fellow collector', + options: (opt) => ({ + who: opt.guildMember({ + name: 'who', + description: 'who to steal from', + required: true + }), + count: opt.integer({ + name: 'amount', + description: ' How much you want to steal' + }) + }) + }, async (message, { who, count }) => { - if (message.author?.id === who.id) - return await message.reply("You can't steal from yourself!"); - if (await potatoKV.get(`steal-${message.author?.id}`)) - return await message.reply( + if (message.member?.user.id === who.user.id) + return await message.respond("You can't steal from yourself!"); + if (await potatoKV.get(`steal-${message.member?.user.id}`)) + return await message.respond( `${discord.decor.Emojis.POLICE_OFFICER} Your potato thief actions are being currently scrutinized. Lay low for a while.` ); const success = Math.random() < 0.25; - const userPotatos = (await potatoKV.get(message.author?.id)) || 0; - const targetPotatos = (await potatoKV.get(who.id)) || 0; + const userPotatos = + (await potatoKV.get(message.member?.user.id)) || 0; + const targetPotatos = (await potatoKV.get(who.user.id)) || 0; if (count > userPotatos) - return await message.reply( - 'You can only steal as many potatos as you have!' + return await message.respond( + 'You can only steal as many potatoes as you have!' ); if (count > targetPotatos) - return await message.reply('That user doesnt have that many potatos!'); + return await message.respond( + 'That user doesnt have that many potatoes!' + ); if (count < 1) - return await message.reply('You need to steal at least one potato.'); + return await message.respond('You need to steal at least one potato.'); if (count > 5) - return await message.reply( + return await message.respond( 'Your small hands can only carry 5 potatos!' ); - await potatoKV.put(`steal-${message.author?.id}`, true, { - ttl: randomTimeBetween(3 * 60 * 1000, 10 * 60 * 1000) + await potatoKV.put(`steal-${message.member?.user.id}`, true, { + ttl: randomTimeBetween(20 * 60 * 1000, 30 * 60 * 1000) }); const newUserPotatos = userPotatos + count * (success ? 1 : -1); const newTargetPotatos = targetPotatos + count * (success ? -1 : 1); - await potatoKV.put(message.author?.id, newUserPotatos); - await potatoKV.put(who.id, newTargetPotatos); - - await message.reply( - new discord.Embed({ - title: `${discord.decor.Emojis.GLOVES} ${discord.decor.Emojis.POTATO} ${discord.decor.Emojis.GLOVES}`, - description: `Your thievery ${success ? 'paid off' : 'sucked'}, you ${ - success ? 'stole' : 'gave' - } ${count} potato${count === 1 ? '' : 's'} ${ - success ? 'from' : 'to' - } ${who.getTag()}, ${ - success ? 'giving you a total of' : 'leaving you with' - } ${newUserPotatos} potato${ - newUserPotatos === 1 ? '' : 's' - }. ${discord.decor.Emojis.POTATO.repeat(newUserPotatos)} ${ - success - ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND - : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND - }`, - color: 0x11111c - }) - ); + await potatoKV.put(message.member?.user.id, newUserPotatos); + await potatoKV.put(who.user.id, newTargetPotatos); + + await message.respond(`${discord.decor.Emojis.GLOVES} ${ + discord.decor.Emojis.POTATO + } ${discord.decor.Emojis.GLOVES} +Your thievery ${success ? 'paid off' : 'sucked'}, you ${ + success ? 'stole' : 'gave' + } ${count} potato${count === 1 ? '' : 'es'} ${ + success ? 'from' : 'to' + } ${who.user.getTag()}, ${ + success ? 'giving you a total of' : 'leaving you with' + } ${newUserPotatos} potato${ + newUserPotatos === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat(newUserPotatos)} ${ + success + ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND + : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND + }`); } ); - potatoSubcommands.on( - { name: 'give', description: 'give potatos to other people' }, - (args) => ({ who: args.user(), count: args.integerOptional() }), + potato_slash.register( + { + name: 'give', + description: 'Give potatoes to a fellow collector', + options: (opt) => ({ + who: opt.guildMember({ + name: 'who', + description: 'who to bless with more potatoes', + required: true + }), + count: opt.integer({ + name: 'amount', + description: ' How much you want to give' + }) + }) + }, async (message, { who, count }) => { - if (message.author?.id === who.id) - return await message.reply("You can't give potatos to yourself!"); - if (who.bot) - return await message.reply("You can't give potatos to bots!"); - const userPotatos = (await potatoKV.get(message.author?.id)) || 0; - const targetPotatos = (await potatoKV.get(who.id)) || 0; + if (message.member?.user.id === who.user.id) + return await message.respond("You can't give potatos to yourself!"); + if (who.user.bot) + return await message.respond("You can't give potatos to bots!"); + const userPotatos = + (await potatoKV.get(message.member?.user.id)) || 0; + const targetPotatos = (await potatoKV.get(who.user.id)) || 0; if (!count && count !== 0) count = 1; if (count > userPotatos) - return await message.reply( + return await message.respond( 'You can only give as many potatos as you have!' ); if (count < 1) - return await message.reply('You need to send at least one potato.'); + return await message.respond('You need to send at least one potato.'); const newUserPotatos = userPotatos - count; const newTargetPotatos = targetPotatos + count; - await potatoKV.put(message.author?.id, newUserPotatos); - await potatoKV.put(who.id, newTargetPotatos); + await potatoKV.put(message.member?.user.id, newUserPotatos); + await potatoKV.put(who.user.id, newTargetPotatos); - await message.reply( - `you gave ${count} potato${ - count === 1 ? '' : 's' - } to ${who.getTag()}, how nice of you.` + await message.respond( + `You gave ${count} potato${ + count === 1 ? '' : 'es' + } to ${who.user.getTag()}, how nice of you.` ); } ); - potatoSubcommands.on( - { name: 'top', description: 'top potatos' }, - (args) => ({ count: args.integerOptional() }), + potato_slash.register( + { + name: 'top', + description: 'View the top potato collector', + options: (opt) => ({ + count: opt.integer({ + name: 'count', + description: 'top x collectors', + required: false + }) + }) + }, async (message, { count }) => { count = Math.min(Math.max(3, count || 10), 20); const items = await potatoKV.items(); @@ -423,16 +434,16 @@ potatoCommands.subcommand('potato', (potatoSubcommands) => { } const ownIndex = sorted.findIndex( - (item) => item.key === message.author.id + (item) => item.key === message.member.user.id ); if (ownIndex >= count) { description += `\` ... \` *${ownIndex - count + 1}* other farmers\n`; description += `\` ${(ownIndex + 1).toString().padStart(2, ' ')} \` **${ - message.author.username - }**#${message.author.discriminator} - ${sorted[ownIndex].value} potato${ - sorted[ownIndex].value === 1 ? '' : 's' - }`; + message.member.user.username + }**#${message.member.user.discriminator} - ${ + sorted[ownIndex].value + } potato${sorted[ownIndex].value === 1 ? '' : 'es'}`; } else if (count > 3) { const { user, potatos } = userMap[count - 1]; description += `\` ${count.toString().padStart(2, ' ')} \` **${ @@ -440,59 +451,71 @@ potatoCommands.subcommand('potato', (potatoSubcommands) => { }**#${user?.discriminator} - ${potatos.toLocaleString()} potatos\n`; } - await message.reply( - new discord.Embed({ - title: `${discord.decor.Emojis.TROPHY} Leaderboard​ ${discord.decor.Emojis.CROWN}`, - description - }) - ); + await message.respond(`${discord.decor.Emojis.TROPHY} Leaderboard​ ${discord.decor.Emojis.CROWN} + +${description}`); } ); - potatoSubcommands.on( + potato_slash.register( { name: 'drop', description: 'drop a potato in the chat' }, - () => ({}), async (message) => { - const userPotatos = (await potatoKV.get(message.author?.id)) || 0; + const userPotatos = + (await potatoKV.get(message.member?.user.id)) || 0; if (!userPotatos) - return await message.reply("you don't have any potatos!"); + return await message.respond("you don't have any potatos!"); const lastPotato = await potatoKV.get('lastPotato'); if (lastPotato) - return await message.reply( + return await message.respond( `there is already an active potato waiting to be picked up in <#${ lastPotato.split('-')[0] }>!` ); - await potatoKV.put(message.author?.id, userPotatos - 1); + await potatoKV.put(message.member?.user.id, userPotatos - 1); - const reply = await message.reply(discord.decor.Emojis.POTATO); + const reply = await message.respond(discord.decor.Emojis.POTATO); const cooldown = randomTimeBetween(3 * 60 * 1000, 20 * 60 * 1000); await potatoKV.put('cooldown', true, { ttl: cooldown }); - await potatoKV.put('lastPotato', `${message.channelId}-${reply.id}`, { - ttl: cooldown - }); + await potatoKV.put( + 'lastPotato', + `${message.channelId}-No id because slash command...`, + { + ttl: cooldown + } + ); } ); - potatoSubcommands.on( + potato_slash.register( { name: 'modify', - description: 'modify a users potatos' + description: 'Modify the potatoes of a fellow collector', + showSourceMessage: false, + options: (opt) => ({ + who: opt.guildMember({ + name: 'who', + description: "who's potatoes to modify", + required: true + }), + count: opt.string({ + name: 'amount', + description: ' How much you want to give/take' + }) + }) }, - (args) => ({ who: args.user(), count: args.string() }), async (message, { who, count }) => { - if (!(await discord.command.filters.isAdministrator().filter(message))) - return await message.reply('missing permissions'); - if (who.bot) - return await message.reply( + if (!message.member.can(discord.Permissions.ADMINISTRATOR)) + return await message.respondEphemeral('missing permissions'); + if (who.user.bot) + return await message.respondEphemeral( 'thats a.. bot. you wanna modify a bots potatos??' ); - const oldCount = (await potatoKV.get(who.id)) || 0; + const oldCount = (await potatoKV.get(who.user.id)) || 0; let newCount = oldCount; if (count.startsWith('+')) newCount += parseInt(count.replace('+', '')); @@ -501,151 +524,52 @@ potatoCommands.subcommand('potato', (potatoSubcommands) => { else newCount = parseInt(count); if (isNaN(newCount as number)) - return await message.reply('invalid count'); + return await message.respond('invalid count'); - await potatoKV.put(who.id, newCount as number); - await message.reply( - `Ok, updated ${who.getTag()}'s potatos to ${newCount}` + await potatoKV.put(who.user.id, newCount as number); + await message.respondEphemeral( + `Ok, updated ${who.user.getTag()}'s potatoes to ${newCount}` ); } ); if (ALLOW_DAILY) - potatoSubcommands.on( - { name: 'daily', description: 'daily potato' }, - () => ({}), + potato_slash.register( + { + name: 'daily', + description: 'Get your daily potato!' + }, async (message) => { - if (await potatoKV.get(`daily-${message.author.id}`)) - return await message.reply('you already claimed your daily potato!'); + if (await potatoKV.get(`daily-${message.member.user.id}`)) + return await message.respond( + 'you already claimed your daily potato!' + ); - await potatoKV.put(`daily-${message.author.id}`, true, { + await potatoKV.put(`daily-${message.member.user.id}`, true, { ttl: Math.ceil(Date.now() / 1000 / 60 / 60 / 24) * 24 * 60 * 60 * 1000 - Date.now() }); const newCount = await potatoKV.transact( - message.author.id, + message.member.user.id, (prev: number | undefined) => (prev || 0) + 1 ); - await message.reply( + await message.respond( `you claimed your daily potato, and now hold onto ${newCount} potatos.` ); } ); - const lottery = potatoSubcommands.subcommandGroup({ - name: 'lottery', - description: 'potato lottery commands' - }); - - setDefaultReply(lottery); - - lottery.on( - { name: '', description: 'pool info' }, - () => ({}), - async (message) => { - const channel = await discord.getGuildTextChannel( - global.POTATO_LOTTERY_CHANNEL - ); - if (!channel) - return await message.reply( - `${discord.decor.Emojis.X} sorry, the lottery has been prohibited by the authorities.` - ); - - const lotteryData = ((await potatoKV.get('lottery')) || {}) as { - [key: string]: number; - }; - await message.reply( - `${ - Object.keys(lotteryData).length - } people are currently bidding a total of ${Object.values( - lotteryData - ).reduce((a, b) => a + b, 0)} potatos${ - lotteryData[message.author.id] - ? `. you are in the pool with ${lotteryData[message.author.id]} ${ - lotteryData[message.author.id] === 1 ? 'potato' : 'potatos' - }` - : '' - }. ${nextDrawText()}` - ); - } - ); - - lottery.on( - { name: 'deposit', description: 'deposits potatos into the lottery pool' }, - (args) => ({ count: args.integer() }), - async (message, { count }) => { - const channel = await discord.getGuildTextChannel( - global.POTATO_LOTTERY_CHANNEL - ); - if (!channel) - return await message.reply( - `${discord.decor.Emojis.X} sorry, the lottery has been prohibited by the authorities.` - ); - - const currentCount = - (await potatoKV.get(message.author?.id)) || 0; - - if (count > currentCount) - return await message.reply( - 'You can only deposit as many potatos as you have!' - ); - - if (count < 1) - return await message.reply('You need to deposit at least 1 potato.'); - - await potatoKV.put(message.author?.id, currentCount - count); - - const lotteryData = await potatoKV.transact( - 'lottery', - (prev: pylon.JsonObject | undefined) => { - const next = {} as { [key: string]: number }; - if (prev) - for (const [key, value] of Object.entries(prev as object)) - next[key] = value; - - next[message.author.id] = - (((prev && prev[message.author.id]) || 0) as number) + count; - - return next; - } - ); - - const totalCount = Object.values(lotteryData as object).reduce( - (a, b) => a + b, - 0 - ); - const gamblerCount = Object.keys(lotteryData as object).length; - - await message.reply( - `you deposited ${count} ${ - count === 1 ? 'potato' : 'potatos' - }, there are ${totalCount} ${ - totalCount === 1 ? 'potato' : 'potatos' - } from ${gamblerCount} ${ - gamblerCount === 1 ? 'gambler' : 'gamblers' - } in the pool${ - lotteryData![message.author.id] !== count - ? ` (${lotteryData![message.author.id]} of those are yours)` - : '' - }. ${nextDrawText()}` - ); - } - ); - - const shop = potatoSubcommands.subcommandGroup({ + const shop = potato_slash.registerGroup({ name: 'shop', - description: 'potato shop commands' + description: 'Buy stuff with potatoes in the potato shop' }); - setDefaultReply(shop); - - shop.on( - { name: 'list', aliases: [''], description: 'list all potato shop items' }, - () => ({}), + shop.register( + { name: 'list', description: 'list all potato shop items' }, async (message) => { if (!Object.keys(SHOP_ITEMS).length) - return await message.reply('no items currently available, sorry!'); + return await message.respond('no items currently available, sorry!'); const fields = await Promise.all( Object.entries(SHOP_ITEMS) @@ -657,38 +581,48 @@ potatoCommands.subcommand('potato', (potatoSubcommands) => { })) ); - await message.reply( - new discord.Embed({ - title: 'Potato Shop', - description: `**Available Items**\nuse \`${potatoCommands.commandPrefixes[0]}potato shop buy \` to purchase an item listed here`, - fields - }) - ); + let embed = new discord.Embed({ + title: 'Potato Shop', + description: + '**Available Items**\nuse `/potato shop buy ` to purchase an item listed here', + fields: fields + }); + + await message.respond({ embeds: [embed] }); } ); - shop.on( + shop.register( { name: 'buy', - aliases: ['purchase'], - description: 'purchase a potato shop item' + description: 'purchase a potato shop item', + options: (opt) => ({ + item: opt.string({ + name: 'item', + description: 'What item to buy', + required: true + }) + }) }, - (args) => ({ item: args.text() }), async (message, { item }) => { const itemObj = SHOP_ITEMS[item]; if (!itemObj || !itemObj.enabled) - return await message.reply( - `invalid potato item. use \`${potatoCommands.commandPrefixes[0]}potato shop list\` to get a list of all available items` + return await message.respond( + `invalid potato item. use \`/potato shop list\` to get a list of all available items` ); const purchases = ((await potatoKV.get('shop')) || - []) as { user: string; item: string; expiresAt: number | undefined }[]; + []) as { + user: string; + item: string; + expiresAt: number | undefined; + }[]; const purchase = purchases.find( (purchase) => - purchase.user === message.author.id && purchase.item === item + purchase.user === message.member.user.id && purchase.item === item ); if (purchase) - return message.reply( + return message.respond( `You already bought this item!${ purchase.expiresAt ? ` You can buy it again on ${new Date( @@ -698,22 +632,23 @@ potatoCommands.subcommand('potato', (potatoSubcommands) => { }` ); - const userPotatos = (await potatoKV.get(message.author.id)) || 0; + const userPotatos = + (await potatoKV.get(message.member.user.id)) || 0; if (userPotatos < itemObj.price) - return await message.reply( + return await message.respond( "you don't have enough potatos for that item!" ); try { - await itemObj.onPurchase(message.author); + await itemObj.onPurchase(message.member.user); } catch (err) { - return await message.reply( + return await message.respond( `There was an error processing your order: ${err.message}` ); } await potatoKV.transact( - message.author.id, + message.member.user.id, (prev: number | undefined) => (prev || 0) - itemObj.price ); @@ -723,7 +658,7 @@ potatoCommands.subcommand('potato', (potatoSubcommands) => { [ ...(prev || []), { - user: message.author.id, + user: message.member.user.id, item, expiresAt: itemObj.duration ? Date.now() + itemObj.duration @@ -732,56 +667,687 @@ potatoCommands.subcommand('potato', (potatoSubcommands) => { ] as pylon.JsonArray ); - await message.reply(`You successfully bought \`${item}\`!`); + await message.respond(`You successfully bought \`${item}\`!`); } ); -}); +} else { + const nextDrawText = () => { + const nextDraw = + (Math.ceil(Date.now() / 1000 / 60 / POTATO_LOTTERY_TIME_MINUTES) * + 1000 * + 60 * + POTATO_LOTTERY_TIME_MINUTES - + Date.now()) / + 1000 / + 60; + + const minutes = Math.floor(nextDraw); + const seconds = Math.floor((nextDraw % 1) * 60); + return `next draw is in ${minutes} ${ + minutes === 1 ? 'minute' : 'minutes' + } and ${seconds} ${seconds === 1 ? 'second' : 'seconds'}`; + }; + const setDefaultReply = (commandGroup: discord.command.CommandGroup) => { + commandGroup.default( + () => ({}), + async (message) => + await message.reply( + `${discord.decor.Emojis.NO_ENTRY} unknown potato command, try \`${potatoCommands.commandPrefixes[0]}potato help\`` + ) + ); + }; + potatoCommands.subcommand('potato', (potatoSubcommands) => { + setDefaultReply(potatoSubcommands); + + potatoSubcommands.on( + { name: 'help', description: 'potato help' }, + () => ({}), + async (message) => { + await message.reply( + new discord.Embed({ + title: `${discord.decor.Emojis.POTATO} help ${discord.decor.Emojis.POTATO}`, + description: [ + `when a ${discord.decor.Emojis.POTATO} is dropped, be the first to pick it up by posting a ${discord.decor.Emojis.POTATO} too.`, + '', + '**commands**:', + '- `!potato help` - shows this help message', + '- `!potato` - show off your potato balance', + '- `!potato inspect [user]` - inspect another [user]s potato balance', + '- `!potato top [count]` - top n potato collectors', + '- `!potato gamble ` - gamble potatos', + '- `!potato steal ` - steal potatos from other people', + "- `!potato give ` - give your potatos to other people - if you're feeling kind.", + '- `!potato drop` - drop one of your potatos. the fastest to pick it up gets it', + '- `!potato daily` - claim your daily potato', + '', + '- `!potato lottery` - info about the current lottery pool', + '- `!potato lottery deposit ` - deposit potatoes into the lottery pool', + '', + '- `!potato shop list` - list all available shop items', + '- `!potato shop buy ` - buy from the shop' + ].join('\n') + }) + ); + } + ); -pylon.tasks.cron( - 'lottery', - `0 0/${POTATO_LOTTERY_TIME_MINUTES} * * * * *`, - async () => { - const channel = await discord.getGuildTextChannel( - global.POTATO_LOTTERY_CHANNEL + potatoSubcommands.on( + { name: '', description: 'potato count' }, + (args) => ({}), + async (message, {}) => { + const target = message.author; + + const currentCount = (await potatoKV.get(target.id)) || 0; + await message.reply( + new discord.Embed({ + title: `${discord.decor.Emojis.POTATO} potato count ${discord.decor.Emojis.POTATO}`, + description: `${message.author.getTag()} has ${currentCount} potato${ + currentCount === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat( + Math.min(currentCount, 100) + )}`, + color: 0x11111c, + thumbnail: { url: message.author.getAvatarUrl() } + }) + ); + } + ); + + potatoSubcommands.on( + { name: 'inspect', description: 'potato count' }, + (args) => ({ who: args.user() }), + async (message, { who }) => { + const currentCount = (await potatoKV.get(who.id)) || 0; + await message.reply( + new discord.Embed({ + title: `${discord.decor.Emojis.POTATO} potato count ${discord.decor.Emojis.POTATO}`, + description: `${who.getTag()} has ${currentCount} potato${ + currentCount === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat( + Math.min(currentCount, 100) + )}`, + color: 0x11111c, + thumbnail: { url: who.getAvatarUrl() } + }) + ); + } ); - if (!channel) return; - let lotteryData = (await potatoKV.get('lottery')) as - | { [key: string]: number } - | undefined; - if (!lotteryData || Object.keys(lotteryData).length < 2) return; + potatoSubcommands.on( + { name: 'gamble', description: 'gamble potatoes' }, + (args) => ({ count: args.integer() }), + async (message, { count }) => { + if (await potatoKV.get(`gamble-${message.author?.id}`)) + return await message.reply( + `${discord.decor.Emojis.NO_ENTRY_SIGN} ${discord.decor.Emojis.POTATO} gambling addiction is a serious problem. Regulations require a wait.` + ); + + const currentCount = + (await potatoKV.get(message.author?.id)) || 0; + + if (count > currentCount) + return await message.reply( + 'You can only gamble as many potatoes as you have!' + ); + + if (count > 10 || count < 1) + return await message.reply( + 'You can only gamble between 1 and 10 potatoes.' + ); + + await potatoKV.put(`gamble-${message.author?.id}`, true, { + ttl: randomTimeBetween(2 * 60 * 1000, 5 * 60 * 1000) + }); + + const won = Math.random() > 0.5; + const newCount = currentCount + count * (won ? 1 : -1); + await potatoKV.put(message.author?.id, newCount); + + await message.reply( + new discord.Embed({ + title: `${discord.decor.Emojis.GAME_DIE} ${discord.decor.Emojis.POTATO} ${discord.decor.Emojis.GAME_DIE}`, + description: `Your gambling ${won ? 'paid off' : 'sucked'}, you ${ + won ? 'won' : 'lost' + } ${count} potato${count === 1 ? '' : 's'}, ${ + won ? 'giving you' : 'leaving you with' + } a total of ${newCount} potato${ + newCount === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat(newCount)} ${ + won + ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND + : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND + }`, + color: 0x11111c + }) + ); + } + ); + + potatoSubcommands.on( + { name: 'steal', description: 'steal potatos' }, + (args) => ({ who: args.user(), count: args.integer() }), + async (message, { who, count }) => { + if (message.author?.id === who.id) + return await message.reply("You can't steal from yourself!"); + if (await potatoKV.get(`steal-${message.author?.id}`)) + return await message.reply( + `${discord.decor.Emojis.POLICE_OFFICER} Your potato thief actions are being currently scrutinized. Lay low for a while.` + ); + const success = Math.random() < 0.25; + const userPotatos = + (await potatoKV.get(message.author?.id)) || 0; + const targetPotatos = (await potatoKV.get(who.id)) || 0; + + if (count > userPotatos) + return await message.reply( + 'You can only steal as many potatos as you have!' + ); + + if (count > targetPotatos) + return await message.reply( + 'That user doesnt have that many potatoes!' + ); + + if (count < 1) + return await message.reply('You need to steal at least one potato.'); + + if (count > 5) + return await message.reply( + 'Your small hands can only carry 5 potatoes!' + ); + + await potatoKV.put(`steal-${message.author?.id}`, true, { + ttl: randomTimeBetween(3 * 60 * 1000, 10 * 60 * 1000) + }); + + const newUserPotatos = userPotatos + count * (success ? 1 : -1); + const newTargetPotatos = targetPotatos + count * (success ? -1 : 1); + + await potatoKV.put(message.author?.id, newUserPotatos); + await potatoKV.put(who.id, newTargetPotatos); + + await message.reply( + new discord.Embed({ + title: `${discord.decor.Emojis.GLOVES} ${discord.decor.Emojis.POTATO} ${discord.decor.Emojis.GLOVES}`, + description: `Your thievery ${ + success ? 'paid off' : 'sucked' + }, you ${success ? 'stole' : 'gave'} ${count} potato${ + count === 1 ? '' : 'es' + } ${success ? 'from' : 'to'} ${who.getTag()}, ${ + success ? 'giving you a total of' : 'leaving you with' + } ${newUserPotatos} potato${ + newUserPotatos === 1 ? '' : 'es' + }. ${discord.decor.Emojis.POTATO.repeat(newUserPotatos)} ${ + success + ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND + : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND + }`, + color: 0x11111c + }) + ); + } + ); + + potatoSubcommands.on( + { name: 'give', description: 'give potatoes to other people' }, + (args) => ({ who: args.user(), count: args.integerOptional() }), + async (message, { who, count }) => { + if (message.author?.id === who.id) + return await message.reply("You can't give potatoes to yourself!"); + if (who.bot) + return await message.reply("You can't give potatoes to bots!"); + const userPotatos = + (await potatoKV.get(message.author?.id)) || 0; + const targetPotatos = (await potatoKV.get(who.id)) || 0; + + if (!count && count !== 0) count = 1; + + if (count > userPotatos) + return await message.reply( + 'You can only give as many potatoes as you have!' + ); + + if (count < 1) + return await message.reply('You need to send at least one potato.'); + + const newUserPotatos = userPotatos - count; + const newTargetPotatos = targetPotatos + count; - const msg = await channel.sendMessage( - `the potato gods are choosing a lottery winner...` + await potatoKV.put(message.author?.id, newUserPotatos); + await potatoKV.put(who.id, newTargetPotatos); + + await message.reply( + `you gave ${count} potato${ + count === 1 ? '' : 'es' + } to ${who.getTag()}, how nice of you.` + ); + } ); - await sleep(Math.random() * 10000 + 5000); - await msg.delete().catch(() => {}); - - lotteryData = (await potatoKV.get('lottery')) as - | { [key: string]: number } - | undefined; - if (!lotteryData || Object.keys(lotteryData).length < 2) return; - - const idList = [] as string[]; - for (const [key, value] of Object.entries(lotteryData)) - idList.push(...(new Array(value).fill(key) as string[])); - const randomID = idList[Math.floor(Math.random() * idList.length)]; - - const newCount = await potatoKV.transact( - randomID, - (prev: number | undefined) => (prev || 0) + idList.length + + potatoSubcommands.on( + { name: 'top', description: 'top potatoes' }, + (args) => ({ count: args.integerOptional() }), + async (message, { count }) => { + count = Math.min(Math.max(3, count || 10), 20); + const items = await potatoKV.items(); + const filtered = items.filter( + (entry) => + !isNaN((entry.key as unknown) as number) && + ((entry.value as unknown) as number) > 0 + ); + const sorted = filtered.sort( + (a, b) => (b.value as number) - (a.value as number) + ); + const top = sorted.slice(0, count); + count = top.length; + const userMap = await Promise.all( + top.map((entry) => + discord + .getUser(entry.key) + .then((user) => ({ user, potatos: entry.value as number })) + ) + ); + + let description = `${discord.decor.Emojis.POTATO} **${filtered + .reduce((a, b) => a + (b.value as number), 0) + .toLocaleString()}**\n`; + description += `${discord.decor.Emojis.MAN_FARMER} **${filtered.length}**\n\n`; + description += `${discord.decor.Emojis.CHART_WITH_UPWARDS_TREND} **Ranks** ${discord.decor.Emojis.MUSCLE}\n`; + + for (const entry of userMap.slice(0, Math.max(3, count - 1))) { + const { user, potatos } = entry; + const place = userMap.indexOf(entry); + description += `\` ${MEDALS[place] || + `${(place + 1).toString().padStart(2, ' ')} `} \` **${ + user?.username + }**#${user?.discriminator} - ${potatos.toLocaleString()} potatoes\n`; + } + + const ownIndex = sorted.findIndex( + (item) => item.key === message.author.id + ); + + if (ownIndex >= count) { + description += `\` ... \` *${ownIndex - count + 1}* other farmers\n`; + description += `\` ${(ownIndex + 1) + .toString() + .padStart(2, ' ')} \` **${message.author.username}**#${ + message.author.discriminator + } - ${sorted[ownIndex].value} potato${ + sorted[ownIndex].value === 1 ? '' : 'es' + }`; + } else if (count > 3) { + const { user, potatos } = userMap[count - 1]; + description += `\` ${count.toString().padStart(2, ' ')} \` **${ + user?.username + }**#${user?.discriminator} - ${potatos.toLocaleString()} potatos\n`; + } + + await message.reply( + new discord.Embed({ + title: `${discord.decor.Emojis.TROPHY} Leaderboard​ ${discord.decor.Emojis.CROWN}`, + description + }) + ); + } ); - await potatoKV.delete('lottery'); - await channel.sendMessage( - `the potato gods have chosen <@${randomID}> as a lottery winner (${Math.floor( - (lotteryData[randomID] / idList.length) * 1000 - ) / 10}% chance)! they won ${ - idList.length - } potatos, giving them a total of ${newCount}.` + + potatoSubcommands.on( + { name: 'drop', description: 'drop a potato in the chat' }, + () => ({}), + async (message) => { + const userPotatos = + (await potatoKV.get(message.author?.id)) || 0; + + if (!userPotatos) + return await message.reply("you don't have any potatoes!"); + + const lastPotato = await potatoKV.get('lastPotato'); + if (lastPotato) + return await message.reply( + `there is already an active potato waiting to be picked up in <#${ + lastPotato.split('-')[0] + }>!` + ); + + await potatoKV.put(message.author?.id, userPotatos - 1); + + const reply = await message.reply(discord.decor.Emojis.POTATO); + + const cooldown = randomTimeBetween(3 * 60 * 1000, 20 * 60 * 1000); + + await potatoKV.put('cooldown', true, { ttl: cooldown }); + await potatoKV.put('lastPotato', `${message.channelId}-${reply.id}`, { + ttl: cooldown + }); + } ); - } -); + potatoSubcommands.on( + { + name: 'modify', + description: 'modify a users potatoes' + }, + (args) => ({ who: args.user(), count: args.string() }), + async (message, { who, count }) => { + if (!(await discord.command.filters.isAdministrator().filter(message))) + return await message.reply('missing permissions'); + if (who.bot) + return await message.reply( + 'thats a.. bot. you wanna modify a bots potatos??' + ); + const oldCount = (await potatoKV.get(who.id)) || 0; + + let newCount = oldCount; + if (count.startsWith('+')) newCount += parseInt(count.replace('+', '')); + else if (count.startsWith('-')) + newCount -= parseInt(count.replace('-', '')); + else newCount = parseInt(count); + + if (isNaN(newCount as number)) + return await message.reply('invalid count'); + + await potatoKV.put(who.id, newCount as number); + await message.reply( + `Ok, updated ${who.getTag()}'s potatoes to ${newCount}` + ); + } + ); + + if (ALLOW_DAILY) + potatoSubcommands.on( + { name: 'daily', description: 'daily potato' }, + () => ({}), + async (message) => { + if (await potatoKV.get(`daily-${message.author.id}`)) + return await message.reply( + 'you already claimed your daily potato!' + ); + + await potatoKV.put(`daily-${message.author.id}`, true, { + ttl: + Math.ceil(Date.now() / 1000 / 60 / 60 / 24) * + 24 * + 60 * + 60 * + 1000 - + Date.now() + }); + const newCount = await potatoKV.transact( + message.author.id, + (prev: number | undefined) => (prev || 0) + 1 + ); + await message.reply( + `you claimed your daily potato, and now hold onto ${newCount} potatoes.` + ); + } + ); + + const lottery = potatoSubcommands.subcommandGroup({ + name: 'lottery', + description: 'potato lottery commands' + }); + + setDefaultReply(lottery); + + lottery.on( + { name: '', description: 'pool info' }, + () => ({}), + async (message) => { + const channel = await discord.getGuildTextChannel( + global.POTATO_LOTTERY_CHANNEL + ); + if (!channel) + return await message.reply( + `${discord.decor.Emojis.X} sorry, the lottery has been prohibited by the authorities.` + ); + + const lotteryData = ((await potatoKV.get('lottery')) || {}) as { + [key: string]: number; + }; + await message.reply( + `${ + Object.keys(lotteryData).length + } people are currently bidding a total of ${Object.values( + lotteryData + ).reduce((a, b) => a + b, 0)} potatoes${ + lotteryData[message.author.id] + ? `. you are in the pool with ${lotteryData[message.author.id]} ${ + lotteryData[message.author.id] === 1 ? 'potato' : 'potatoes' + }` + : '' + }. ${nextDrawText()}` + ); + } + ); + + lottery.on( + { + name: 'deposit', + description: 'deposits potatoes into the lottery pool' + }, + (args) => ({ count: args.integer() }), + async (message, { count }) => { + const channel = await discord.getGuildTextChannel( + global.POTATO_LOTTERY_CHANNEL + ); + if (!channel) + return await message.reply( + `${discord.decor.Emojis.X} sorry, the lottery has been prohibited by the authorities.` + ); + + const currentCount = + (await potatoKV.get(message.author?.id)) || 0; + + if (count > currentCount) + return await message.reply( + 'You can only deposit as many potatoes as you have!' + ); + + if (count < 1) + return await message.reply('You need to deposit at least 1 potato.'); + + await potatoKV.put(message.author?.id, currentCount - count); + + const lotteryData = await potatoKV.transact( + 'lottery', + (prev: pylon.JsonObject | undefined) => { + const next = {} as { [key: string]: number }; + if (prev) + for (const [key, value] of Object.entries(prev as object)) + next[key] = value; + + next[message.author.id] = + (((prev && prev[message.author.id]) || 0) as number) + count; + + return next; + } + ); + + const totalCount = Object.values(lotteryData as object).reduce( + (a, b) => a + b, + 0 + ); + const gamblerCount = Object.keys(lotteryData as object).length; + + await message.reply( + `you deposited ${count} ${ + count === 1 ? 'potato' : 'potatoes' + }, there are ${totalCount} ${ + totalCount === 1 ? 'potato' : 'potatoes' + } from ${gamblerCount} ${ + gamblerCount === 1 ? 'gambler' : 'gamblers' + } in the pool${ + lotteryData![message.author.id] !== count + ? ` (${lotteryData![message.author.id]} of those are yours)` + : '' + }. ${nextDrawText()}` + ); + } + ); + + const shop = potatoSubcommands.subcommandGroup({ + name: 'shop', + description: 'potato shop commands' + }); + + setDefaultReply(shop); + + shop.on( + { + name: 'list', + aliases: [''], + description: 'list all potato shop items' + }, + () => ({}), + async (message) => { + if (!Object.keys(SHOP_ITEMS).length) + return await message.reply('no items currently available, sorry!'); + + const fields = await Promise.all( + Object.entries(SHOP_ITEMS) + .filter(([, item]) => item.enabled) + .map(async ([name, item]) => ({ + name: `${name} - ${item.price} ${discord.decor.Emojis.POTATO}`, + value: item.description, + inline: true + })) + ); + + await message.reply( + new discord.Embed({ + title: 'Potato Shop', + description: `**Available Items**\nuse \`${ + (potatoCommands as any).commandPrefixes[0] + }potato shop buy \` to purchase an item listed here`, + fields + }) + ); + } + ); + + shop.on( + { + name: 'buy', + aliases: ['purchase'], + description: 'purchase a potato shop item' + }, + (args) => ({ item: args.text() }), + async (message, { item }) => { + const itemObj = SHOP_ITEMS[item]; + if (!itemObj || !itemObj.enabled) + return await message.reply( + `invalid potato item. use \`${ + (potatoCommands as any).commandPrefixes[0] + }potato shop list\` to get a list of all available items` + ); + + const purchases = ((await potatoKV.get('shop')) || + []) as { + user: string; + item: string; + expiresAt: number | undefined; + }[]; + const purchase = purchases.find( + (purchase) => + purchase.user === message.author.id && purchase.item === item + ); + if (purchase) + return message.reply( + `You already bought this item!${ + purchase.expiresAt + ? ` You can buy it again on ${new Date( + purchase.expiresAt + ).toUTCString()}` + : '' + }` + ); + + const userPotatos = + (await potatoKV.get(message.author.id)) || 0; + if (userPotatos < itemObj.price) + return await message.reply( + "you don't have enough potatoes for that item!" + ); + + try { + await itemObj.onPurchase(message.author); + } catch (err) { + return await message.reply( + `There was an error processing your order: ${err.message}` + ); + } + + await potatoKV.transact( + message.author.id, + (prev: number | undefined) => (prev || 0) - itemObj.price + ); + + await potatoKV.transact( + 'shop', + (prev: pylon.JsonArray | undefined) => + [ + ...(prev || []), + { + user: message.author.id, + item, + expiresAt: itemObj.duration + ? Date.now() + itemObj.duration + : undefined + } + ] as pylon.JsonArray + ); + + await message.reply(`You successfully bought \`${item}\`!`); + } + ); + }); + + pylon.tasks.cron( + 'lottery', + `0 0/${POTATO_LOTTERY_TIME_MINUTES} * * * * *`, + async () => { + const channel = await discord.getGuildTextChannel( + global.POTATO_LOTTERY_CHANNEL + ); + if (!channel) return; + + let lotteryData = (await potatoKV.get('lottery')) as + | { [key: string]: number } + | undefined; + if (!lotteryData || Object.keys(lotteryData).length < 2) return; + + const msg = await channel.sendMessage( + `the potato gods are choosing a lottery winner...` + ); + await sleep(Math.random() * 10000 + 5000); + await msg.delete().catch(() => {}); + + lotteryData = (await potatoKV.get('lottery')) as + | { [key: string]: number } + | undefined; + if (!lotteryData || Object.keys(lotteryData).length < 2) return; + + const idList = [] as string[]; + for (const [key, value] of Object.entries(lotteryData)) + idList.push(...(new Array(value).fill(key) as string[])); + const randomID = idList[Math.floor(Math.random() * idList.length)]; + + const newCount = await potatoKV.transact( + randomID, + (prev: number | undefined) => (prev || 0) + idList.length + ); + await potatoKV.delete('lottery'); + await channel.sendMessage( + `the potato gods have chosen <@${randomID}> as a lottery winner (${Math.floor( + (lotteryData[randomID] / idList.length) * 1000 + ) / 10}% chance)! they won ${ + idList.length + } potatoes, giving them a total of ${newCount}.` + ); + } + ); +} pylon.tasks.cron('shop', '0 0/5 * * * * *', async () => { const purchases = ((await potatoKV.get('shop')) || []) as { user: string; From e5a40046e44cbe2d2789374ea158f5ab755af456 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Wed, 24 Feb 2021 15:33:56 +0100 Subject: [PATCH 18/23] Delete slash_potato.ts --- slash_potato.ts | 682 ------------------------------------------------ 1 file changed, 682 deletions(-) delete mode 100644 slash_potato.ts diff --git a/slash_potato.ts b/slash_potato.ts deleted file mode 100644 index 569aeb6..0000000 --- a/slash_potato.ts +++ /dev/null @@ -1,682 +0,0 @@ -const potato_slash = discord.interactions.commands.registerGroup({ - name: 'potato', - description: 'Potato commands' -}); - -const ALLOW_DAILY = true; -const SHOP_ITEMS = { - 'potato farmer': { - price: 1, - description: 'gives you the potato farmer role for 24h', - async onPurchase(user: discord.User) { - const guild = await discord.getGuild(); - const member = await guild.getMember(user.id); - const role = await guild - .getRoles() - .then((roles) => roles.find((role) => role.name === 'potato farmer')); - if (!member || !role) throw new Error('invalid role or member'); - await member.addRole(role.id); - }, - async onExpire(user: discord.User) { - const guild = await discord.getGuild(); - const member = await guild.getMember(user.id); - const role = await guild - .getRoles() - .then((roles) => roles.find((role) => role.name === 'potato farmer')); - if (!member || !role || !member.roles.includes(role.id)) return; - - await member.removeRole(role.id); - }, - enabled: true, - duration: 24 * 60 * 60 * 1000 // 24 hours, checked in 5 minute intervals - } -} as { - [key: string]: { - price: number; - duration: number | undefined; - description: string; - enabled: boolean; - onPurchase: Function; - onExpire: Function; - }; -}; -const MEDALS = [ - discord.decor.Emojis.FIRST_PLACE_MEDAL, - discord.decor.Emojis.SECOND_PLACE_MEDAL, - discord.decor.Emojis.THIRD_PLACE_MEDAL -]; - -const potatoKV = new pylon.KVNamespace('potato'); -const randomTimeBetween = (min: number, max: number) => - Math.round(Math.random() * (max - min) + min); - -discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { - if (!message.author || message.author.bot) return; - - if (await potatoKV.get('cooldown')) { - if (message.content === discord.decor.Emojis.POTATO) { - const [lastChannel, potatoId] = - (await potatoKV.get('lastPotato'))?.split('-') || []; - if (lastChannel !== message.channelId) return; - - try { - await message - .getChannel() - .then((c) => c.getMessage(potatoId)) - .then((m) => m?.delete()) - .catch(() => {}); - - await message.delete().catch(() => {}); - } catch (nothing) {} - - const poisonous = Math.random() < 0.01; - - const oldCount = (await potatoKV.get(message.author.id)) || 0; - const newCount = Math.max( - 0, - oldCount + - (poisonous - ? -Math.max( - 1, - Math.min(10, Math.floor((Math.random() * oldCount) / 4)) - ) - : 1) - ); - - await potatoKV.put(message.author.id, newCount); - await potatoKV.delete('lastPotato'); - await message.reply( - new discord.Embed({ - title: `${ - poisonous ? discord.decor.Emojis.SKULL : discord.decor.Emojis.POTATO - } potato claimed ${discord.decor.Emojis.POTATO}`, - description: `${message.author.getTag()} ${ - poisonous - ? `tried to pick up a poisonous potato, poisoning ${oldCount - - newCount} potatos in the process` - : 'has claimed a potato' - }, and now holds onto ${newCount} potato${ - newCount === 1 ? '' : 'es' - }.`, - color: 0x11111c, - thumbnail: { url: message.author.getAvatarUrl() }, - footer: { - text: poisonous - ? '' - : "to the rest of you, can't catch em all, right?" - } - }) - ); - } - - return; - } else { - const [lastChannel, potatoId] = - (await potatoKV.get('lastPotato'))?.split('-') || []; - - await discord - .getGuild() - .then( - (g) => - g.getChannel(lastChannel) as Promise< - discord.GuildTextChannel | undefined - > - ) - .then((c) => c?.getMessage(potatoId)) - .then((m) => m?.delete()) - .catch(() => {}); - } - - if (Math.random() > 0.3) return; - - const reply = await message.reply(discord.decor.Emojis.POTATO); - - const cooldown = randomTimeBetween(3 * 60 * 1000, 20 * 60 * 1000); - - await potatoKV.put('cooldown', true, { ttl: cooldown }); - await potatoKV.put('lastPotato', `${message.channelId}-${reply.id}`); -}); - -potato_slash.register( - { - name: 'help', - description: 'Displays usable potato commands' - }, - async (message) => { - await message.respondEphemeral(`${discord.decor.Emojis.POTATO} help ${discord.decor.Emojis.POTATO} - -when a ${discord.decor.Emojis.POTATO} is dropped, be the first to pick it up by posting a ${discord.decor.Emojis.POTATO} too. - -**commands**: -- \`/potato help\` - shows this help message -- \`/potato inspect [user]\` - inspect another [user]s potato balance or your own -- \`/potato top [count]\` - top n potato collectors -- \`/potato gamble \` - gamble potatoes -- \`/potato steal \` - steal potatoes from other people -- \`/potato give \` - give your potatoes to other people - if you're feeling kind. -- \`/potato drop\` - drop one of your potatoes. the fastest to pick it up gets it -- \`/potato daily\` - claim your daily potato - -- \`/potato shop list\` - list all available shop items -- \`/potato shop buy \` - buy from the shop -`); - } -); - -potato_slash.register( - { - name: 'inspect', - description: 'inspect how many potatoes someone has', - options: (opt) => ({ - who: opt.guildMember({ - name: 'who', - description: "who's potatoes to inspect", - required: false - }) - }) - }, - async (message, { who }) => { - var user: discord.User; - if (who) { - user = who.user; - } else { - user = message.member.user; - } - //Had to add that as default subcommands are not possible => No /potato - const currentCount = (await potatoKV.get(user.id)) || 0; - await message.respond( - `${discord.decor.Emojis.POTATO} potato count ${ - discord.decor.Emojis.POTATO - } - -${user.getTag()} has ${currentCount} potato${ - currentCount === 1 ? '' : 'es' - }. ${discord.decor.Emojis.POTATO.repeat(Math.min(currentCount, 100))}` - ); - } -); - -potato_slash.register( - { - name: 'gamble', - description: 'gamble with your potatoes', - options: (opt) => ({ - amount: opt.integer({ - name: 'amount', - description: 'how many potatoes you want to gamble', - required: true - }) - }) - }, - async (message, { amount }) => { - if (await potatoKV.get(`gamble-${message.member?.user.id}`)) - return await message.respond( - `${discord.decor.Emojis.NO_ENTRY_SIGN} ${discord.decor.Emojis.POTATO} gambling addiction is a serious problem. Regulations require a wait.` - ); - - const currentCount = - (await potatoKV.get(message.member?.user.id)) || 0; - - if (amount > currentCount) - return await message.respond( - 'You can only gamble as many potatoes as you have!' - ); - - if (amount > 10 || amount < 1) - return await message.respond( - 'You can only gamble between 1 and 10 potatoes.' - ); - - await potatoKV.put(`gamble-${message.member?.user.id}`, true, { - ttl: randomTimeBetween(2 * 60 * 1000, 5 * 60 * 1000) - }); - - const won = Math.random() > 0.5; - const newCount = currentCount + amount * (won ? 1 : -1); - await potatoKV.put(message.member?.user.id, newCount); - - await message.respond(`${discord.decor.Emojis.GAME_DIE} ${ - discord.decor.Emojis.POTATO - } ${discord.decor.Emojis.GAME_DIE} - -Your gambling ${won ? 'paid off' : 'sucked'}, you ${ - won ? 'won' : 'lost' - } ${amount} potato${amount === 1 ? '' : 'es'}, ${ - won ? 'giving you' : 'leaving you with' - } a total of ${newCount} potato${ - newCount === 1 ? '' : 'es' - }. ${discord.decor.Emojis.POTATO.repeat(newCount)} ${ - won - ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND - : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND - }`); - } -); -potato_slash.register( - { - name: 'steal', - description: 'Steal potatoes from a fellow collector', - options: (opt) => ({ - who: opt.guildMember({ - name: 'who', - description: 'who to steal from', - required: true - }), - count: opt.integer({ - name: 'amount', - description: ' How much you want to steal' - }) - }) - }, - async (message, { who, count }) => { - if (message.member?.user.id === who.user.id) - return await message.respond("You can't steal from yourself!"); - if (await potatoKV.get(`steal-${message.member?.user.id}`)) - return await message.respond( - `${discord.decor.Emojis.POLICE_OFFICER} Your potato thief actions are being currently scrutinized. Lay low for a while.` - ); - const success = Math.random() < 0.25; - const userPotatos = - (await potatoKV.get(message.member?.user.id)) || 0; - const targetPotatos = (await potatoKV.get(who.user.id)) || 0; - - if (count > userPotatos) - return await message.respond( - 'You can only steal as many potatoes as you have!' - ); - - if (count > targetPotatos) - return await message.respond('That user doesnt have that many potatoes!'); - - if (count < 1) - return await message.respond('You need to steal at least one potato.'); - - if (count > 5) - return await message.respond( - 'Your small hands can only carry 5 potatos!' - ); - - await potatoKV.put(`steal-${message.member?.user.id}`, true, { - ttl: randomTimeBetween(3 * 60 * 1000, 10 * 60 * 1000) - }); - - const newUserPotatos = userPotatos + count * (success ? 1 : -1); - const newTargetPotatos = targetPotatos + count * (success ? -1 : 1); - - await potatoKV.put(message.member?.user.id, newUserPotatos); - await potatoKV.put(who.user.id, newTargetPotatos); - - await message.respond(`${discord.decor.Emojis.GLOVES} ${ - discord.decor.Emojis.POTATO - } ${discord.decor.Emojis.GLOVES} -Your thievery ${success ? 'paid off' : 'sucked'}, you ${ - success ? 'stole' : 'gave' - } ${count} potato${count === 1 ? '' : 'es'} ${ - success ? 'from' : 'to' - } ${who.user.getTag()}, ${ - success ? 'giving you a total of' : 'leaving you with' - } ${newUserPotatos} potato${ - newUserPotatos === 1 ? '' : 'es' - }. ${discord.decor.Emojis.POTATO.repeat(newUserPotatos)} ${ - success - ? discord.decor.Emojis.CHART_WITH_UPWARDS_TREND - : discord.decor.Emojis.CHART_WITH_DOWNWARDS_TREND - }`); - } -); - -potato_slash.register( - { - name: 'give', - description: 'Give potatoes to a fellow collector', - options: (opt) => ({ - who: opt.guildMember({ - name: 'who', - description: 'who to bless with more potatoes', - required: true - }), - count: opt.integer({ - name: 'amount', - description: ' How much you want to give' - }) - }) - }, - async (message, { who, count }) => { - if (message.member?.user.id === who.user.id) - return await message.respond("You can't give potatos to yourself!"); - if (who.user.bot) - return await message.respond("You can't give potatos to bots!"); - const userPotatos = - (await potatoKV.get(message.member?.user.id)) || 0; - const targetPotatos = (await potatoKV.get(who.user.id)) || 0; - - if (!count && count !== 0) count = 1; - - if (count > userPotatos) - return await message.respond( - 'You can only give as many potatos as you have!' - ); - - if (count < 1) - return await message.respond('You need to send at least one potato.'); - - const newUserPotatos = userPotatos - count; - const newTargetPotatos = targetPotatos + count; - - await potatoKV.put(message.member?.user.id, newUserPotatos); - await potatoKV.put(who.user.id, newTargetPotatos); - - await message.respond( - `You gave ${count} potato${ - count === 1 ? '' : 'es' - } to ${who.user.getTag()}, how nice of you.` - ); - } -); - -potato_slash.register( - { - name: 'top', - description: 'View the top potato collector', - options: (opt) => ({ - count: opt.integer({ - name: 'count', - description: 'top x collectors', - required: false - }) - }) - }, - async (message, { count }) => { - count = Math.min(Math.max(3, count || 10), 20); - const items = await potatoKV.items(); - const filtered = items.filter( - (entry) => - !isNaN((entry.key as unknown) as number) && - ((entry.value as unknown) as number) > 0 - ); - const sorted = filtered.sort( - (a, b) => (b.value as number) - (a.value as number) - ); - const top = sorted.slice(0, count); - count = top.length; - const userMap = await Promise.all( - top.map((entry) => - discord - .getUser(entry.key) - .then((user) => ({ user, potatos: entry.value as number })) - ) - ); - - let description = `${discord.decor.Emojis.POTATO} **${filtered - .reduce((a, b) => a + (b.value as number), 0) - .toLocaleString()}**\n`; - description += `${discord.decor.Emojis.MAN_FARMER} **${filtered.length}**\n\n`; - description += `${discord.decor.Emojis.CHART_WITH_UPWARDS_TREND} **Ranks** ${discord.decor.Emojis.MUSCLE}\n`; - - for (const entry of userMap.slice(0, Math.max(3, count - 1))) { - const { user, potatos } = entry; - const place = userMap.indexOf(entry); - description += `\` ${MEDALS[place] || - `${(place + 1).toString().padStart(2, ' ')} `} \` **${ - user?.username - }**#${user?.discriminator} - ${potatos.toLocaleString()} potatos\n`; - } - - const ownIndex = sorted.findIndex( - (item) => item.key === message.member.user.id - ); - - if (ownIndex >= count) { - description += `\` ... \` *${ownIndex - count + 1}* other farmers\n`; - description += `\` ${(ownIndex + 1).toString().padStart(2, ' ')} \` **${ - message.member.user.username - }**#${message.member.user.discriminator} - ${ - sorted[ownIndex].value - } potato${sorted[ownIndex].value === 1 ? '' : 'es'}`; - } else if (count > 3) { - const { user, potatos } = userMap[count - 1]; - description += `\` ${count.toString().padStart(2, ' ')} \` **${ - user?.username - }**#${user?.discriminator} - ${potatos.toLocaleString()} potatos\n`; - } - - await message.respond(`${discord.decor.Emojis.TROPHY} Leaderboard​ ${discord.decor.Emojis.CROWN} - -${description}`); - } -); - -potato_slash.register( - { name: 'drop', description: 'drop a potato in the chat' }, - async (message) => { - const userPotatos = - (await potatoKV.get(message.member?.user.id)) || 0; - - if (!userPotatos) - return await message.respond("you don't have any potatos!"); - - const lastPotato = await potatoKV.get('lastPotato'); - if (lastPotato) - return await message.respond( - `there is already an active potato waiting to be picked up in <#${ - lastPotato.split('-')[0] - }>!` - ); - - await potatoKV.put(message.member?.user.id, userPotatos - 1); - - const reply = await message.respond(discord.decor.Emojis.POTATO); - - const cooldown = randomTimeBetween(3 * 60 * 1000, 20 * 60 * 1000); - - await potatoKV.put('cooldown', true, { ttl: cooldown }); - await potatoKV.put( - 'lastPotato', - `${message.channelId}-No id because slash command...`, - { - ttl: cooldown - } - ); - } -); - -potato_slash.register( - { - name: 'modify', - description: 'Modify the potatoes of a fellow collector', - showSourceMessage: false, - options: (opt) => ({ - who: opt.guildMember({ - name: 'who', - description: "who's potatoes to modify", - required: true - }), - count: opt.string({ - name: 'amount', - description: ' How much you want to give/take' - }) - }) - }, - async (message, { who, count }) => { - if (!message.member.can(discord.Permissions.ADMINISTRATOR)) - return await message.respondEphemeral('missing permissions'); - if (who.user.bot) - return await message.respondEphemeral( - 'thats a.. bot. you wanna modify a bots potatos??' - ); - const oldCount = (await potatoKV.get(who.user.id)) || 0; - - let newCount = oldCount; - if (count.startsWith('+')) newCount += parseInt(count.replace('+', '')); - else if (count.startsWith('-')) - newCount -= parseInt(count.replace('-', '')); - else newCount = parseInt(count); - - if (isNaN(newCount as number)) - return await message.respond('invalid count'); - - await potatoKV.put(who.user.id, newCount as number); - await message.respondEphemeral( - `Ok, updated ${who.user.getTag()}'s potatoes to ${newCount}` - ); - } -); - -if (ALLOW_DAILY) - potato_slash.register( - { - name: 'daily', - description: 'Get your daily potato!' - }, - async (message) => { - if (await potatoKV.get(`daily-${message.member.user.id}`)) - return await message.respond('you already claimed your daily potato!'); - - await potatoKV.put(`daily-${message.member.user.id}`, true, { - ttl: - Math.ceil(Date.now() / 1000 / 60 / 60 / 24) * 24 * 60 * 60 * 1000 - - Date.now() - }); - const newCount = await potatoKV.transact( - message.member.user.id, - (prev: number | undefined) => (prev || 0) + 1 - ); - await message.respond( - `you claimed your daily potato, and now hold onto ${newCount} potatos.` - ); - } - ); - -const shop = potato_slash.registerGroup({ - name: 'shop', - description: 'Buy stuff with potatoes in the potato shop' -}); - -shop.register( - { name: 'list', description: 'list all potato shop items' }, - async (message) => { - if (!Object.keys(SHOP_ITEMS).length) - return await message.respond('no items currently available, sorry!'); - - const fields = await Promise.all( - Object.entries(SHOP_ITEMS) - .filter(([, item]) => item.enabled) - .map(async ([name, item]) => ({ - name: `${name} - ${item.price} ${discord.decor.Emojis.POTATO}`, - value: item.description, - inline: true - })) - ); - - let embed = new discord.Embed({ - title: 'Potato Shop', - description: - '**Available Items**\nuse `/potato shop buy ` to purchase an item listed here', - fields: fields - }); - - await message.respond({ embeds: [embed] }); - } -); - -shop.register( - { - name: 'buy', - description: 'purchase a potato shop item', - options: (opt) => ({ - item: opt.string({ - name: 'item', - description: 'What item to buy', - required: true - }) - }) - }, - async (message, { item }) => { - const itemObj = SHOP_ITEMS[item]; - if (!itemObj || !itemObj.enabled) - return await message.respond( - `invalid potato item. use \`/potato shop list\` to get a list of all available items` - ); - - const purchases = ((await potatoKV.get('shop')) || []) as { - user: string; - item: string; - expiresAt: number | undefined; - }[]; - const purchase = purchases.find( - (purchase) => - purchase.user === message.member.user.id && purchase.item === item - ); - if (purchase) - return message.respond( - `You already bought this item!${ - purchase.expiresAt - ? ` You can buy it again on ${new Date( - purchase.expiresAt - ).toUTCString()}` - : '' - }` - ); - - const userPotatos = - (await potatoKV.get(message.member.user.id)) || 0; - if (userPotatos < itemObj.price) - return await message.respond( - "you don't have enough potatos for that item!" - ); - - try { - await itemObj.onPurchase(message.member.user); - } catch (err) { - return await message.respond( - `There was an error processing your order: ${err.message}` - ); - } - - await potatoKV.transact( - message.member.user.id, - (prev: number | undefined) => (prev || 0) - itemObj.price - ); - - await potatoKV.transact( - 'shop', - (prev: pylon.JsonArray | undefined) => - [ - ...(prev || []), - { - user: message.member.user.id, - item, - expiresAt: itemObj.duration - ? Date.now() + itemObj.duration - : undefined - } - ] as pylon.JsonArray - ); - - await message.respond(`You successfully bought \`${item}\`!`); - } -); - -pylon.tasks.cron('shop', '0 0/5 * * * * *', async () => { - const purchases = ((await potatoKV.get('shop')) || []) as { - user: string; - item: string; - expiresAt: number | undefined; - }[]; - - const newPurchases = []; - - for (const purchase of purchases) { - if (purchase.expiresAt && purchase.expiresAt <= Date.now()) { - const item = SHOP_ITEMS[purchase.item]; - if (!item) continue; - - discord - .getUser(purchase.user) - .then((user) => item.onExpire(user)) - .catch((err) => console.error(err)); - } else newPurchases.push(purchase); - } - - await potatoKV.put('shop', newPurchases as pylon.JsonArray); -}); From 3fef49ba1090a53658aa8dd4a1ad3b2cd166179b Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Wed, 24 Feb 2021 15:37:52 +0100 Subject: [PATCH 19/23] Update README.md respectively --- README.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2405a76..569d74d 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,15 @@ If you intend to use the potato lottery system. you also have to define a lotter global.POTATO_LOTTERY_CHANNEL = '693621234365366302'; ``` -# Addons - -If you use the code in `autodelete.ts` Pylon will autodelete potatoes of a user when they leave your server to save the limited amount of kv keys (256). Again put this in a seperate file and import it if you want to use. It also has the option to log when someone leaves, to remove that feature remove the lines that have been marked as "Only necessary if you want to send a message when a user leaves" - -# Port to slash commands -The potato economy has also been ported to slash commands, a new discord feature. If you'd rather use this version do what you would do with `potato.ts` but with `potato_slash.ts` - -**Changes** - -Besides all commands being slash commands there are a few differences - +You can also choose to use the slash command version by setting +```ts +const SLASH_COMMANDS = true +``` +Changes if you set it to true: 1) Potato lottery has been disabled as it was just causing trouble and Pylon can only have up to 10 slash commands so it had to go 2) `/potato drop` now doesn't delete the potato as Pylon can't delete a slash command response 3) `/potato` doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user -**Make sure to only use one of the potato systems** +# Addons + +If you use the code in `autodelete.ts` Pylon will autodelete potatoes of a user when they leave your server to save the limited amount of kv keys (256). Again put this in a seperate file and import it if you want to use. It also has the option to log when someone leaves, to remove that feature remove the lines that have been marked as "Only necessary if you want to send a message when a user leaves" From 53fd62d3d6d09a4c84c3e39c197a5f662dbbd47b Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Wed, 24 Feb 2021 15:39:31 +0100 Subject: [PATCH 20/23] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 569d74d..6f3cfa0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,9 @@ You can also choose to use the slash command version by setting ```ts const SLASH_COMMANDS = true ``` -Changes if you set it to true: +Slash commands are a discord feature which will make it easier for users to use the commands as they will get the commands displayed by just typing /potato + +Other changes if you set it to true: 1) Potato lottery has been disabled as it was just causing trouble and Pylon can only have up to 10 slash commands so it had to go 2) `/potato drop` now doesn't delete the potato as Pylon can't delete a slash command response 3) `/potato` doesnt exist, to see your own potatoes uses `/potato inspect` withput providing a user From c858833e6b883a86ff27904f233dddb2397bef1f Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Wed, 24 Feb 2021 15:39:50 +0100 Subject: [PATCH 21/23] Codeblock --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f3cfa0..0017550 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ You can also choose to use the slash command version by setting ```ts const SLASH_COMMANDS = true ``` -Slash commands are a discord feature which will make it easier for users to use the commands as they will get the commands displayed by just typing /potato +Slash commands are a discord feature which will make it easier for users to use the commands as they will get the commands displayed by just typing `/potato` Other changes if you set it to true: 1) Potato lottery has been disabled as it was just causing trouble and Pylon can only have up to 10 slash commands so it had to go From 83aee2cf9322c76b048e97cf70570dde8c639cce Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Wed, 24 Feb 2021 15:40:29 +0100 Subject: [PATCH 22/23] slash_commands => SLASH_COMMANDS --- potato.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/potato.ts b/potato.ts index c510f90..432477d 100644 --- a/potato.ts +++ b/potato.ts @@ -2,7 +2,7 @@ const potatoCommands = new discord.command.CommandGroup({ defaultPrefix: '!' }); -const slash_commands: boolean = true; //Define here to either use slash commands (use true) or normal commands (use false) +const SLASH_COMMANDS: boolean = true; //Define here to either use slash commands (use true) or normal commands (use false) const POTATO_LOTTERY_TIME_MINUTES = 5; //Define here how long potato lottery should take const ALLOW_DAILY = true; //Define here if people should be able to claim daily potatoes or not const SHOP_ITEMS = { From 106f8d20993dcc0109aeb39734aa8422464569a4 Mon Sep 17 00:00:00 2001 From: Kile <69253692+Kile@users.noreply.github.com> Date: Fri, 26 Feb 2021 09:52:12 +0100 Subject: [PATCH 23/23] Update potato.ts --- potato.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/potato.ts b/potato.ts index 432477d..56bed5f 100644 --- a/potato.ts +++ b/potato.ts @@ -52,8 +52,8 @@ const randomTimeBetween = (min: number, max: number) => Math.round(Math.random() * (max - min) + min); discord.on(discord.Event.MESSAGE_CREATE, async (message: discord.Message) => { - if (!['710873588646936576', '739525325267927082'].includes(message.channelId)) - return; + //if (!['channel_id_1', 'channel_id_2'].includes(message.channelId)) <- to have potatoe spawn in only specific channels, uncomment this and input the channel ids + //return; if (!message.author || message.author.bot) return; if (await potatoKV.get('cooldown')) {