WIP: Check if player has role or recruits

This commit is contained in:
Joseph Ferano 2022-06-10 22:05:26 +07:00
parent 929195393a
commit c4f542d885
11 changed files with 148 additions and 71 deletions

View File

@ -28,7 +28,7 @@
<Compile Include="Whitelist.fs" />
<Compile Include="Admin.fs" />
<Compile Include="Bot.fs" />
<None Include="Scripts\Whitelist.fsx" />
<None Include="Scripts\GetWhitelisted.fsx" />
<None Include="Scripts\Airdrop.fsx" />
</ItemGroup>
<Import Project="..\.paket\Paket.Restore.targets" />

View File

@ -116,6 +116,8 @@ let getStoreItems (storeId : string) =
Stock = reader.int "stock"
LimitStock = reader.bool "limit_stock"
Available = reader.bool "available"
RequiresInvites = None
RequiresRole = None
StoreItem.Item = readItem reader
})
|> Async.AwaitTask
@ -136,6 +138,8 @@ let getStoreItemBySymbol (itemSymbol : string) =
Stock = reader.int "stock"
LimitStock = reader.bool "limit_stock"
Available = reader.bool "available"
RequiresInvites = None
RequiresRole = None
StoreItem.Item = readItem reader
})
|> Async.AwaitTask

View File

@ -141,6 +141,8 @@ type StoreItem = {
Stock : int
LimitStock : bool
Available : bool
RequiresRole : uint64 option
RequiresInvites : int option
Item : Item
}

View File

