2021-01-25 22:06:12 +00:00
import Command from "../../../core/command" ;
import $ from "../../../core/lib" ;
import { Storage } from "../../../core/structures" ;
import { isAuthorized , getMoneyEmbed , getSendEmbed , ECO_EMBED_COLOR } from "./eco-utils" ;
2021-04-06 03:57:03 +00:00
import { User } from "discord.js" ;
2021-01-25 22:06:12 +00:00
export const BetCommand = new Command ( {
description : "Bet your Mons with other people [TBD]" ,
usage : "<user> <amount> <duration>" ,
run : "Who are you betting with?" ,
user : new Command ( {
2021-01-26 15:28:04 +00:00
// 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?" ) ;
}
} ,
2021-01-25 22:06:12 +00:00
number : new Command ( {
2021-01-26 15:28:04 +00:00
// handles missing duration argument
async run ( { args , author , channel , guild } ) : Promise < any > {
if ( isAuthorized ( guild , channel ) ) {
const sender = Storage . getUser ( author . id ) ;
2021-04-06 03:57:03 +00:00
const target = args [ 0 ] as User ;
const receiver = Storage . getUser ( target . id ) ;
2021-01-26 15:28:04 +00:00
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 ) ) ;
2021-02-23 22:25:28 +00:00
else if ( receiver . money < amount )
return channel . send ( "They don't have enough Mons for that." , getMoneyEmbed ( target ) ) ;
2021-01-26 15:28:04 +00:00
return channel . send ( "How long until the bet ends?" ) ;
}
} ,
2021-01-25 22:06:12 +00:00
any : new Command ( {
2021-04-06 03:57:03 +00:00
async run ( { client , args , author , message , channel , guild , askYesOrNo } ) : Promise < any > {
2021-01-25 22:06:12 +00:00
if ( isAuthorized ( guild , channel ) ) {
2021-02-21 13:11:25 +00:00
// [Pertinence to make configurable on the fly.]
// Lower and upper bounds for bet
const durationBounds = { min : "1m" , max : "1d" } ;
2021-01-25 22:06:12 +00:00
const sender = Storage . getUser ( author . id ) ;
2021-04-06 03:57:03 +00:00
const target = args [ 0 ] as User ;
const receiver = Storage . getUser ( target . id ) ;
2021-01-25 22:06:12 +00:00
const amount = Math . floor ( args [ 1 ] ) ;
const duration = parseDuration ( args [ 2 ] . trim ( ) ) ;
2021-01-26 15:28:04 +00:00
// handle invalid target
if ( target . id == author . id )
return channel . send ( "You can't bet Mons with yourself!" ) ;
2021-01-25 22:06:12 +00:00
else if ( target . bot && process . argv [ 2 ] !== "dev" )
return channel . send ( "You can't bet Mons with a bot!" ) ;
2021-01-26 15:28:04 +00:00
// 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 ) ) ;
2021-02-23 22:25:28 +00:00
else if ( receiver . money < amount )
return channel . send ( "They don't have enough Mons for that." , getMoneyEmbed ( target ) ) ;
2021-01-26 15:28:04 +00:00
// handle invalid duration
2021-01-25 22:06:12 +00:00
if ( duration <= 0 )
2021-02-21 13:11:25 +00:00
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 } ` ) ;
2021-01-25 22:06:12 +00:00
2021-01-26 15:28:04 +00:00
// Ask target whether or not they want to take the bet.
2021-01-26 18:37:31 +00:00
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 ) {
2021-02-21 13:11:25 +00:00
// [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.
2021-01-26 18:37:31 +00:00
sender . money -= amount ;
receiver . money -= amount ;
2021-04-06 03:57:03 +00:00
// Very hacky solution for persistence but better than no solution, backup returns runs during the bot's setup code.
sender . quoteUnquoteSoCalledInsuranceForEcoBetIfItEvenCanBeSoCalled += amount ;
receiver . quoteUnquoteSoCalledInsuranceForEcoBetIfItEvenCanBeSoCalled += amount ;
2021-01-26 18:37:31 +00:00
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. ` ) ;
2021-04-06 03:57:03 +00:00
// Wait for the duration of the bet.
2021-01-26 18:37:31 +00:00
client . setTimeout ( async ( ) = > {
2021-04-06 03:57:03 +00:00
// 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 ) ;
2021-02-21 13:11:25 +00:00
// [TODO: when D.JSv13 comes out, inline reply to clean up.]
2021-01-26 18:37:31 +00:00
// When bet is over, give a vote to ask people their thoughts.
2021-04-06 03:57:03 +00:00
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 } ` ) ;
2021-01-26 18:37:31 +00:00
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 = > {
2021-04-06 03:57:03 +00:00
// 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 ;
2021-01-26 18:37:31 +00:00
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. ` ) ;
}
2021-04-06 03:57:03 +00:00
sender . quoteUnquoteSoCalledInsuranceForEcoBetIfItEvenCanBeSoCalled -= amount ;
receiver . quoteUnquoteSoCalledInsuranceForEcoBetIfItEvenCanBeSoCalled -= amount ;
2021-01-26 17:11:50 +00:00
Storage . save ( ) ;
2021-01-26 18:37:31 +00:00
} ) ;
} , duration ) ;
}
2021-04-06 03:57:03 +00:00
else
2021-01-26 18:37:31 +00:00
await channel . send ( ` <@ ${ target . id } > has rejected your bet, <@ ${ author . id } > ` ) ;
2021-01-25 22:06:12 +00:00
}
}
} )
} )
} )
} ) ;
/ * *
* Parses a duration string into milliseconds
* Examples :
2021-01-26 15:28:04 +00:00
* - 3 d - > 3 days - > 259200000 ms
* - 2 h - > 2 hours - > 7200000 ms
* - 7 m - > 7 minutes - > 420000 ms
2021-01-25 22:06:12 +00:00
* - 3 s - > 3 seconds - > 3000 ms
* /
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 ;
2021-02-21 13:11:25 +00:00
}