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…
	
	Add table
		Add a link
		
	
		Reference in a new issue