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
:> 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

View File

@ -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"

View File

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

View File

@ -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", $"<t:{time}:R>", 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**, 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
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 aint 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

View File

@ -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