From fb9c638848172fab1c591439bdf75eaecf186c71 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 25 Jun 2022 01:26:41 +0700 Subject: [PATCH] Add NFT holder check and rank feature for store item --- Bot/Bot.fs | 6 ++--- Bot/DbService.fs | 12 ++++++--- Bot/GameTypes.fs | 1 + Bot/Games/Store.fs | 60 ++++++++++++++++++++++++++++++++++------- Bot/GuildEnvironment.fs | 5 ++++ 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/Bot/Bot.fs b/Bot/Bot.fs index 0cbe48c..7a2f599 100644 --- a/Bot/Bot.fs +++ b/Bot/Bot.fs @@ -100,15 +100,15 @@ let asdf _ (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) = } |> Async.StartAsTask :> Task //hackerBattleBot.add_InteractionCreated(AsyncEventHandler(asdf)) -hackerBattleBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously -GuildEnvironment.botClientHacker <- Some hackerBattleBot - jpegBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously GuildEnvironment.botClientJpeg <- Some jpegBot storeBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously GuildEnvironment.botClientStore <- Some storeBot +hackerBattleBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously +GuildEnvironment.botClientHacker <- Some hackerBattleBot + adminBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously inviterBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously diff --git a/Bot/DbService.fs b/Bot/DbService.fs index 93f85cd..db787fe 100644 --- a/Bot/DbService.fs +++ b/Bot/DbService.fs @@ -105,7 +105,7 @@ let getStoreItems (storeId : string) = |> Sql.connect |> Sql.parameters [ "sid", Sql.string storeId ] |> Sql.query """ - SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end, + SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end,rank, buy_price,sell_price,rate_limit,expiration,drop_chance,can_trade,can_consume,attack_power,defense_power,class_name,max_stack,mods FROM store_item JOIN item i on store_item.item_id = i.id @@ -116,6 +116,7 @@ let getStoreItems (storeId : string) = Stock = reader.int "stock" LimitStock = reader.bool "limit_stock" Available = reader.bool "available" + Rank = reader.int "rank" SaleEnd = reader.int64OrNone "sale_end" TotalSold = None RequiresInvites = reader.intOrNone "require_invites" @@ -128,7 +129,7 @@ let getAllActiveStoreItems () = connStr |> Sql.connect |> Sql.query """ - SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end, + SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end,rank, buy_price,sell_price,rate_limit,expiration,drop_chance,can_trade,can_consume,attack_power,defense_power,class_name,max_stack,mods FROM store_item JOIN item i on store_item.item_id = i.id @@ -139,6 +140,7 @@ let getAllActiveStoreItems () = Stock = reader.int "stock" LimitStock = reader.bool "limit_stock" Available = reader.bool "available" + Rank = reader.int "rank" SaleEnd = reader.int64OrNone "sale_end" TotalSold = None RequiresInvites = reader.intOrNone "require_invites" @@ -153,7 +155,7 @@ let getRafflesWithPurchases storeId = |> Sql.parameters [ "sid" , Sql.string storeId ] |> Sql.query """ WITH raffles AS - (SELECT store_id,stock,available,limit_stock,i.id AS raffle_id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end, + (SELECT store_id,stock,available,limit_stock,i.id AS raffle_id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end,rank, buy_price,sell_price,rate_limit,expiration,drop_chance,can_trade,can_consume,attack_power,defense_power,class_name,max_stack,mods FROM store_item JOIN item i on store_item.item_id = i.id @@ -168,6 +170,7 @@ FULL JOIN (SELECT item_id, count(*) AS total FROM inventory_item Stock = reader.int "stock" LimitStock = reader.bool "limit_stock" Available = reader.bool "available" + Rank = reader.int "rank" SaleEnd = reader.int64OrNone "sale_end" TotalSold = reader.intOrNone "total" RequiresInvites = reader.intOrNone "require_invites" @@ -181,7 +184,7 @@ let getStoreItemBySymbol (itemSymbol : string) = |> Sql.connect |> Sql.parameters [ "iid", Sql.string itemSymbol ] |> Sql.query """ - SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end, + SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end,rank, buy_price,sell_price,rate_limit,expiration,drop_chance,can_trade,can_consume,attack_power,defense_power,class_name,max_stack,mods FROM store_item JOIN item i on store_item.item_id = i.id @@ -192,6 +195,7 @@ let getStoreItemBySymbol (itemSymbol : string) = Stock = reader.int "stock" LimitStock = reader.bool "limit_stock" Available = reader.bool "available" + Rank = reader.int "rank" SaleEnd = reader.int64OrNone "sale_end" TotalSold = None RequiresInvites = reader.intOrNone "require_invites" diff --git a/Bot/GameTypes.fs b/Bot/GameTypes.fs index df478b3..e6f00df 100644 --- a/Bot/GameTypes.fs +++ b/Bot/GameTypes.fs @@ -144,6 +144,7 @@ type StoreItem = { LimitStock : bool Available : bool TotalSold : int option + Rank : int SaleEnd : int64 option RequiresRole : uint64 option RequiresInvites : int option diff --git a/Bot/Games/Store.fs b/Bot/Games/Store.fs index 0e360c6..f4f39f7 100644 --- a/Bot/Games/Store.fs +++ b/Bot/Games/Store.fs @@ -11,6 +11,18 @@ open Degenz.Messaging open Degenz.PlayerInteractions open FsToolkit.ErrorHandling +let holderRoles = [ GuildEnvironment.roleHolder1 ; GuildEnvironment.roleHolder5 ; GuildEnvironment.roleHolder10 ; GuildEnvironment.roleHolder50 ] + +let getRankEmbedColor = function + | 3 -> DiscordColor.Red + | 2 -> DiscordColor.Green + | _ -> DiscordColor.Blurple + +let getRankButtonColor = function + | 3 -> ButtonStyle.Danger + | 2 -> ButtonStyle.Success + | _ -> ButtonStyle.Primary + let embedWithError error = DiscordFollowupMessageBuilder() .AsEphemeral() @@ -140,7 +152,7 @@ let getItemEmbeds owned (items : StoreItem list) = embed.AddField("🚫 Closed", $"", true) |> ignore) item.TotalSold |> Option.iter (fun total -> embed.AddField("Total Sold", string total, true) |> ignore) - embed.Color <- WeaponClass.getClassEmbedColor item.Item + embed.Color <- getRankEmbedColor item.Rank embed.Title <- titleText embed.Description <- item.Item.Description embed.ImageUrl <- "https://stage.degenz.game/blank-row.png" @@ -166,13 +178,13 @@ let getBuyItemsEmbed storeId player (storeInventory : StoreItem list) = match owned , inStock , saleStillOngoing with | _ , false , _ -> let msg = if item.Available then "Out of Stock" else "Unavailable" - DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"{item.Item.Name} ({msg})", true) - | false , true , true -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}") - | _ , _ , false -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Closed {item.Item.Name}", true) + DiscordButtonComponent(getRankButtonColor item.Rank, $"Buy-{item.Item.Id}-{storeId}", $"{item.Item.Name} ({msg})", true) + | false , true , true -> DiscordButtonComponent(getRankButtonColor item.Rank, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}") + | _ , _ , false -> DiscordButtonComponent(getRankButtonColor item.Rank, $"Buy-{item.Item.Id}-{storeId}", $"Closed {item.Item.Name}", true) | _ -> match checkDoesntExceedStackCap item.Item player with - | Ok _ -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}") - | Error _ -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Own {item.Item.Name}", true) + | Ok _ -> DiscordButtonComponent(getRankButtonColor item.Rank, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}") + | Error _ -> DiscordButtonComponent(getRankButtonColor item.Rank, $"Buy-{item.Item.Id}-{storeId}", $"Own {item.Item.Name}", true) :> DiscordComponent) let builder = @@ -255,6 +267,7 @@ let buy (storeId : string) (filterBy : ItemType option) (ctx : IDiscordContext) match filterBy with | Some itemType -> items |> List.filter (fun item -> item.Item.Type = itemType) | None -> items + |> List.sortByDescending (fun i -> i.Rank) let itemStore = getBuyItemsEmbed storeId player items' do! ctx.FollowUp itemStore |> Async.AwaitTask do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeId @@ -271,6 +284,7 @@ let buyForPlayer storeId player (filterBy : ItemType option) (ctx : IDiscordCont match filterBy with | Some itemType -> items |> List.filter (fun item -> item.Item.Type = itemType) | None -> items + |> List.sortByDescending (fun i -> i.Rank) let itemStore = getBuyItemsEmbed storeId player items' do! ctx.FollowUp itemStore |> Async.AwaitTask do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeId @@ -320,7 +334,7 @@ Then try again… let checkHasRequiredRole storeItem (user : DiscordMember) = match storeItem.RequiresRole with | Some roleId -> - if user.Roles |> Seq.exists (fun r -> r.Id = roleId) then + if holderRoles |> Seq.contains roleId || user.Roles |> Seq.exists (fun r -> r.Id = roleId) then Ok () else let embed = DiscordEmbedBuilder() @@ -347,6 +361,32 @@ Then try again… |> Error | None -> Ok () +let checkIsHolder storeItem (user : DiscordMember) = + // TODO: Add a check to see if they have a a minimum role + match storeItem.RequiresRole with + | Some roleId -> + if holderRoles |> Seq.contains roleId then + if user.Roles |> Seq.exists (fun r -> holderRoles |> Seq.contains r.Id) then + Ok () + else + let embed = DiscordEmbedBuilder() + embed.Description <- $""" +āŒ **Degen**, can’t you **READ**?! +āš ļø **__Entry Requirements:__** Have the <@&{roleId}> + +To become a verified holder +1ļøāƒ£ļø If you still don't own a Degenz Game NFT, head over to https://magiceden.io/marketplace/degenz_game +2ļøāƒ£ Verify your wallet <#{GuildEnvironment.channelVerifyNft}> + +Then try again…""" + DiscordFollowupMessageBuilder() + .AsEphemeral() + .AddEmbed(embed) + |> Error + else + Ok () + | 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 { @@ -358,6 +398,7 @@ let handleBuyItem (dispatch : IDiscordContext -> Task) (ctx : IDiscordContext) i do! checkHasStock storeItem do! checkItemSaleStillActive storeItem do! checkDoesntExceedStackCap storeItem.Item player + do! checkIsHolder storeItem (ctx.GetDiscordMember()) do! checkHasRequiredRole storeItem (ctx.GetDiscordMember()) do! checkHasRequiredInvites storeItem player return storeItem @@ -468,11 +509,12 @@ let sendBackalleyEmbed (ctx : IDiscordContext) = embed.Color <- DiscordColor.Lilac embed.Description <- $"Hey, what do you want, kid?\nI ain’t got all day…" builder.AddEmbed embed |> ignore - let button1 = DiscordButtonComponent(ButtonStyle.Danger, $"ShowStore-0-BACKALLEY1", $"NFT Raffle") :> DiscordComponent + let button3 = DiscordButtonComponent(ButtonStyle.Danger, $"ShowStore-0-BACKALLEY3", $"Holder Raffle") :> DiscordComponent + let button1 = DiscordButtonComponent(ButtonStyle.Success, $"ShowStore-0-BACKALLEY1", $"NFT Raffle") :> DiscordComponent let button2 = DiscordButtonComponent(ButtonStyle.Success, $"ShowStore-0-BACKALLEY2", $"Whitelist Raffle") :> DiscordComponent // let button3 = DiscordButtonComponent(ButtonStyle.Success, $"ShowStore-0-BACKALLEY3", $"USDT Raffles") :> DiscordComponent let button4 = DiscordButtonComponent(ButtonStyle.Primary, $"ShowJpegInventory-0-0", $"My Stash") :> DiscordComponent - builder.AddComponents [| button1 ; button2 ; button4 |] |> ignore + builder.AddComponents [| button3 ; button1 ; button2 ; button4 |] |> ignore do! GuildEnvironment.botClientJpeg.Value.SendMessageAsync(channel, builder) |> Async.AwaitTask diff --git a/Bot/GuildEnvironment.fs b/Bot/GuildEnvironment.fs index 142d996..4f8da18 100644 --- a/Bot/GuildEnvironment.fs +++ b/Bot/GuildEnvironment.fs @@ -46,6 +46,7 @@ let channelAnnouncements = getId "CHANNEL_ANNOUNCEMENTS" let channelGeneral = getId "CHANNEL_GENERAL" let channelQuests = getId "CHANNEL_QUESTS" let channelQuestProof = getId "CHANNEL_QUEST_PROOF" +let channelVerifyNft = getId "CHANNEL_VERIFY_NFT" //let channelThievery = getId "CHANNEL_THIEVERY" let botIdHackerBattle = getId "BOT_HACKER_BATTLE" let botIdArmory = getId "BOT_ARMORY" @@ -63,6 +64,10 @@ let roleMagicEden = getId "ROLE_MAGICEDEN" let roleRecruiter1x = getId "ROLE_RECRUITER_1X" let roleRecruiter2x = getId "ROLE_RECRUITER_2X" let roleRecruiter3x = getId "ROLE_RECRUITER_3X" +let roleHolder1 = getId "ROLE_HOLDER_1" +let roleHolder5 = getId "ROLE_HOLDER_5" +let roleHolder10 = getId "ROLE_HOLDER_10" +let roleHolder50 = getId "ROLE_HOLDER_50" let mutable botClientRecruit : DiscordClient option = None let mutable botClientHacker : DiscordClient option = None let mutable botClientSlots : DiscordClient option = None