@ -9,22 +9,32 @@ open DSharpPlus.SlashCommands
open Degenz
open Degenz.Messaging
open Degenz.PlayerInteractions
open FsToolkit.ErrorHandling
let checkHasStock (item : StoreItem) player =
let embedWithError error =
DiscordFollowupMessageBuilder()
.AsEphemeral()
.WithContent(error)
|> Error
let checkHasStock (item : StoreItem) =
if item.Stock > 0 || item.LimitStock = false
then Ok player
else Error $"{item.Item.Name} is out of stock! Check back later to purchase"
then Ok ()
else $"{item.Item.Name} is out of stock! Check back later to purchase"
|> embedWithError
let checkHasSufficientFunds (item : Item) player =
match item.Attributes with
| CanBuy price ->
if player.Bank - price >= 0<GBT>
then Ok player
else Error $"""
then Ok ()
else $"""
**__Current Balance:__** {player.Bank} 💰 $GBT
Hold up Degen! You don't have enough $GBT!
Go to <#{GuildEnvironment.channelQuests}> to start earning some now..."""
| _ -> Error $"{item.Name} item cannot be bought"
|> embedWithError
| _ -> $"{item.Name} item cannot be bought"
|> embedWithError
let getTotalOwnedOfItem (item : Item) (inventory : Item list) =
inventory
@ -37,20 +47,23 @@ let checkDoesntExceedStackCap (item : Item) player =
match item.Attributes , itemCount with
| CanStack max , Some count ->
if count >= max
then Error $"You own the maximum allowed amount {item.Name}!"
else Ok player
| _ , Some _ -> Error $"You already own this item"
| _ -> Ok player
then $"You own the maximum allowed amount {item.Name}!"
|> embedWithError
else Ok ()
| _ , Some _ -> "You already own this item" |> embedWithError
| _ -> Ok ()
let checkSoldItemAlready (item : Item) player =
if player.Inventory |> List.exists (fun i -> item.Id = i.Id)
then Ok player
else Error $"{item.Name} not found in your inventory! Looks like you sold it already."
then Ok ()
else $"{item.Name} not found in your inventory! Looks like you sold it already."
|> embedWithError
let checkHasItemsInArsenal itemType items player =
if List.isEmpty items |> not
then Ok player
else Error $"You currently have no {itemType} in your arsenal to sell!"
then Ok ()
else $"You currently have no {itemType} in your arsenal to sell!"
|> embedWithError
let getItemEmbeds owned (items : StoreItem list) =
items
@ -198,7 +211,7 @@ let showJpegsEmbed (ctx : IDiscordContext) = PlayerInteractions.executePlayerAct
let jpegs =
player.Inventory
|> Inventory.getItemsByType ItemType.Jpeg
|> List.map (fun i -> { StoreId = "BACKALLEY" ; Item = i ; Stock = 1 ; LimitStock = false ; Available = true })
|> List.map (fun i -> { StoreId = "" ; Item = i ; Stock = 1 ; LimitStock = false ; Available = true ; RequiresInvites = None ; RequiresRole = None })
match jpegs with
| [] -> do! Messaging.sendFollowUpMessage ctx $"You currently do not own any jpegs or raffle tickets. Go to <#{GuildEnvironment.channelBackAlley}> to buy some"
| jpegs ->
@ -224,56 +237,104 @@ let buy storeId (filterBy : ItemType option) (ctx : IDiscordContext) =
with ex -> printfn $"{ex.Message}"
})
let buyForPlayer storeId player (filterBy : ItemType option) (ctx : IDiscordContext) = async {
try
let! items = DbService.getStoreItems storeId
if items.Length > 0 then
let items' =
match filterBy with
| Some itemType -> items |> List.filter (fun item -> item.Item.Type = itemType)
| None -> items
let itemStore = getBuyItemsEmbed storeId player items'
do! ctx.FollowUp itemStore |> Async.AwaitTask
do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeId
else
do! Messaging.sendFollowUpMessage ctx "There are currently no items available, check back later"
with ex -> printfn $"{ex.Message}"
}
let sell itemType getItems (ctx : IDiscordContext) =
executePlayerAction ctx (fun player -> async {
let items = getItems player.Inventory
match checkHasItemsInArsenal itemType items player with
| Ok _ -> let itemStore = getSellEmbed items
do! ctx.FollowUp(itemStore) |> Async.AwaitTask
| Error e -> do! sendFollowUpMessage ctx e
| Error e -> do! ctx.FollowUp e |> Async.AwaitTask
do! Analytics.buyItemCommand (ctx.GetDiscordMember()) itemType
})
let checkHasRequiredInvites storeItem player =
async {
match storeItem.RequiresInvites with
| Some amount ->
let! totalInvites = InviteTracker.getInvitedUserCount player.DiscordId
if amount <= totalInvites then
return Ok ()
else
return "" |> embedWithError
| None -> return Ok ()
}
let checkHasRequiredRole storeItem (user : DiscordMember) =
match storeItem.RequiresRole with
| Some roleId ->
if user.Roles |> Seq.exists (fun r -> r.Id = roleId) then
Ok ()
else
let builder = DiscordFollowupMessageBuilder().AsEphemeral()
let embed = DiscordEmbedBuilder()
embed.Description <- $"""
"""
builder.AddEmbed(embed)
"" |> embedWithError
| None -> Ok ()
// TODO: When you buy a shield, prompt the user to activate it
let handleBuyItem (dispatch : IDiscordContext -> Task) (ctx : IDiscordContext) itemId =
executePlayerAction ctx (fun player -> async {
let storeId = ctx.GetInteractionId().Split("-").[2]
let! storeInventory = DbService.getStoreItems storeId
let storeItem = storeInventory |> List.find (fun si -> si.Item.Id = itemId)
let item = storeInventory |> List.map (fun i -> i.Item) |> Inventory.findItemById itemId
do! player
|> checkHasSufficientFunds item
>>= checkHasStock storeItem
>>= checkDoesntExceedStackCap item
|> handleResultWithResponse ctx (fun player -> async {
let price = match item.Attributes with CanBuy price -> price | _ -> 0<GBT>
let! result = asyncResult {
let! storeInventory = DbService.getStoreItems storeId
let storeItem = storeInventory |> List.find (fun si -> si.Item.Id = itemId)
do! checkHasSufficientFunds storeItem.Item player
do! checkHasStock storeItem
do! checkDoesntExceedStackCap storeItem.Item player
do! checkHasRequiredRole storeItem (ctx.GetDiscordMember())
do! checkHasRequiredInvites storeItem player
return storeItem
}
return!
result
|> handleResultWithEmbed ctx (fun storeItem -> async {
let price = match storeItem.Item.Attributes with CanBuy price -> price | _ -> 0<GBT>
try
do! dispatch ctx |> Async.AwaitTask
do! DbService.updatePlayerCurrency -price player.DiscordId |> Async.Ignore
do! DbService.addToPlayerInventory player.DiscordId item |> Async.Ignore
do! DbService.addToPlayerInventory player.DiscordId storeItem.Item |> Async.Ignore
if storeItem.LimitStock = true && storeItem.Stock > 0 then
do! DbService.decrementItemStock item |> Async.Ignore
do! DbService.decrementItemStock storeItem.Item |> Async.Ignore
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
let embed = purchaseItemEmbed 1 item
match item.Attributes , getTotalOwnedOfItem item player.Inventory |> Option.defaultValue 0 with
let embed = purchaseItemEmbed 1 storeItem.Item
match storeItem.Item.Attributes , getTotalOwnedOfItem storeItem.Item player.Inventory |> Option.defaultValue 0 with
| CanStack max , amount ->
embed.AddField("Owned", $"{amount + 1}", true) |> ignore
embed.AddField("New $GBT Balance", $"`💰` {player.Bank - price} `(-{price} $GBT)`", true) |> ignore
if amount + 1 < max then
let btn = DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Buy-{item.Id}-{storeId}", $"Buy Another")
let btn = DiscordButtonComponent(WeaponClass.getClassButtonColor storeItem.Item, $"Buy-{storeItem.Item.Id}-{storeId}", $"Buy Another")
builder.AddComponents(btn) |> ignore
| _ -> ()
builder.AddEmbed(embed) |> ignore
do! ctx.FollowUp builder |> Async.AwaitTask
let builder = DiscordMessageBuilder()
builder.WithContent($"{player.Name} just purchased {item.Name}!") |> ignore
builder.WithContent($"{player.Name} just purchased {storeItem.Item.Name}!") |> ignore
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelEventsHackerBattle)
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
do! Analytics.buyItemButton (ctx.GetDiscordMember()) item.Id price
do! Analytics.buyItemButton (ctx.GetDiscordMember()) storeItem.Item.Id price
with ex ->
printfn $"STORE ERROR: {ex.Message}"
})
@ -285,7 +346,7 @@ let handleSell (ctx : IDiscordContext) itemId =
do!
player
|> checkSoldItemAlready item
|> handleResultWithResponse ctx (fun player -> async {
|> handleResultWithEmbed ctx (fun () -> async {
match item.Attributes with
| CanSell price ->
do!
@ -300,28 +361,6 @@ let handleSell (ctx : IDiscordContext) itemId =
})
})
let showStats (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async {
let embed = DiscordEmbedBuilder()
PlayerStats.stats
|> List.iter (fun statConfig ->
let playerStat = PlayerStats.getPlayerStat statConfig player
let min =
match statConfig.BaseRange.Min = playerStat.ModRange.Min with
| true -> $"{statConfig.BaseRange.Min}"
| false -> $"{statConfig.BaseRange.Min} (+{playerStat.ModRange.Min}) "
let max =
match statConfig.BaseRange.Max = playerStat.ModRange.Max with
| true -> $"{statConfig.BaseRange.Max}"
| false -> $"{statConfig.BaseRange.Max} (+{playerStat.ModRange.Max}) "
let field = $"{min} |---------------| {max}"
embed.AddField(string statConfig.Id , field) |> ignore)
let builder =
DiscordFollowupMessageBuilder()
.AddEmbed(embed)
.AsEphemeral(true)
do! ctx.FollowUp builder |> Async.AwaitTask
})
let handleJpegEvents _ (event : ComponentInteractionCreateEventArgs) =
let ctx = DiscordEventContext event :> IDiscordContext
let id = ctx.GetInteractionId()
@ -364,15 +403,15 @@ let sendBackalleyEmbed (ctx : IDiscordContext) =
let builder = DiscordMessageBuilder()
let embed = DiscordEmbedBuilder()
embed.ImageUrl <- "https://s7.gifyu.com/images/ezgif.com-gif-maker-23203b9dca779ba7cf.gif"
embed.Title <- "Jpeg Store"
embed.Title <- "🎟️ Raffle Store"
embed.Color <- DiscordColor.Black
embed.Description <- "Hey, what do you want kid?"
embed.Description <- $"Hey, what do you want, kid?\nI aint got all day… {Formatter.Emoji}"
builder.AddEmbed embed |> ignore
let button1 = DiscordButtonComponent(ButtonStyle.Success, $"ShowStore-0-BACKALLEY1", $"NFT Raffles") :> DiscordComponent
let button1 = DiscordButtonComponent(ButtonStyle.Danger, $"ShowStore-0-BACKALLEY1", $"NFT Raffles") :> DiscordComponent
let button2 = DiscordButtonComponent(ButtonStyle.Success, $"ShowStore-0-BACKALLEY2", $"Whitelist Raffles") :> DiscordComponent
let button3 = DiscordButtonComponent(ButtonStyle.Success, $"ShowStore-0-BACKALLEY3", $"USDT Raffles") :> DiscordComponent
// let button3 = DiscordButtonComponent(ButtonStyle.Success, $"ShowStore-0-BACKALLEY3", $"USDT Raffles") :> DiscordComponent
let button4 = DiscordButtonComponent(ButtonStyle.Primary, $"ShowJpegInventory-0-0", $"View My Stash") :> DiscordComponent
builder.AddComponents [| button1 ; button2 ; button3 ; button4 |] |> ignore
builder.AddComponents [| button1 ; button2 ; button4 |] |> ignore
do! GuildEnvironment.botClientJpeg.Value.SendMessageAsync(channel, builder)
|> Async.AwaitTask

View File

@ -11,7 +11,7 @@ open Npgsql.FSharp
open Solnet.Wallet
let connStr = GuildEnvironment.connectionString
let InviteRewardAmount = 100<GBT>
let InviteRewardAmount = 300<GBT>
let InviteLinkButtonText = "Get My Invite Link"
type Invite = {
@ -318,16 +318,16 @@ let sendInitialEmbed (ctx : IDiscordContext) =
try
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelRecruitment)
let rewardMsg = $"""
**__Win $2,000:__**
**__Win $3,000:__**
🙋 1 invite = 1 entry everyday*
🎟 $100 daily raffles till mint
**__How To Invite:__**
1️⃣ Click the green button below
2️⃣ Share your unique link with Degenz
2️⃣ Share your unique link with Friends
**__Bonus__**
💰 Earn an extra 100 $GBT for every invite!
💰 Earn an extra 300 $GBT for every invite!
**Every invite increases your chances of winning*
"""
@ -335,7 +335,7 @@ let sendInitialEmbed (ctx : IDiscordContext) =
DiscordEmbedBuilder()
.WithColor(DiscordColor.CornflowerBlue)
.WithDescription(rewardMsg)
.WithImageUrl("https://s8.gifyu.com/images/invite-banner-usdc.png")
.WithImageUrl("https://s8.gifyu.com/images/invite-banner-usdcb670496dc3653cb3.png")
.WithTitle("Invite Degenz")
let builder = DiscordMessageBuilder().AddEmbed(embed)

View File

@ -56,3 +56,8 @@ let handleResultWithResponse ctx fn (player : Result<PlayerData, string>) =
match player with
| Ok p -> fn p
| Error e -> async { do! Messaging.sendFollowUpMessage ctx e }
let handleResultWithEmbed<'a> (ctx : IDiscordContext) fn (player : Result<'a, DiscordFollowupMessageBuilder>) =
match player with
| Ok a -> fn a
| Error e -> async { do! ctx.FollowUp e |> Async.AwaitTask}

View File

@ -24,10 +24,12 @@ let sendInitialEmbed (ctx : IDiscordContext) =
embed.Title <- "Degenz Game Whitelist"
embed.Color <- DiscordColor.White
embed.Description <- $"""
You need to **BUY** Whitelist with 💰 $GBT...
**__Requirements:__**
You need to BUY Whitelist with 💰 $GBT
You must have INVITED at least 1 Degen
**__To Earn $GBT:__**
1️⃣ Recruit Degenz in <#{GuildEnvironment.channelRecruitment}>
1️⃣ Invite Degenz in <#{GuildEnvironment.channelRecruitment}>
2️⃣ Chat to level up in the <#{GuildEnvironment.channelGeneral}>
3️⃣ Complete fun quests inside <#{GuildEnvironment.channelQuests}>
"""
@ -59,7 +61,28 @@ let grantWhitelistRole isOg (ctx : IDiscordContext) =
let handleButtonEvent _ (event : ComponentInteractionCreateEventArgs) =
let ctx = DiscordEventContext event :> IDiscordContext
match event.Id with
| id when id.StartsWith("GimmeWhitelist") -> Store.buy "WHITELIST" None ctx
| id when id.StartsWith("GimmeWhitelist") ->
task {
let builder = DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Content")
do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder) |> Async.AwaitTask
let! invites = InviteTracker.getInvitedUserCount (ctx.GetDiscordMember().Id)
if invites > 0 then
let! playerResult = DbService.tryFindPlayer (ctx.GetDiscordMember().Id)
match playerResult with
| Some player -> do! Store.buyForPlayer "WHITELIST" player None ctx |> Async.StartAsTask
| None -> do! Messaging.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
else
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
builder.Content <- $"""
**Degen**, cant you **READ**?!
⚠️ **__Requirements:__** 1x Invited User
To BUY Whitelist you must have **__INVITED__** 1 Degen.
☑️ Go to <#{GuildEnvironment.channelRecruitment}>
☑️ Invite just 1 Degen!
"""
do! ctx.FollowUp(builder)
} :> Task
| id when id.StartsWith("Buy") ->
task {
let id = ctx.GetInteractionId()

View File

@ -6,3 +6,4 @@ dotenv.net
Npgsql.FSharp
mixpanel-csharp
Solnet.Rpc
FsToolkit.ErrorHandling

View File

@ -8,6 +8,7 @@ nuget FSharp.Core >= 6.0.0
nuget DSharpPlus >= 4.3.0-nightly-01135
nuget DSharpPlus.Interactivity >= 4.3.0-nightly-01135
nuget DSharpPlus.SlashCommands >= 4.3.0-nightly-01135
nuget FsToolkit.ErrorHandling
nuget MongoDB.Driver
nuget dotenv.net 3.1.1

View File

@ -31,6 +31,8 @@ NUGET
System.Runtime.CompilerServices.Unsafe (>= 5.0)
System.ValueTuple (>= 4.5)
FSharp.Core (6.0.3)
FsToolkit.ErrorHandling (2.13)
FSharp.Core (>= 4.7.2)
Microsoft.Bcl.AsyncInterfaces (6.0) - restriction: || (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0)
System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461))
Microsoft.Bcl.HashCode (1.1.1) - restriction: || (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0)