diff --git a/Bot/Bot.fsproj b/Bot/Bot.fsproj
index 0a13f65..0669f35 100644
--- a/Bot/Bot.fsproj
+++ b/Bot/Bot.fsproj
@@ -28,7 +28,7 @@
-
+
diff --git a/Bot/DbService.fs b/Bot/DbService.fs
index 872edb7..8997795 100644
--- a/Bot/DbService.fs
+++ b/Bot/DbService.fs
@@ -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
diff --git a/Bot/GameTypes.fs b/Bot/GameTypes.fs
index 45364fc..2da086d 100644
--- a/Bot/GameTypes.fs
+++ b/Bot/GameTypes.fs
@@ -141,6 +141,8 @@ type StoreItem = {
Stock : int
LimitStock : bool
Available : bool
+ RequiresRole : uint64 option
+ RequiresInvites : int option
Item : Item
}
diff --git a/Bot/Games/Store.fs b/Bot/Games/Store.fs
index 68e703a..79bba8b 100644
--- a/Bot/Games/Store.fs
+++ b/Bot/Games/Store.fs
@@ -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
- 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
+ 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
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 ain’t 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
diff --git a/Bot/InviteTracker.fs b/Bot/InviteTracker.fs
index 0e9e26d..394142d 100644
--- a/Bot/InviteTracker.fs
+++ b/Bot/InviteTracker.fs
@@ -11,7 +11,7 @@ open Npgsql.FSharp
open Solnet.Wallet
let connStr = GuildEnvironment.connectionString
-let InviteRewardAmount = 100
+let InviteRewardAmount = 300
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)
diff --git a/Bot/PlayerInteractions.fs b/Bot/PlayerInteractions.fs
index 170c80d..7d5a787 100644
--- a/Bot/PlayerInteractions.fs
+++ b/Bot/PlayerInteractions.fs
@@ -56,3 +56,8 @@ let handleResultWithResponse ctx fn (player : Result) =
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}
diff --git a/Bot/Scripts/Whitelist.fsx b/Bot/Scripts/GetWhitelisted.fsx
similarity index 100%
rename from Bot/Scripts/Whitelist.fsx
rename to Bot/Scripts/GetWhitelisted.fsx
diff --git a/Bot/Whitelist.fs b/Bot/Whitelist.fs
index 4442603..02f0c07 100644
--- a/Bot/Whitelist.fs
+++ b/Bot/Whitelist.fs
@@ -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**, can’t 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()
diff --git a/Bot/paket.references b/Bot/paket.references
index a301393..823b16c 100644
--- a/Bot/paket.references
+++ b/Bot/paket.references
@@ -5,4 +5,5 @@ DSharpPlus.SlashCommands
dotenv.net
Npgsql.FSharp
mixpanel-csharp
-Solnet.Rpc
\ No newline at end of file
+Solnet.Rpc
+FsToolkit.ErrorHandling
\ No newline at end of file
diff --git a/paket.dependencies b/paket.dependencies
index c393de4..65c4be5 100644
--- a/paket.dependencies
+++ b/paket.dependencies
@@ -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
@@ -16,4 +17,4 @@ nuget mixpanel-csharp 5.0.0
nuget Solnet.Extensions
nuget Solnet.KeyStore
nuget Solnet.Programs
-nuget Solnet.Rpc
+nuget Solnet.Rpc
\ No newline at end of file
diff --git a/paket.lock b/paket.lock
index 5687dea..4835e9b 100644
--- a/paket.lock
+++ b/paket.lock
@@ -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)