Add NFT holder check and rank feature for store item

This commit is contained in:
Joseph Ferano 2022-06-25 01:26:41 +07:00
parent 977e70d97d
commit fb9c638848
5 changed files with 68 additions and 16 deletions

View File

@ -100,15 +100,15 @@ let asdf _ (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) =
} |> Async.StartAsTask } |> Async.StartAsTask
:> Task :> Task
//hackerBattleBot.add_InteractionCreated(AsyncEventHandler(asdf)) //hackerBattleBot.add_InteractionCreated(AsyncEventHandler(asdf))
hackerBattleBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
GuildEnvironment.botClientHacker <- Some hackerBattleBot
jpegBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously jpegBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
GuildEnvironment.botClientJpeg <- Some jpegBot GuildEnvironment.botClientJpeg <- Some jpegBot
storeBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously storeBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
GuildEnvironment.botClientStore <- Some storeBot GuildEnvironment.botClientStore <- Some storeBot
hackerBattleBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
GuildEnvironment.botClientHacker <- Some hackerBattleBot
adminBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously adminBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
inviterBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously inviterBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously

View File

@ -105,7 +105,7 @@ let getStoreItems (storeId : string) =
|> Sql.connect |> Sql.connect
|> Sql.parameters [ "sid", Sql.string storeId ] |> Sql.parameters [ "sid", Sql.string storeId ]
|> Sql.query """ |> 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 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 FROM store_item
JOIN item i on store_item.item_id = i.id JOIN item i on store_item.item_id = i.id
@ -116,6 +116,7 @@ let getStoreItems (storeId : string) =
Stock = reader.int "stock" Stock = reader.int "stock"
LimitStock = reader.bool "limit_stock" LimitStock = reader.bool "limit_stock"
Available = reader.bool "available" Available = reader.bool "available"
Rank = reader.int "rank"
SaleEnd = reader.int64OrNone "sale_end" SaleEnd = reader.int64OrNone "sale_end"
TotalSold = None TotalSold = None
RequiresInvites = reader.intOrNone "require_invites" RequiresInvites = reader.intOrNone "require_invites"
@ -128,7 +129,7 @@ let getAllActiveStoreItems () =
connStr connStr
|> Sql.connect |> Sql.connect
|> Sql.query """ |> 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 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 FROM store_item
JOIN item i on store_item.item_id = i.id JOIN item i on store_item.item_id = i.id
@ -139,6 +140,7 @@ let getAllActiveStoreItems () =
Stock = reader.int "stock" Stock = reader.int "stock"
LimitStock = reader.bool "limit_stock" LimitStock = reader.bool "limit_stock"
Available = reader.bool "available" Available = reader.bool "available"
Rank = reader.int "rank"
SaleEnd = reader.int64OrNone "sale_end" SaleEnd = reader.int64OrNone "sale_end"
TotalSold = None TotalSold = None
RequiresInvites = reader.intOrNone "require_invites" RequiresInvites = reader.intOrNone "require_invites"
@ -153,7 +155,7 @@ let getRafflesWithPurchases storeId =
|> Sql.parameters [ "sid" , Sql.string storeId ] |> Sql.parameters [ "sid" , Sql.string storeId ]
|> Sql.query """ |> Sql.query """
WITH raffles AS 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 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 FROM store_item
JOIN item i on store_item.item_id = i.id 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" Stock = reader.int "stock"
LimitStock = reader.bool "limit_stock" LimitStock = reader.bool "limit_stock"
Available = reader.bool "available" Available = reader.bool "available"
Rank = reader.int "rank"
SaleEnd = reader.int64OrNone "sale_end" SaleEnd = reader.int64OrNone "sale_end"
TotalSold = reader.intOrNone "total" TotalSold = reader.intOrNone "total"
RequiresInvites = reader.intOrNone "require_invites" RequiresInvites = reader.intOrNone "require_invites"
@ -181,7 +184,7 @@ let getStoreItemBySymbol (itemSymbol : string) =
|> Sql.connect |> Sql.connect
|> Sql.parameters [ "iid", Sql.string itemSymbol ] |> Sql.parameters [ "iid", Sql.string itemSymbol ]
|> Sql.query """ |> 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 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 FROM store_item
JOIN item i on store_item.item_id = i.id JOIN item i on store_item.item_id = i.id
@ -192,6 +195,7 @@ let getStoreItemBySymbol (itemSymbol : string) =
Stock = reader.int "stock" Stock = reader.int "stock"
LimitStock = reader.bool "limit_stock" LimitStock = reader.bool "limit_stock"
Available = reader.bool "available" Available = reader.bool "available"
Rank = reader.int "rank"
SaleEnd = reader.int64OrNone "sale_end" SaleEnd = reader.int64OrNone "sale_end"
TotalSold = None TotalSold = None
RequiresInvites = reader.intOrNone "require_invites" RequiresInvites = reader.intOrNone "require_invites"

View File

@ -144,6 +144,7 @@ type StoreItem = {
LimitStock : bool LimitStock : bool
Available : bool Available : bool
TotalSold : int option TotalSold : int option
Rank : int
SaleEnd : int64 option SaleEnd : int64 option
RequiresRole : uint64 option RequiresRole : uint64 option
RequiresInvites : int option RequiresInvites : int option

View File

@ -11,6 +11,18 @@ open Degenz.Messaging
open Degenz.PlayerInteractions open Degenz.PlayerInteractions
open FsToolkit.ErrorHandling 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 = let embedWithError error =
DiscordFollowupMessageBuilder() DiscordFollowupMessageBuilder()
.AsEphemeral() .AsEphemeral()
@ -140,7 +152,7 @@ let getItemEmbeds owned (items : StoreItem list) =
embed.AddField("🚫 Closed", $"<t:{time}:R>", true) |> ignore) embed.AddField("🚫 Closed", $"<t:{time}:R>", true) |> ignore)
item.TotalSold |> Option.iter (fun total -> embed.AddField("Total Sold", string total, 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.Title <- titleText
embed.Description <- item.Item.Description embed.Description <- item.Item.Description
embed.ImageUrl <- "https://stage.degenz.game/blank-row.png" 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 match owned , inStock , saleStillOngoing with
| _ , false , _ -> | _ , false , _ ->
let msg = if item.Available then "Out of Stock" else "Unavailable" 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) DiscordButtonComponent(getRankButtonColor item.Rank, $"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 , true , true -> DiscordButtonComponent(getRankButtonColor item.Rank, $"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) | _ , _ , false -> DiscordButtonComponent(getRankButtonColor item.Rank, $"Buy-{item.Item.Id}-{storeId}", $"Closed {item.Item.Name}", true)
| _ -> | _ ->
match checkDoesntExceedStackCap item.Item player with match checkDoesntExceedStackCap item.Item player with
| Ok _ -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}") | Ok _ -> DiscordButtonComponent(getRankButtonColor item.Rank, $"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) | Error _ -> DiscordButtonComponent(getRankButtonColor item.Rank, $"Buy-{item.Item.Id}-{storeId}", $"Own {item.Item.Name}", true)
:> DiscordComponent) :> DiscordComponent)
let builder = let builder =
@ -255,6 +267,7 @@ let buy (storeId : string) (filterBy : ItemType option) (ctx : IDiscordContext)
match filterBy with match filterBy with
| Some itemType -> items |> List.filter (fun item -> item.Item.Type = itemType) | Some itemType -> items |> List.filter (fun item -> item.Item.Type = itemType)
| None -> items | None -> items
|> List.sortByDescending (fun i -> i.Rank)
let itemStore = getBuyItemsEmbed storeId player items' let itemStore = getBuyItemsEmbed storeId player items'
do! ctx.FollowUp itemStore |> Async.AwaitTask do! ctx.FollowUp itemStore |> Async.AwaitTask
do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeId do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeId
@ -271,6 +284,7 @@ let buyForPlayer storeId player (filterBy : ItemType option) (ctx : IDiscordCont
match filterBy with match filterBy with
| Some itemType -> items |> List.filter (fun item -> item.Item.Type = itemType) | Some itemType -> items |> List.filter (fun item -> item.Item.Type = itemType)
| None -> items | None -> items
|> List.sortByDescending (fun i -> i.Rank)
let itemStore = getBuyItemsEmbed storeId player items' let itemStore = getBuyItemsEmbed storeId player items'
do! ctx.FollowUp itemStore |> Async.AwaitTask do! ctx.FollowUp itemStore |> Async.AwaitTask
do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeId do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeId
@ -320,7 +334,7 @@ Then try again…
let checkHasRequiredRole storeItem (user : DiscordMember) = let checkHasRequiredRole storeItem (user : DiscordMember) =
match storeItem.RequiresRole with match storeItem.RequiresRole with
| Some roleId -> | 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 () Ok ()
else else
let embed = DiscordEmbedBuilder() let embed = DiscordEmbedBuilder()
@ -347,6 +361,32 @@ Then try again…
|> Error |> Error
| None -> Ok () | 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**, cant 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 // TODO: When you buy a shield, prompt the user to activate it
let handleBuyItem (dispatch : IDiscordContext -> Task) (ctx : IDiscordContext) itemId = let handleBuyItem (dispatch : IDiscordContext -> Task) (ctx : IDiscordContext) itemId =
executePlayerAction ctx (fun player -> async { executePlayerAction ctx (fun player -> async {
@ -358,6 +398,7 @@ let handleBuyItem (dispatch : IDiscordContext -> Task) (ctx : IDiscordContext) i
do! checkHasStock storeItem do! checkHasStock storeItem
do! checkItemSaleStillActive storeItem do! checkItemSaleStillActive storeItem
do! checkDoesntExceedStackCap storeItem.Item player do! checkDoesntExceedStackCap storeItem.Item player
do! checkIsHolder storeItem (ctx.GetDiscordMember())
do! checkHasRequiredRole storeItem (ctx.GetDiscordMember()) do! checkHasRequiredRole storeItem (ctx.GetDiscordMember())
do! checkHasRequiredInvites storeItem player do! checkHasRequiredInvites storeItem player
return storeItem return storeItem
@ -468,11 +509,12 @@ let sendBackalleyEmbed (ctx : IDiscordContext) =
embed.Color <- DiscordColor.Lilac embed.Color <- DiscordColor.Lilac
embed.Description <- $"Hey, what do you want, kid?\nI aint got all day" embed.Description <- $"Hey, what do you want, kid?\nI aint got all day"
builder.AddEmbed embed |> ignore 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 button2 = DiscordButtonComponent(ButtonStyle.Success, $"ShowStore-0-BACKALLEY2", $"Whitelist Raffle") :> 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", $"My Stash") :> 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) do! GuildEnvironment.botClientJpeg.Value.SendMessageAsync(channel, builder)
|> Async.AwaitTask |> Async.AwaitTask

View File

@ -46,6 +46,7 @@ let channelAnnouncements = getId "CHANNEL_ANNOUNCEMENTS"
let channelGeneral = getId "CHANNEL_GENERAL" let channelGeneral = getId "CHANNEL_GENERAL"
let channelQuests = getId "CHANNEL_QUESTS" let channelQuests = getId "CHANNEL_QUESTS"
let channelQuestProof = getId "CHANNEL_QUEST_PROOF" let channelQuestProof = getId "CHANNEL_QUEST_PROOF"
let channelVerifyNft = getId "CHANNEL_VERIFY_NFT"
//let channelThievery = getId "CHANNEL_THIEVERY" //let channelThievery = getId "CHANNEL_THIEVERY"
let botIdHackerBattle = getId "BOT_HACKER_BATTLE" let botIdHackerBattle = getId "BOT_HACKER_BATTLE"
let botIdArmory = getId "BOT_ARMORY" let botIdArmory = getId "BOT_ARMORY"
@ -63,6 +64,10 @@ let roleMagicEden = getId "ROLE_MAGICEDEN"
let roleRecruiter1x = getId "ROLE_RECRUITER_1X" let roleRecruiter1x = getId "ROLE_RECRUITER_1X"
let roleRecruiter2x = getId "ROLE_RECRUITER_2X" let roleRecruiter2x = getId "ROLE_RECRUITER_2X"
let roleRecruiter3x = getId "ROLE_RECRUITER_3X" 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 botClientRecruit : DiscordClient option = None
let mutable botClientHacker : DiscordClient option = None let mutable botClientHacker : DiscordClient option = None
let mutable botClientSlots : DiscordClient option = None let mutable botClientSlots : DiscordClient option = None