mirror of
https://github.com/keanuplayz/TravBot-v3.git
synced 2024-08-15 02:33:12 +00:00
Merge pull request #30 from Hades785/eco-bet
This commit is contained in:
commit
667389340d
4 changed files with 208 additions and 2 deletions
|
@ -3,6 +3,7 @@ import {isAuthorized, getMoneyEmbed} from "./subcommands/eco-utils";
|
|||
import {DailyCommand, PayCommand, GuildCommand, LeaderboardCommand} from "./subcommands/eco-core";
|
||||
import {BuyCommand, ShopCommand} from "./subcommands/eco-shop";
|
||||
import {MondayCommand} from "./subcommands/eco-extras";
|
||||
import {BetCommand} from "./subcommands/eco-bet";
|
||||
|
||||
export default new Command({
|
||||
description: "Economy command for Monika.",
|
||||
|
@ -16,7 +17,8 @@ export default new Command({
|
|||
leaderboard: LeaderboardCommand,
|
||||
buy: BuyCommand,
|
||||
shop: ShopCommand,
|
||||
monday: MondayCommand
|
||||
monday: MondayCommand,
|
||||
bet: BetCommand
|
||||
},
|
||||
user: new Command({
|
||||
description: "See how much money someone else has by using their user ID or pinging them.",
|
||||
|
|
190
src/commands/fun/subcommands/eco-bet.ts
Normal file
190
src/commands/fun/subcommands/eco-bet.ts
Normal file
|
@ -0,0 +1,190 @@
|
|||
import Command from "../../../core/command";
|
||||
import $ from "../../../core/lib";
|
||||
import {Storage} from "../../../core/structures";
|
||||
import {isAuthorized, getMoneyEmbed, getSendEmbed, ECO_EMBED_COLOR} from "./eco-utils";
|
||||
import {User} from "discord.js";
|
||||
|
||||
export const BetCommand = new Command({
|
||||
description: "Bet your Mons with other people.",
|
||||
usage: "<user> <amount> <duration>",
|
||||
run: "Who are you betting with?",
|
||||
user: new Command({
|
||||
description: "User to bet with.",
|
||||
// handles missing amount argument
|
||||
async run({args, author, channel, guild}): Promise<any> {
|
||||
if (isAuthorized(guild, channel)) {
|
||||
const target = args[0];
|
||||
|
||||
// handle invalid target
|
||||
if (target.id == author.id)
|
||||
return channel.send("You can't bet Mons with yourself!");
|
||||
else if (target.bot && process.argv[2] !== "dev")
|
||||
return channel.send("You can't bet Mons with a bot!");
|
||||
|
||||
return channel.send("How much are you betting?");
|
||||
}
|
||||
},
|
||||
number: new Command({
|
||||
description: "Amount of Mons to bet.",
|
||||
// handles missing duration argument
|
||||
async run({args, author, channel, guild}): Promise<any> {
|
||||
if (isAuthorized(guild, channel)) {
|
||||
const sender = Storage.getUser(author.id);
|
||||
const target = args[0] as User;
|
||||
const receiver = Storage.getUser(target.id);
|
||||
const amount = Math.floor(args[1]);
|
||||
|
||||
// handle invalid target
|
||||
if (target.id == author.id)
|
||||
return channel.send("You can't bet Mons with yourself!");
|
||||
else if (target.bot && process.argv[2] !== "dev")
|
||||
return channel.send("You can't bet Mons with a bot!");
|
||||
|
||||
// handle invalid amount
|
||||
if (amount <= 0)
|
||||
return channel.send("You must bet at least one Mon!");
|
||||
else if (sender.money < amount)
|
||||
return channel.send("You don't have enough Mons for that.", getMoneyEmbed(author));
|
||||
else if (receiver.money < amount)
|
||||
return channel.send("They don't have enough Mons for that.", getMoneyEmbed(target));
|
||||
|
||||
return channel.send("How long until the bet ends?");
|
||||
}
|
||||
},
|
||||
any: new Command({
|
||||
description: "Duration of the bet.",
|
||||
async run({client, args, author, message, channel, guild, askYesOrNo}): Promise<any> {
|
||||
if (isAuthorized(guild, channel)) {
|
||||
// [Pertinence to make configurable on the fly.]
|
||||
// Lower and upper bounds for bet
|
||||
const durationBounds = { min:"1m", max:"1d" };
|
||||
|
||||
const sender = Storage.getUser(author.id);
|
||||
const target = args[0] as User;
|
||||
const receiver = Storage.getUser(target.id);
|
||||
const amount = Math.floor(args[1]);
|
||||
const duration = parseDuration(args[2].trim());
|
||||
|
||||
// handle invalid target
|
||||
if (target.id == author.id)
|
||||
return channel.send("You can't bet Mons with yourself!");
|
||||
else if (target.bot && process.argv[2] !== "dev")
|
||||
return channel.send("You can't bet Mons with a bot!");
|
||||
|
||||
// handle invalid amount
|
||||
if (amount <= 0)
|
||||
return channel.send("You must bet at least one Mon!");
|
||||
else if (sender.money < amount)
|
||||
return channel.send("You don't have enough Mons for that.", getMoneyEmbed(author));
|
||||
else if (receiver.money < amount)
|
||||
return channel.send("They don't have enough Mons for that.", getMoneyEmbed(target));
|
||||
|
||||
// handle invalid duration
|
||||
if (duration <= 0)
|
||||
return channel.send("Invalid bet duration");
|
||||
else if (duration <= parseDuration(durationBounds.min))
|
||||
return channel.send(`Bet duration is too short, maximum duration is ${durationBounds.min}`);
|
||||
else if (duration >= parseDuration(durationBounds.max))
|
||||
return channel.send(`Bet duration is too long, maximum duration is ${durationBounds.max}`);
|
||||
|
||||
// Ask target whether or not they want to take the bet.
|
||||
const takeBet = await askYesOrNo(
|
||||
await channel.send(`<@${target.id}>, do you want to take this bet of ${$(amount).pluralise("Mon", "s")}`),
|
||||
target.id
|
||||
);
|
||||
|
||||
if (takeBet) {
|
||||
// [MEDIUM PRIORITY: bet persistence to prevent losses in case of shutdown.]
|
||||
// Remove amount money from both parts at the start to avoid duplication of money.
|
||||
sender.money -= amount;
|
||||
receiver.money -= amount;
|
||||
// Very hacky solution for persistence but better than no solution, backup returns runs during the bot's setup code.
|
||||
sender.ecoBetInsurance += amount;
|
||||
receiver.ecoBetInsurance += amount;
|
||||
Storage.save();
|
||||
|
||||
// Notify both users.
|
||||
await channel.send(`<@${target.id}> has taken <@${author.id}>'s bet, the bet amount of ${$(amount).pluralise("Mon", "s")} has been deducted from each of them.`);
|
||||
|
||||
// Wait for the duration of the bet.
|
||||
client.setTimeout(async () => {
|
||||
// In debug mode, saving the storage will break the references, so you have to redeclare sender and receiver for it to actually save.
|
||||
const sender = Storage.getUser(author.id);
|
||||
const receiver = Storage.getUser(target.id);
|
||||
// [TODO: when D.JSv13 comes out, inline reply to clean up.]
|
||||
// When bet is over, give a vote to ask people their thoughts.
|
||||
const voteMsg = await channel.send(`VOTE: do you think that <@${target.id}> has won the bet?\nhttps://discord.com/channels/${guild!.id}/${channel.id}/${message.id}`);
|
||||
await voteMsg.react("✅");
|
||||
await voteMsg.react("❌");
|
||||
|
||||
// Filter reactions to only collect the pertinent ones.
|
||||
voteMsg.awaitReactions(
|
||||
(reaction, user) => {
|
||||
return ["✅", "❌"].includes(reaction.emoji.name);
|
||||
},
|
||||
// [Pertinence to make configurable on the fly.]
|
||||
{ time: parseDuration("2m") }
|
||||
).then(reactions => {
|
||||
// Count votes
|
||||
const okReaction = reactions.get("✅");
|
||||
const noReaction = reactions.get("❌");
|
||||
const ok = okReaction ? (okReaction.count ?? 1) - 1 : 0;
|
||||
const no = noReaction ? (noReaction.count ?? 1) - 1 : 0;
|
||||
|
||||
if (ok > no) {
|
||||
receiver.money += amount * 2;
|
||||
channel.send(`By the people's votes, <@${target.id}> has won the bet that <@${author.id}> had sent them.`);
|
||||
}
|
||||
else if (ok < no) {
|
||||
sender.money += amount * 2;
|
||||
channel.send(`By the people's votes, <@${target.id}> has lost the bet that <@${author.id}> had sent them.`);
|
||||
}
|
||||
else {
|
||||
sender.money += amount;
|
||||
receiver.money += amount;
|
||||
channel.send(`By the people's votes, <@${target.id}> couldn't be determined to have won or lost the bet that <@${author.id}> had sent them.`);
|
||||
}
|
||||
sender.ecoBetInsurance -= amount;
|
||||
receiver.ecoBetInsurance -= amount;
|
||||
Storage.save();
|
||||
});
|
||||
}, duration);
|
||||
}
|
||||
else
|
||||
await channel.send(`<@${target.id}> has rejected your bet, <@${author.id}>`);
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Parses a duration string into milliseconds
|
||||
* Examples:
|
||||
* - 3d -> 3 days -> 259200000ms
|
||||
* - 2h -> 2 hours -> 7200000ms
|
||||
* - 7m -> 7 minutes -> 420000ms
|
||||
* - 3s -> 3 seconds -> 3000ms
|
||||
*/
|
||||
function parseDuration(duration: string): number {
|
||||
// extract last char as unit
|
||||
const unit = duration[duration.length - 1].toLowerCase();
|
||||
// get the rest as value
|
||||
let value: number = +duration.substring(0, duration.length - 1);
|
||||
|
||||
if (!["d","h","m","s"].includes(unit) || isNaN(value))
|
||||
return 0;
|
||||
|
||||
if (unit === "d")
|
||||
value *= 86400000; // 1000ms * 60s * 60m * 24h
|
||||
else if (unit === "h")
|
||||
value *= 3600000; // 1000ms * 60s * 60m
|
||||
else if (unit === "m")
|
||||
value *= 60000; // 1000ms * 60s
|
||||
else if (unit === "s")
|
||||
value *= 1000; // 1000ms
|
||||
|
||||
return value;
|
||||
}
|
|
@ -31,6 +31,7 @@ class User {
|
|||
public timezone: number | null; // This is for the standard timezone only, not the daylight savings timezone
|
||||
public daylightSavingsRegion: "na" | "eu" | "sh" | null;
|
||||
public todoList: {[timestamp: string]: string};
|
||||
public ecoBetInsurance: number;
|
||||
|
||||
constructor(data?: GenericJSON) {
|
||||
this.money = select(data?.money, 0, Number);
|
||||
|
@ -50,6 +51,7 @@ class User {
|
|||
}
|
||||
}
|
||||
}
|
||||
this.ecoBetInsurance = select(data?.ecoBetInsurance, 0, Number);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Event from "../core/event";
|
||||
import {client} from "../index";
|
||||
import $ from "../core/lib";
|
||||
import {Config} from "../core/structures";
|
||||
import {Config, Storage} from "../core/structures";
|
||||
import {updateGlobalEmoteRegistry} from "../core/lib";
|
||||
|
||||
export default new Event<"ready">({
|
||||
|
@ -16,5 +16,17 @@ export default new Event<"ready">({
|
|||
});
|
||||
}
|
||||
updateGlobalEmoteRegistry();
|
||||
|
||||
// Run this setup block once to restore eco bet money in case the bot went down. (And I guess search the client for those users to let them know too.)
|
||||
for (const id in Storage.users) {
|
||||
const user = Storage.users[id];
|
||||
|
||||
if(user.ecoBetInsurance > 0) {
|
||||
client.users.cache.get(id)?.send(`Because my system either crashed or restarted while you had a pending bet, the total amount of money that you bet, which was \`${user.ecoBetInsurance}\`, has been restored.`);
|
||||
user.money += user.ecoBetInsurance;
|
||||
user.ecoBetInsurance = 0;
|
||||
}
|
||||
}
|
||||
Storage.save();
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue