Merge branch 'master' into dev

This commit is contained in:
Joseph Ferano 2022-06-19 21:34:33 +07:00
commit 36e6225201
4 changed files with 87 additions and 49 deletions

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,
SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end,
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"
SaleEnd = reader.int64OrNone "sale_end"
TotalSold = None
RequiresInvites = reader.intOrNone "require_invites"
RequiresRole = reader.stringOrNone "require_role" |> Option.map uint64
@ -127,7 +128,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,
SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end,
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
@ -138,6 +139,7 @@ let getAllActiveStoreItems () =
Stock = reader.int "stock"
LimitStock = reader.bool "limit_stock"
Available = reader.bool "available"
SaleEnd = reader.int64OrNone "sale_end"
TotalSold = None
RequiresInvites = reader.intOrNone "require_invites"
RequiresRole = reader.stringOrNone "require_role" |> Option.map uint64
@ -151,7 +153,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,
(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,
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
@ -166,6 +168,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"
SaleEnd = reader.int64OrNone "sale_end"
TotalSold = reader.intOrNone "total"
RequiresInvites = reader.intOrNone "require_invites"
RequiresRole = reader.stringOrNone "require_role" |> Option.map uint64
@ -178,7 +181,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,
SELECT store_id,stock,available,limit_stock,i.id,name,description,icon_url,image_url,category,require_role,require_invites,sale_end,
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
@ -189,6 +192,7 @@ let getStoreItemBySymbol (itemSymbol : string) =
Stock = reader.int "stock"
LimitStock = reader.bool "limit_stock"
Available = reader.bool "available"
SaleEnd = reader.int64OrNone "sale_end"
TotalSold = None
RequiresInvites = reader.intOrNone "require_invites"
RequiresRole = reader.stringOrNone "require_role" |> Option.map uint64

View File

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

View File

@ -53,13 +53,20 @@ let checkDoesntExceedStackCap (item : Item) player =
| _ , Some _ -> "You already own this item" |> embedWithError
| _ -> Ok ()
let checkItemSaleStillActive (item : StoreItem) =
match item.SaleEnd with
| Some time ->
let date = DateTimeOffset.FromUnixTimeSeconds(time).DateTime.ToUniversalTime()
if DateTime.UtcNow < date then Ok () else $"Sale for {item.Item.Name} has already ended!" |> embedWithError
| None -> Ok ()
let checkSoldItemAlready (item : Item) player =
if player.Inventory |> List.exists (fun i -> item.Id = i.Id)
then Ok ()
else $"{item.Name} not found in your inventory! Looks like you sold it already."
|> embedWithError
let checkHasItemsInArsenal itemType items player =
let checkHasItemsInArsenal itemType items =
if List.isEmpty items |> not
then Ok ()
else $"You currently have no {itemType} in your arsenal to sell!"
@ -125,6 +132,13 @@ let getItemEmbeds owned (items : StoreItem list) =
| _ -> ())
// if item.Item.Type = ItemType.Whitelist then
// embed.AddField("Mint Allowance", (if item.Item.Id = "WHITEOG" then 2 else 1) |> string, true) |> ignore
item.SaleEnd |> Option.iter (fun time ->
let date = DateTimeOffset.FromUnixTimeSeconds(time).DateTime.ToUniversalTime()
if DateTime.UtcNow < date then
embed.AddField("⏰ Closes", $"<t:{time}:R>", true) |> ignore
else
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.Title <- titleText
@ -142,12 +156,19 @@ let getBuyItemsEmbed storeId player (storeInventory : StoreItem list) =
storeInventory
|> List.map (fun item ->
let owned = player.Inventory |> List.exists (fun i -> i.Id = item.Item.Id)
let saleStillOngoing =
match item.SaleEnd with
| Some time ->
let date = DateTimeOffset.FromUnixTimeSeconds(time).DateTime.ToUniversalTime()
DateTime.UtcNow < date
| None -> true
let inStock = item.Available && (item.Stock > 0 || item.LimitStock = false)
match owned , inStock with
| _ , false ->
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 -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}")
| 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)
| _ ->
match checkDoesntExceedStackCap item.Item player with
| Ok _ -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}-{storeId}", $"Buy {item.Item.Name}")
@ -170,7 +191,7 @@ let purchaseItemEmbed quantity (item : Item) =
match item.Type with
| ItemType.Jpeg ->
let itemName = item.Name.Replace("🎟️", "")
embed.Description <- $"Congratulations! You are in the draw for the {itemName}.\n\nThe winner will be announced soon in <#{GuildEnvironment.channelGiveaway}>"
embed.Description <- $"Congratulations! You are in the draw for the {itemName}.\n\nWinners announced in <#{GuildEnvironment.channelGiveaway}>"
embed.ImageUrl <- item.ImageUrl
embed.Thumbnail <- DiscordEmbedBuilder.EmbedThumbnail()
embed.Thumbnail.Url <- item.IconUrl
@ -261,7 +282,7 @@ let buyForPlayer storeId player (filterBy : ItemType option) (ctx : IDiscordCont
let sell itemType getItems (ctx : IDiscordContext) =
executePlayerAction ctx (fun player -> async {
let items = getItems player.Inventory
match checkHasItemsInArsenal itemType items player with
match checkHasItemsInArsenal itemType items with
| Ok _ -> let itemStore = getSellEmbed items
do! ctx.FollowUp(itemStore) |> Async.AwaitTask
| Error e -> do! ctx.FollowUp e |> Async.AwaitTask
@ -335,6 +356,7 @@ let handleBuyItem (dispatch : IDiscordContext -> Task) (ctx : IDiscordContext) i
let storeItem = storeInventory |> List.find (fun si -> si.Item.Id = itemId)
do! checkHasSufficientFunds storeItem.Item player
do! checkHasStock storeItem
do! checkItemSaleStillActive storeItem
do! checkDoesntExceedStackCap storeItem.Item player
do! checkHasRequiredRole storeItem (ctx.GetDiscordMember())
do! checkHasRequiredInvites storeItem player

View File

@ -68,20 +68,26 @@ let private createInvite inviter code =
|> Sql.executeNonQueryAsync
|> Async.AwaitTask
let private addInvitedUser did code count =
try
let private addInvitedUser did inviterId code =
connStr
|> Sql.connect
|> Sql.executeTransactionAsync [
|> Sql.parameters [ "@code" , Sql.string code ; "@did" , Sql.string (string did) ; "@iid" , Sql.string (string inviterId) ]
|> Sql.query """
INSERT INTO invited_user (inviter_id, discord_id, invite_id)
VALUES (@iid, @did, (SELECT id FROM invite WHERE code = @code))
"""
INSERT INTO invited_user (discord_id, invite_id)
VALUES (@did, (SELECT id FROM invite WHERE code = @code));
""" , [ [ "@code" , Sql.string code ; "@did" , Sql.string (string did) ] ]
"UPDATE invite SET count = @count WHERE code = @code" , [ [ "count" , Sql.int count ; "code" , Sql.string code ] ]
]
|> Sql.executeNonQueryAsync
|> Async.AwaitTask
|> Async.Ignore
let private updateInviteCount code count =
connStr
|> Sql.connect
|> Sql.parameters [ "count" , Sql.int count ; "code" , Sql.string code ]
|> Sql.query "UPDATE invite SET count = @count WHERE code = @code"
|> Sql.executeNonQueryAsync
|> Async.AwaitTask
|> Async.Ignore
with _ -> async.Zero ()
let private markInvitedAccepted did =
connStr
@ -129,18 +135,14 @@ let private checkUserAlreadyInvited userId = async {
}
let checkInviteAccepted (userId : uint64) = async {
try
let! result =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string userId) ]
|> Sql.query "SELECT accepted FROM invited_user WHERE discord_id = @did"
|> Sql.executeRowAsync (fun read -> read.bool "accepted")
|> Sql.executeAsync (fun read -> read.bool "accepted")
|> Async.AwaitTask
return result
with ex ->
printfn "%s %u" ex.Message userId
return false
return List.tryHead result |> Option.defaultValue false
}
let private getInviteAttributions userId =
@ -279,10 +281,20 @@ let private processNewUser (eventArgs : GuildMemberAddEventArgs) =
for invite in guildInvites do
let result = cachedInvites.TryFind(invite.Code)
match result with
| Some (_,count) ->
| Some (inviterId,count) ->
if invite.Uses > count then
do! addInvitedUser eventArgs.Member.Id invite.Code invite.Uses |> Async.Ignore
do! Analytics.invitedUserEntered invite.Code invite.Inviter.Id eventArgs.Member.Id invite.Inviter.Username eventArgs.Member.Username
do! updateInviteCount invite.Code invite.Uses
try
match! checkUserAlreadyInvited eventArgs.Member.Id with
| false ->
do! addInvitedUser eventArgs.Member.Id inviterId invite.Code |> Async.Ignore
match! DbService.tryFindPlayer inviterId with
| Some inviter ->
do! Analytics.invitedUserEntered invite.Code inviter.DiscordId eventArgs.Member.Id inviter.Name eventArgs.Member.Username
| None ->
do! Analytics.invitedUserEntered invite.Code inviterId eventArgs.Member.Id "Unknown" eventArgs.Member.Username
| true -> ()
with ex -> printfn $"Tried to add existing user {eventArgs.Member.Id}:{eventArgs.Member.Username} to invites: {ex.Message}"
| None -> ()
} :> Task
@ -290,7 +302,7 @@ let acceptInvite (guild : DiscordGuild) (user : DiscordMember) =
task {
match! checkInviteAccepted user.Id with
| false ->
let! _ = markInvitedAccepted user.Id |> Async.Ignore
do! markInvitedAccepted user.Id |> Async.Ignore
try
let! invite = getInviteFromInvitedUser user.Id
let! player = DbService.tryFindPlayer invite.Inviter
@ -310,18 +322,20 @@ let acceptInvite (guild : DiscordGuild) (user : DiscordMember) =
let role3x = guild.Roles.TryGetValue(GuildEnvironment.roleRecruiter3x) |> snd
let role2x = guild.Roles.TryGetValue(GuildEnvironment.roleRecruiter2x) |> snd
let role1x = guild.Roles.TryGetValue(GuildEnvironment.roleRecruiter1x) |> snd
match invite.Count with
| count when count > 10 ->
do! [ user.GrantRoleAsync(role3x) ; user.RevokeRoleAsync(role2x) ; user.RevokeRoleAsync(role1x) ]
let! playerMember = guild.GetMemberAsync(invite.Inviter)
let! totalInvites = getInvitedUserCount player.DiscordId
if totalInvites >= 10 then
do! [ playerMember.GrantRoleAsync(role3x) ; playerMember.RevokeRoleAsync(role2x) ; playerMember.RevokeRoleAsync(role1x) ]
|> List.map Async.AwaitTask
|> Async.Parallel
|> Async.Ignore
| count when count > 5 ->
do! [ user.GrantRoleAsync(role2x) ; user.RevokeRoleAsync(role1x) ]
elif totalInvites >= 5 then
do! [ playerMember.GrantRoleAsync(role2x) ; playerMember.RevokeRoleAsync(role1x) ]
|> List.map Async.AwaitTask
|> Async.Parallel
|> Async.Ignore
| _ -> do! user.GrantRoleAsync(role1x)
else
do! playerMember.GrantRoleAsync(role1x)
do! Analytics.invitedUserAccepted invite.Code player.DiscordId user.Id player.Name user.Username
| None -> return ()
with _ -> ()
@ -343,6 +357,9 @@ let sendInitialEmbed (ctx : IDiscordContext) =
**__Bonus__**
💰 Earn an extra {InviteRewardAmount} $GBT for every invite!
<:purple_fist:986685279031152650> <@#{GuildEnvironment.roleRecruiter1x}> role if you invite 1 or more Degen
<:red_fist:986685280868249690> <@#{GuildEnvironment.roleRecruiter2x}> role is you invite 5 or more Degen
<:gold_fist:986685276942377052> <@#{GuildEnvironment.roleRecruiter3x}> role is you invite 10 or more Degen
**Every invite increases your chances of winning*
"""
@ -370,7 +387,7 @@ let showWalletStatus (ctx : IDiscordContext) =
try
match! getWalletAddress player.DiscordId with
| Some address -> do! Messaging.sendFollowUpMessage ctx $"""
🚀 __Mint Date:__ Mid June
🚀 __Mint Date:__ June 20th
__Status:__ We have successfully received your wallet address: {address}"""
| None -> do! Messaging.sendFollowUpMessage ctx "You haven't submitted a wallet yet. Type `/submit`, paste your **Solana Wallet Address**, then press enter"
with ex ->
@ -473,12 +490,7 @@ let handleMessageCreated _ (event : MessageCreateEventArgs) =
do! event.Message.DeleteAsync()
} :> Task
let handleGuildMemberAdded _ (eventArgs : GuildMemberAddEventArgs) =
task {
let! exists = checkUserAlreadyInvited eventArgs.Member.Id
if not exists then
do! processNewUser eventArgs
} :> Task
let handleGuildMemberAdded _ (eventArgs : GuildMemberAddEventArgs) = processNewUser eventArgs
let submitAddress (address : string) (ctx : IDiscordContext) =
PlayerInteractions.executePlayerAction ctx (fun player -> async {
@ -505,9 +517,8 @@ let submitAddress (address : string) (ctx : IDiscordContext) =
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleWhiteOGPending)
do! user.RevokeRoleAsync(role) |> Async.AwaitTask
do! Messaging.sendFollowUpMessage ctx $"""
🚀 __Mint Date:__ Mid June
🚀 __Mint Date:__ June 20th
{msg} {address}
Keep an eye on <#{GuildEnvironment.channelAnnouncements}> for updates."""