Merge branch 'dev' into staging
This commit is contained in:
commit
3e0e500182
11
Bot/Admin.fs
11
Bot/Admin.fs
@ -2,6 +2,7 @@ module Degenz.Admin
|
|||||||
|
|
||||||
open System
|
open System
|
||||||
open System.IO
|
open System.IO
|
||||||
|
open System.Reflection
|
||||||
open System.Threading.Tasks
|
open System.Threading.Tasks
|
||||||
open DSharpPlus
|
open DSharpPlus
|
||||||
open DSharpPlus.Entities
|
open DSharpPlus.Entities
|
||||||
@ -14,6 +15,7 @@ type InitEmbeds =
|
|||||||
| Dojo = 0
|
| Dojo = 0
|
||||||
| Whitelist = 1
|
| Whitelist = 1
|
||||||
| Slots = 2
|
| Slots = 2
|
||||||
|
| JpegStore = 3
|
||||||
|
|
||||||
let handleGuildDownloadReady (_ : DiscordClient) (event : GuildDownloadCompletedEventArgs) =
|
let handleGuildDownloadReady (_ : DiscordClient) (event : GuildDownloadCompletedEventArgs) =
|
||||||
task {
|
task {
|
||||||
@ -24,6 +26,7 @@ let handleGuildDownloadReady (_ : DiscordClient) (event : GuildDownloadCompleted
|
|||||||
let permission = DiscordApplicationCommandPermission(adminRole, true)
|
let permission = DiscordApplicationCommandPermission(adminRole, true)
|
||||||
let commands = commands |> Seq.map (fun com -> DiscordGuildApplicationCommandPermissions(com.Id, [ permission ]))
|
let commands = commands |> Seq.map (fun com -> DiscordGuildApplicationCommandPermissions(com.Id, [ permission ]))
|
||||||
do! guild.BatchEditApplicationCommandPermissionsAsync(commands) |> Async.AwaitTask |> Async.Ignore
|
do! guild.BatchEditApplicationCommandPermissionsAsync(commands) |> Async.AwaitTask |> Async.Ignore
|
||||||
|
return ()
|
||||||
} :> Task
|
} :> Task
|
||||||
|
|
||||||
let sendEmbed embed (ctx : IDiscordContext) =
|
let sendEmbed embed (ctx : IDiscordContext) =
|
||||||
@ -32,6 +35,7 @@ let sendEmbed embed (ctx : IDiscordContext) =
|
|||||||
| InitEmbeds.Dojo -> Trainer.sendInitialEmbed ctx
|
| InitEmbeds.Dojo -> Trainer.sendInitialEmbed ctx
|
||||||
| InitEmbeds.Whitelist -> InviteTracker.sendInitialEmbed ctx
|
| InitEmbeds.Whitelist -> InviteTracker.sendInitialEmbed ctx
|
||||||
| InitEmbeds.Slots -> SlotMachine.sendInitialEmbedFromSlashCommand ctx
|
| InitEmbeds.Slots -> SlotMachine.sendInitialEmbedFromSlashCommand ctx
|
||||||
|
| InitEmbeds.JpegStore -> Store.sendInitialEmbed ctx
|
||||||
| _ -> ()
|
| _ -> ()
|
||||||
do! Messaging.sendSimpleResponse ctx "Sent!"
|
do! Messaging.sendSimpleResponse ctx "Sent!"
|
||||||
} :> Task
|
} :> Task
|
||||||
@ -137,24 +141,29 @@ type AdminBot() =
|
|||||||
else
|
else
|
||||||
Messaging.sendSimpleResponse ctx $"You are not admin" |> Async.StartAsTask :> Task
|
Messaging.sendSimpleResponse ctx $"You are not admin" |> Async.StartAsTask :> Task
|
||||||
|
|
||||||
|
[<SlashCommandPermissions(Permissions.Administrator)>]
|
||||||
[<SlashCommand("admin-invites", "Get total invites from a specific user", false)>]
|
[<SlashCommand("admin-invites", "Get total invites from a specific user", false)>]
|
||||||
member this.GetAttributions (ctx : InteractionContext, [<Option("player", "The player you want to check")>] user : DiscordUser) =
|
member this.GetAttributions (ctx : InteractionContext, [<Option("player", "The player you want to check")>] user : DiscordUser) =
|
||||||
enforceAdmin (DiscordInteractionContext ctx) (InviteTracker.getInvitedUsersForId user)
|
enforceAdmin (DiscordInteractionContext ctx) (InviteTracker.getInvitedUsersForId user)
|
||||||
|
|
||||||
|
[<SlashCommandPermissions(Permissions.Administrator)>]
|
||||||
[<SlashCommand("admin-whitelist-stock", "Set whitelist stock", false)>]
|
[<SlashCommand("admin-whitelist-stock", "Set whitelist stock", false)>]
|
||||||
member this.SetStock (ctx : InteractionContext, [<Option("amount", "Set the amount of WL available for purchase")>] amount : int64) =
|
member this.SetStock (ctx : InteractionContext, [<Option("amount", "Set the amount of WL available for purchase")>] amount : int64) =
|
||||||
enforceAdmin (DiscordInteractionContext ctx) (InviteTracker.setWhitelistStock (int amount))
|
enforceAdmin (DiscordInteractionContext ctx) (InviteTracker.setCurrentWhitelistStock (int amount))
|
||||||
|
|
||||||
|
[<SlashCommandPermissions(Permissions.Administrator)>]
|
||||||
[<SlashCommand("admin-send-embed", "Set whitelist stock", false)>]
|
[<SlashCommand("admin-send-embed", "Set whitelist stock", false)>]
|
||||||
member this.SendEmbedToChannel (ctx : InteractionContext, [<Option("embed", "Which embed to send")>] embed : InitEmbeds) =
|
member this.SendEmbedToChannel (ctx : InteractionContext, [<Option("embed", "Which embed to send")>] embed : InitEmbeds) =
|
||||||
enforceAdmin (DiscordInteractionContext ctx) (sendEmbed embed)
|
enforceAdmin (DiscordInteractionContext ctx) (sendEmbed embed)
|
||||||
|
|
||||||
|
[<SlashCommandPermissions(Permissions.Administrator)>]
|
||||||
[<SlashCommand("admin-get-msg-reactions", "Set whitelist stock", false)>]
|
[<SlashCommand("admin-get-msg-reactions", "Set whitelist stock", false)>]
|
||||||
member this.GetMessageReactions (ctx : InteractionContext,
|
member this.GetMessageReactions (ctx : InteractionContext,
|
||||||
[<Option("channel", "The channel where the message is")>] channel : DiscordChannel,
|
[<Option("channel", "The channel where the message is")>] channel : DiscordChannel,
|
||||||
[<Option("message-id", "The ID of the message with all the reactions")>] messageId : string) =
|
[<Option("message-id", "The ID of the message with all the reactions")>] messageId : string) =
|
||||||
enforceAdmin (DiscordInteractionContext ctx) (getUsersFromMessageReactions channel messageId)
|
enforceAdmin (DiscordInteractionContext ctx) (getUsersFromMessageReactions channel messageId)
|
||||||
|
|
||||||
|
[<SlashCommandPermissions(Permissions.Administrator)>]
|
||||||
[<SlashCommand("admin-get-invites-table", "Invites Table", false)>]
|
[<SlashCommand("admin-get-invites-table", "Invites Table", false)>]
|
||||||
member this.GetInvitesFromReactedMessages (ctx : InteractionContext,
|
member this.GetInvitesFromReactedMessages (ctx : InteractionContext,
|
||||||
[<Option("channel", "The channel where the message is")>] channel : DiscordChannel,
|
[<Option("channel", "The channel where the message is")>] channel : DiscordChannel,
|
||||||
|
@ -94,33 +94,33 @@ let arsenalCommand (discordMember : DiscordMember) =
|
|||||||
]
|
]
|
||||||
track "Arsenal Command Invoked" discordMember.Id data
|
track "Arsenal Command Invoked" discordMember.Id data
|
||||||
|
|
||||||
let buyWeaponCommand (discordMember : DiscordMember) weaponType =
|
let buyItemCommand (discordMember : DiscordMember) store =
|
||||||
let data = [
|
let data = [
|
||||||
"user_display_name" , discordMember.Username
|
"user_display_name" , discordMember.Username
|
||||||
"weapon_type" , string weaponType
|
"store_symbol" , store
|
||||||
]
|
]
|
||||||
track "Buy Weapon Command Invoked" discordMember.Id data
|
track "Buy Item Command Invoked" discordMember.Id data
|
||||||
|
|
||||||
let sellWeaponCommand (discordMember : DiscordMember) weaponType =
|
let sellItemCommand (discordMember : DiscordMember) store =
|
||||||
let data = [
|
let data = [
|
||||||
"user_display_name" , discordMember.Username
|
"user_display_name" , discordMember.Username
|
||||||
"weapon_type" , string weaponType
|
"store_symbol" , store
|
||||||
]
|
]
|
||||||
track "Sell Weapon Command Invoked" discordMember.Id data
|
track "Sell Item Command Invoked" discordMember.Id data
|
||||||
|
|
||||||
let buyWeaponButton (discordMember : DiscordMember) (weapon : ItemDetails) =
|
let buyWeaponButton (discordMember : DiscordMember) itemName itemPrice =
|
||||||
let data = [
|
let data = [
|
||||||
"user_display_name" , discordMember.Username
|
"user_display_name" , discordMember.Username
|
||||||
"weapon_name" , weapon.Name
|
"weapon_name" , itemName
|
||||||
"weapon_price" , string weapon.Price
|
"weapon_price" , string itemPrice
|
||||||
]
|
]
|
||||||
track "Buy Weapon Button Clicked" discordMember.Id data
|
track "Buy Weapon Button Clicked" discordMember.Id data
|
||||||
|
|
||||||
let sellWeaponButton (discordMember : DiscordMember) (weapon : ItemDetails) =
|
let sellWeaponButton (discordMember : DiscordMember) (weapon : Item) price =
|
||||||
let data = [
|
let data = [
|
||||||
"user_display_name" , discordMember.Username
|
"user_display_name" , discordMember.Username
|
||||||
"weapon_name" , weapon.Name
|
"weapon_name" , weapon.Name
|
||||||
"weapon_price" , string weapon.Price
|
"weapon_price" , string price
|
||||||
]
|
]
|
||||||
track "Sell Weapon Button Clicked" discordMember.Id data
|
track "Sell Weapon Button Clicked" discordMember.Id data
|
||||||
|
|
||||||
|
@ -77,7 +77,6 @@ inviterBot.add_ComponentInteractionCreated(AsyncEventHandler(InviteTracker.handl
|
|||||||
slotsBot.add_ComponentInteractionCreated(AsyncEventHandler(SlotMachine.handleButton))
|
slotsBot.add_ComponentInteractionCreated(AsyncEventHandler(SlotMachine.handleButton))
|
||||||
slotsBot.add_GuildDownloadCompleted(AsyncEventHandler(SlotMachine.handleGuildDownloadCompleted))
|
slotsBot.add_GuildDownloadCompleted(AsyncEventHandler(SlotMachine.handleGuildDownloadCompleted))
|
||||||
slotsBot.add_MessageCreated(AsyncEventHandler(SlotMachine.handleMessageCreated))
|
slotsBot.add_MessageCreated(AsyncEventHandler(SlotMachine.handleMessageCreated))
|
||||||
adminBot.add_GuildDownloadCompleted(AsyncEventHandler(Admin.handleGuildDownloadReady))
|
|
||||||
|
|
||||||
let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) =
|
let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) =
|
||||||
async {
|
async {
|
||||||
@ -95,15 +94,15 @@ let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEven
|
|||||||
:> Task
|
:> Task
|
||||||
//hackerBattleBot.add_InteractionCreated(AsyncEventHandler(asdf))
|
//hackerBattleBot.add_InteractionCreated(AsyncEventHandler(asdf))
|
||||||
|
|
||||||
|
storeBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
||||||
|
GuildEnvironment.botClientStore <- Some storeBot
|
||||||
|
|
||||||
slotsBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
slotsBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
||||||
GuildEnvironment.botClientSlots <- Some slotsBot
|
GuildEnvironment.botClientSlots <- Some slotsBot
|
||||||
|
|
||||||
hackerBattleBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
hackerBattleBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
||||||
GuildEnvironment.botClientHacker <- Some hackerBattleBot
|
GuildEnvironment.botClientHacker <- Some hackerBattleBot
|
||||||
|
|
||||||
storeBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
|
||||||
//GuildEnvironment.botClient <- Some storeBot.CurrentUser
|
|
||||||
|
|
||||||
inviterBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
inviterBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
||||||
GuildEnvironment.botClientRecruit <- Some inviterBot
|
GuildEnvironment.botClientRecruit <- Some inviterBot
|
||||||
|
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
<RootNamespace>Degenz</RootNamespace>
|
<RootNamespace>Degenz</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Items.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="paket.references" />
|
<Content Include="paket.references" />
|
||||||
<Compile Include="Prelude.fs" />
|
<Compile Include="Prelude.fs" />
|
||||||
<Compile Include="GuildEnvironment.fs" />
|
<Compile Include="GuildEnvironment.fs" />
|
||||||
|
234
Bot/DbService.fs
234
Bot/DbService.fs
@ -1,11 +1,149 @@
|
|||||||
module Degenz.DbService
|
module Degenz.DbService
|
||||||
|
|
||||||
open System
|
open System
|
||||||
|
open Npgsql
|
||||||
open Npgsql.FSharp
|
open Npgsql.FSharp
|
||||||
open Degenz
|
open Degenz
|
||||||
|
|
||||||
let connStr = GuildEnvironment.connectionString
|
let connStr = GuildEnvironment.connectionString
|
||||||
|
|
||||||
|
type StatMod = { mod_type :string ; target_stat : string ; mod_amount : float }
|
||||||
|
NpgsqlConnection.GlobalTypeMapper.MapComposite<StatMod>("stat_mod") |> ignore
|
||||||
|
|
||||||
|
let readItem (reader : RowReader) =
|
||||||
|
let convertStatMod { mod_type = modType ; target_stat = targetStat; mod_amount = modAmount } =
|
||||||
|
let fx =
|
||||||
|
match modType with
|
||||||
|
| "Min" -> Min (int modAmount)
|
||||||
|
| "Max" -> Max (int modAmount)
|
||||||
|
| "RateMultiplier" -> RateMultiplier (modAmount)
|
||||||
|
| "Booster" -> Add (int modAmount)
|
||||||
|
| _ -> Add (int modAmount)
|
||||||
|
let ( _ , stat ) = StatId.TryParse(targetStat)
|
||||||
|
{ TargetStat = stat ; Effect = fx }
|
||||||
|
{ Item.Id = reader.int "id"
|
||||||
|
Item.Name = reader.string "name"
|
||||||
|
Item.Description = reader.string "description"
|
||||||
|
Item.IconUrl = reader.string "icon"
|
||||||
|
Item.Symbol = reader.string "symbol"
|
||||||
|
Item.Type =
|
||||||
|
match reader.string "category" with
|
||||||
|
| "Hack" -> ItemType.Hack
|
||||||
|
| "Shield" -> ItemType.Shield
|
||||||
|
| "Food" -> ItemType.Food
|
||||||
|
| "Accessory" -> ItemType.Accessory
|
||||||
|
| "Jpeg" -> ItemType.Jpeg
|
||||||
|
| _ -> ItemType.Misc
|
||||||
|
Item.Attributes = [
|
||||||
|
reader.intOrNone "buy_price" |> Option.map (fun a -> Buyable (a * 1<GBT>))
|
||||||
|
reader.intOrNone "sell_price" |> Option.map (fun a -> Sellable (a * 1<GBT>))
|
||||||
|
reader.intOrNone "expiration" |> Option.map (fun a -> Expireable (a * 1<mins>))
|
||||||
|
reader.floatOrNone "drop_chance" |> Option.map (float >> Droppable)
|
||||||
|
reader.intOrNone "attack_power" |> Option.map Attackable
|
||||||
|
reader.intOrNone "defense_power" |> Option.map Defendable
|
||||||
|
reader.stringOrNone "class_name" |> Option.map Classable
|
||||||
|
reader.intOrNone "max_stack" |> Option.map Stackable
|
||||||
|
if reader.bool "can_trade" then Some Tradeable else None
|
||||||
|
if reader.bool "can_consume" then Some Consumable else None
|
||||||
|
|
||||||
|
(match reader.fieldValue<StatMod array> "mods" with
|
||||||
|
| [||] -> None
|
||||||
|
| mods -> mods |> Array.map convertStatMod |> Array.toList |> Modifiable |> Some)
|
||||||
|
] |> List.choose id
|
||||||
|
}
|
||||||
|
|
||||||
|
let getPlayerInventory (did : uint64) =
|
||||||
|
connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "did", Sql.string (string did) ]
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT ii.id,ii.symbol,name,description,icon,category,buy_price,sell_price,rate_limit,expiration,drop_chance,can_trade,can_consume,
|
||||||
|
attack_power,defense_power,class_name,max_stack,mods
|
||||||
|
FROM inventory_item
|
||||||
|
JOIN item ii on inventory_item.item_id = ii.id
|
||||||
|
JOIN "user" usr on inventory_item.user_id = usr.id
|
||||||
|
WHERE usr.discord_id = @did;
|
||||||
|
"""
|
||||||
|
|> Sql.executeAsync readItem
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let addToPlayerInventory (did : uint64) (item : Item) =
|
||||||
|
connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ ( "@did" , Sql.string (string did) ) ; ( "iid" , Sql.int item.Id )]
|
||||||
|
|> Sql.query """
|
||||||
|
INSERT INTO inventory_item (item_id, user_id)
|
||||||
|
VALUES (@iid, (SELECT id FROM "user" WHERE discord_id = @did));
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let getStoreSymbol (channelId : uint64) =
|
||||||
|
connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "cid", Sql.string (string channelId) ]
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT symbol FROM store WHERE channel_id = @cid
|
||||||
|
"""
|
||||||
|
|> Sql.executeRowAsync (fun read -> read.string "symbol")
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let getStoreItems (channelId : uint64) =
|
||||||
|
connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "cid", Sql.string (string channelId) ]
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT stock,available,limit_stock,i.id,i.symbol,name,description,icon,category,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 store st on store_item.store_id = st.id
|
||||||
|
JOIN item i on store_item.item_id = i.id
|
||||||
|
WHERE channel_id = @cid AND available;
|
||||||
|
"""
|
||||||
|
|> Sql.executeAsync (fun reader -> {
|
||||||
|
Stock = reader.int "stock"
|
||||||
|
LimitStock = reader.bool "limit_stock"
|
||||||
|
Available = reader.bool "available"
|
||||||
|
StoreItem.Item = readItem reader
|
||||||
|
})
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let decrementItemStock (item : Item) =
|
||||||
|
connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ ( "iid" , Sql.int item.Id) ]
|
||||||
|
|> Sql.query """
|
||||||
|
UPDATE store_item SET stock = GREATEST(stock - 1, 0)
|
||||||
|
WHERE store_item.item_id = @iid
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|> Async.Ignore
|
||||||
|
|
||||||
|
let getWeapons () =
|
||||||
|
connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT i.id,i.symbol,name,description,icon,category,buy_price,sell_price,rate_limit,expiration,drop_chance,can_trade,can_consume,
|
||||||
|
attack_power,defense_power,class_name,max_stack,mods
|
||||||
|
FROM item WHERE category = 'Hack' OR symbol = 'Shield'
|
||||||
|
"""
|
||||||
|
|> Sql.executeAsync readItem
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let consumeItem (did : uint64) (item : Item) =
|
||||||
|
connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ ( "@did" , Sql.string (string did) ) ; ( "iid" , Sql.int item.Id )]
|
||||||
|
|> Sql.query """
|
||||||
|
DELETE FROM inventory_item
|
||||||
|
WHERE id IN (SELECT id FROM inventory_item
|
||||||
|
WHERE user_id = (SELECT id FROM "user" WHERE discord_id = @did)
|
||||||
|
AND item_id = @iid LIMIT 1)
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
let getPlayerEvents (did : uint64) =
|
let getPlayerEvents (did : uint64) =
|
||||||
connStr
|
connStr
|
||||||
|> Sql.connect
|
|> Sql.connect
|
||||||
@ -34,36 +172,6 @@ let getPlayerEvents (did : uint64) =
|
|||||||
)
|
)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
let getLastPlayedSlotFromPlayer (did : uint64) = async {
|
|
||||||
let! events =
|
|
||||||
connStr
|
|
||||||
|> Sql.connect
|
|
||||||
|> Sql.parameters [ "did", Sql.string (string did) ]
|
|
||||||
|> Sql.query """
|
|
||||||
SELECT player_event.updated_at FROM player_event
|
|
||||||
JOIN "user" u on u.id = player_event.user_id
|
|
||||||
WHERE u.discord_id = @did AND event_type = 'PlayingSlot'
|
|
||||||
"""
|
|
||||||
|> Sql.executeAsync (fun read -> read.dateTime "updated_at" )
|
|
||||||
|> Async.AwaitTask
|
|
||||||
match events with
|
|
||||||
| [] -> return None
|
|
||||||
| es -> return Some (List.head es)
|
|
||||||
}
|
|
||||||
|
|
||||||
let updateSlotPlayedFromPlayer (did : uint64) =
|
|
||||||
connStr
|
|
||||||
|> Sql.connect
|
|
||||||
|> Sql.parameters [ "did", Sql.string (string did) ]
|
|
||||||
|> Sql.query """
|
|
||||||
WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did)
|
|
||||||
UPDATE player_event SET updated_at = now() at time zone 'utc'
|
|
||||||
FROM usr WHERE usr.id = user_id AND player_event.event_type = 'PlayingSlot';
|
|
||||||
"""
|
|
||||||
|> Sql.executeNonQueryAsync
|
|
||||||
|> Async.AwaitTask
|
|
||||||
|> Async.Ignore
|
|
||||||
|
|
||||||
let updatePlayerStats (player : PlayerData) =
|
let updatePlayerStats (player : PlayerData) =
|
||||||
connStr
|
connStr
|
||||||
|> Sql.connect
|
|> Sql.connect
|
||||||
@ -89,28 +197,25 @@ let tryFindPlayer (discordId : uint64) = async {
|
|||||||
|> Sql.connect
|
|> Sql.connect
|
||||||
|> Sql.parameters [ "did", Sql.string (string discordId) ]
|
|> Sql.parameters [ "did", Sql.string (string discordId) ]
|
||||||
|> Sql.query """
|
|> Sql.query """
|
||||||
SELECT discord_id, display_name, gbt, in_game, inventory, strength, focus, charisma, luck FROM "user"
|
SELECT discord_id, display_name, gbt, in_game, strength, focus, charisma, luck FROM "user"
|
||||||
WHERE discord_id = @did
|
WHERE discord_id = @did
|
||||||
"""
|
"""
|
||||||
|> Sql.executeAsync (fun read ->
|
|> Sql.executeAsync (fun read ->
|
||||||
let inv = read.intArray "inventory"
|
{| DiscordId = read.string "discord_id" |> uint64
|
||||||
{|
|
Name = read.string "display_name"
|
||||||
DiscordId = read.string "discord_id" |> uint64
|
Bank = read.intOrNone "gbt" |> Option.map ((*) 1<GBT>) |> Option.defaultValue 0<GBT>
|
||||||
Name = read.string "display_name"
|
Strength = read.intOrNone "strength" |> Option.defaultValue 0
|
||||||
Bank = read.intOrNone "gbt" |> Option.map ((*) 1<GBT>) |> Option.defaultValue 0<GBT>
|
Focus = read.intOrNone "focus" |> Option.defaultValue 0
|
||||||
Inventory = inv |> Array.toList
|
Charisma = read.intOrNone "charisma" |> Option.defaultValue 0
|
||||||
Strength = read.intOrNone "strength" |> Option.defaultValue 0
|
Luck = read.intOrNone "luck" |> Option.defaultValue 0
|
||||||
Focus = read.intOrNone "focus" |> Option.defaultValue 0
|
Active = read.bool "in_game"
|
||||||
Charisma = read.intOrNone "charisma" |> Option.defaultValue 0
|
|
||||||
Luck = read.intOrNone "luck" |> Option.defaultValue 0
|
|
||||||
Active = read.bool "in_game"
|
|
||||||
|})
|
|})
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
match List.tryHead user with
|
match List.tryHead user with
|
||||||
| None -> return None
|
| None -> return None
|
||||||
| Some u ->
|
| Some u ->
|
||||||
let! events = getPlayerEvents u.DiscordId
|
let! events = getPlayerEvents u.DiscordId
|
||||||
let inventory = u.Inventory |> List.choose (fun id -> Armory.weapons |> List.tryFind (fun item -> item.Id = id))
|
let! inventory = getPlayerInventory discordId
|
||||||
let strength = PlayerStats.calculateActiveStat StatId.Strength u.Strength inventory
|
let strength = PlayerStats.calculateActiveStat StatId.Strength u.Strength inventory
|
||||||
let focus = PlayerStats.calculateActiveStat StatId.Focus u.Focus inventory
|
let focus = PlayerStats.calculateActiveStat StatId.Focus u.Focus inventory
|
||||||
let charisma = PlayerStats.calculateActiveStat StatId.Charisma u.Charisma inventory
|
let charisma = PlayerStats.calculateActiveStat StatId.Charisma u.Charisma inventory
|
||||||
@ -135,7 +240,7 @@ let updatePlayerCurrency (addAmount : int<GBT>) (player : PlayerData) =
|
|||||||
"did", Sql.string (string player.DiscordId)
|
"did", Sql.string (string player.DiscordId)
|
||||||
"gbt", Sql.int (int addAmount)
|
"gbt", Sql.int (int addAmount)
|
||||||
] |> Sql.query """
|
] |> Sql.query """
|
||||||
UPDATE "user" SET gbt = gbt + @gbt WHERE discord_id = @did;
|
UPDATE "user" SET gbt = gbt + GREATEST(gbt + @gbt, 0) WHERE discord_id = @did;
|
||||||
"""
|
"""
|
||||||
|> Sql.executeNonQueryAsync
|
|> Sql.executeNonQueryAsync
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
@ -146,14 +251,12 @@ let updatePlayer (player : PlayerData) =
|
|||||||
|> Sql.parameters [
|
|> Sql.parameters [
|
||||||
"did", Sql.string (string player.DiscordId)
|
"did", Sql.string (string player.DiscordId)
|
||||||
"gbt", Sql.int (int player.Bank)
|
"gbt", Sql.int (int player.Bank)
|
||||||
"inv", Sql.intArray (player.Inventory |> Array.ofList |> Array.map (fun item -> item.Id))
|
|
||||||
"strength", Sql.int player.Stats.Strength.Amount
|
"strength", Sql.int player.Stats.Strength.Amount
|
||||||
"focus", Sql.int player.Stats.Focus.Amount
|
"focus", Sql.int player.Stats.Focus.Amount
|
||||||
"charisma", Sql.int player.Stats.Charisma.Amount
|
"charisma", Sql.int player.Stats.Charisma.Amount
|
||||||
"luck", Sql.int player.Stats.Luck.Amount
|
"luck", Sql.int player.Stats.Luck.Amount
|
||||||
] |> Sql.query """
|
] |> Sql.query """
|
||||||
UPDATE "user" SET gbt = @gbt, inventory = @inv,
|
UPDATE "user" SET gbt = @gbt, strength = @strength, focus = @focus, charisma = @charisma, luck = @luck
|
||||||
strength = @strength, focus = @focus, charisma = @charisma, luck = @luck
|
|
||||||
WHERE discord_id = @did
|
WHERE discord_id = @did
|
||||||
"""
|
"""
|
||||||
|> Sql.executeNonQueryAsync
|
|> Sql.executeNonQueryAsync
|
||||||
@ -283,40 +386,3 @@ let getRandomHackablePlayers (did : uint64) =
|
|||||||
|> Sql.executeAsync (fun read -> {| Id = read.string "discord_id" |> uint64 ; Name = read.string "display_name" |})
|
|> Sql.executeAsync (fun read -> {| Id = read.string "discord_id" |> uint64 ; Name = read.string "display_name" |})
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
let getWhitelistItem () =
|
|
||||||
connStr
|
|
||||||
|> Sql.connect
|
|
||||||
|> Sql.query """
|
|
||||||
SELECT stock, price FROM item WHERE symbol = 'WHITELIST'
|
|
||||||
"""
|
|
||||||
|> Sql.executeRowAsync (fun read -> {| Stock = read.int "stock" ; Price = (read.int "price") * 1<GBT> |})
|
|
||||||
|> Async.AwaitTask
|
|
||||||
|
|
||||||
let updateWhitelistStock () = async {
|
|
||||||
try
|
|
||||||
do! connStr
|
|
||||||
|> Sql.connect
|
|
||||||
|> Sql.query """
|
|
||||||
UPDATE item SET stock = stock - 1 WHERE symbol = 'WHITELIST'
|
|
||||||
"""
|
|
||||||
|> Sql.executeNonQueryAsync
|
|
||||||
|> Async.AwaitTask
|
|
||||||
|> Async.Ignore
|
|
||||||
return true
|
|
||||||
with _ -> return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let setWhitelistStock amount = async {
|
|
||||||
try
|
|
||||||
do! connStr
|
|
||||||
|> Sql.connect
|
|
||||||
|> Sql.parameters [ ( "amount" , Sql.int amount ) ]
|
|
||||||
|> Sql.query """
|
|
||||||
UPDATE item SET stock = @amount WHERE symbol = 'WHITELIST'
|
|
||||||
"""
|
|
||||||
|> Sql.executeNonQueryAsync
|
|
||||||
|> Async.AwaitTask
|
|
||||||
|> Async.Ignore
|
|
||||||
return true
|
|
||||||
with _ -> return false
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
module Degenz.Embeds
|
module Degenz.Embeds
|
||||||
|
|
||||||
open System
|
open System
|
||||||
|
open Degenz
|
||||||
open Degenz.Messaging
|
open Degenz.Messaging
|
||||||
open Degenz.Types
|
open Degenz.Types
|
||||||
open DSharpPlus.Entities
|
open DSharpPlus.Entities
|
||||||
@ -10,13 +11,13 @@ let shieldGif = "https://s10.gifyu.com/images/Defense-Degenz-V2-min.gif"
|
|||||||
|
|
||||||
let getItemIcon id =
|
let getItemIcon id =
|
||||||
match enum<ItemId>(id) with
|
match enum<ItemId>(id) with
|
||||||
| ItemId.Virus -> "https://s10.gifyu.com/images/Virus-icon.jpg"
|
| ItemId.Virus -> Some "https://s10.gifyu.com/images/Virus-icon.jpg"
|
||||||
| ItemId.RemoteAccess -> "https://s10.gifyu.com/images/Mind-Control-Degenz-V2-min.jpg"
|
| ItemId.RemoteAccess -> Some "https://s10.gifyu.com/images/Mind-Control-Degenz-V2-min.jpg"
|
||||||
| ItemId.Worm -> "https://s10.gifyu.com/images/WormBugAttack_Degenz-min.jpg"
|
| ItemId.Worm -> Some "https://s10.gifyu.com/images/WormBugAttack_Degenz-min.jpg"
|
||||||
| ItemId.Firewall -> "https://s10.gifyu.com/images/Defense-GIF-1-Degenz-1.jpg"
|
| ItemId.Firewall -> Some "https://s10.gifyu.com/images/Defense-GIF-1-Degenz-1.jpg"
|
||||||
| ItemId.Encryption -> "https://s10.gifyu.com/images/Encryption-Degenz-V2-1-min.jpg"
|
| ItemId.Encryption -> Some "https://s10.gifyu.com/images/Encryption-Degenz-V2-1-min.jpg"
|
||||||
| ItemId.Cypher -> "https://s10.gifyu.com/images/Cypher-Smaller.jpg"
|
| ItemId.Cypher -> Some "https://s10.gifyu.com/images/Cypher-Smaller.jpg"
|
||||||
| _ -> hackGif
|
| _ -> None
|
||||||
|
|
||||||
let getItemGif id =
|
let getItemGif id =
|
||||||
match enum<ItemId>(id) with
|
match enum<ItemId>(id) with
|
||||||
@ -61,8 +62,8 @@ let pickDefense actionId player isTrainer =
|
|||||||
|
|
||||||
for shield in Inventory.getShields player.Inventory do
|
for shield in Inventory.getShields player.Inventory do
|
||||||
let hours = TimeSpan.FromMinutes(int shield.Cooldown).TotalHours |> int
|
let hours = TimeSpan.FromMinutes(int shield.Cooldown).TotalHours |> int
|
||||||
let against = WeaponClass.getGoodAgainst(shield.Class) |> snd
|
let against = WeaponClass.getGoodAgainst shield.Class |> snd
|
||||||
embed.AddField(shield.Item.Name, $"Active {hours} hours\nDefeats {against}", true) |> ignore
|
embed.AddField(shield.Name, $"Active {hours} hours\nDefeats {against}", true) |> ignore
|
||||||
|
|
||||||
DiscordFollowupMessageBuilder()
|
DiscordFollowupMessageBuilder()
|
||||||
.AddComponents(buttons)
|
.AddComponents(buttons)
|
||||||
@ -85,7 +86,7 @@ let pickHack actionId attacker defender isTrainer =
|
|||||||
if not isTrainer then
|
if not isTrainer then
|
||||||
for hack in Inventory.getHacks attacker.Inventory do
|
for hack in Inventory.getHacks attacker.Inventory do
|
||||||
let amount = if hack.Power > int defender.Bank then int defender.Bank else hack.Power
|
let amount = if hack.Power > int defender.Bank then int defender.Bank else hack.Power
|
||||||
embed.AddField(hack.Item.Name, $"Cooldown {hack.Cooldown} mins\nExtract {amount} $GBT", true) |> ignore
|
embed.AddField(hack.Name, $"Cooldown {hack.Cooldown} mins\nExtract {amount} $GBT", true) |> ignore
|
||||||
|
|
||||||
DiscordFollowupMessageBuilder()
|
DiscordFollowupMessageBuilder()
|
||||||
.AddComponents(buttons)
|
.AddComponents(buttons)
|
||||||
@ -95,9 +96,9 @@ let pickHack actionId attacker defender isTrainer =
|
|||||||
let responseSuccessfulHack earnedMoney (targetId : uint64) amountTaken (hack : HackItem) =
|
let responseSuccessfulHack earnedMoney (targetId : uint64) amountTaken (hack : HackItem) =
|
||||||
let embed =
|
let embed =
|
||||||
DiscordEmbedBuilder()
|
DiscordEmbedBuilder()
|
||||||
.WithImageUrl(getItemGif hack.Item.Id)
|
.WithImageUrl(getItemGif hack.Id)
|
||||||
.WithTitle("Hack Attack")
|
.WithTitle("Hack Attack")
|
||||||
.WithDescription($"You successfully hacked <@{targetId}> using {hack.Item.Name}"
|
.WithDescription($"You successfully hacked <@{targetId}> using {hack.Name}"
|
||||||
+ (if earnedMoney then $", and took {amountTaken} 💰$GBT from them!" else "!"))
|
+ (if earnedMoney then $", and took {amountTaken} 💰$GBT from them!" else "!"))
|
||||||
|
|
||||||
DiscordFollowupMessageBuilder()
|
DiscordFollowupMessageBuilder()
|
||||||
@ -105,9 +106,9 @@ let responseSuccessfulHack earnedMoney (targetId : uint64) amountTaken (hack : H
|
|||||||
.AsEphemeral(true)
|
.AsEphemeral(true)
|
||||||
|
|
||||||
let responseCreatedShield (shield : ShieldItem) =
|
let responseCreatedShield (shield : ShieldItem) =
|
||||||
let embed = DiscordEmbedBuilder().WithImageUrl(getItemGif shield.Item.Id)
|
let embed = DiscordEmbedBuilder().WithImageUrl(getItemGif shield.Id)
|
||||||
embed.Title <- "Mounted Shield"
|
embed.Title <- "Mounted Shield"
|
||||||
embed.Description <- $"Mounted {shield.Item.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).TotalHours} hours"
|
embed.Description <- $"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).TotalHours} hours"
|
||||||
|
|
||||||
DiscordFollowupMessageBuilder()
|
DiscordFollowupMessageBuilder()
|
||||||
.AddEmbed(embed)
|
.AddEmbed(embed)
|
||||||
|
@ -4,64 +4,78 @@ open System
|
|||||||
open DSharpPlus
|
open DSharpPlus
|
||||||
open DSharpPlus.Entities
|
open DSharpPlus.Entities
|
||||||
open Degenz
|
open Degenz
|
||||||
open Newtonsoft.Json
|
|
||||||
|
|
||||||
module Armory =
|
|
||||||
let weapons : ItemDetails list =
|
|
||||||
let file = System.IO.File.ReadAllText("Items.json")
|
|
||||||
// let file = System.IO.File.ReadAllText("Bot/Items.json")
|
|
||||||
JsonConvert.DeserializeObject<ItemDetails array>(file)
|
|
||||||
|> Array.toList
|
|
||||||
|
|
||||||
module Inventory =
|
module Inventory =
|
||||||
let getItemsByType itemType inventory =
|
let getItemsByType itemType inventory =
|
||||||
match itemType with
|
match itemType with
|
||||||
| ItemType.Hack -> inventory |> List.filter (fun item -> match item with Hack _ -> true | _ -> false)
|
| ItemType.Hack -> inventory |> List.filter (fun item -> match item.Type with ItemType.Hack _ -> true | _ -> false)
|
||||||
| ItemType.Shield -> inventory |> List.filter (fun item -> match item with Shield _ -> true | _ -> false)
|
| ItemType.Shield -> inventory |> List.filter (fun item -> match item.Type with ItemType.Shield _ -> true | _ -> false)
|
||||||
| ItemType.Food -> inventory |> List.filter (fun item -> match item with Food _ -> true | _ -> false)
|
| ItemType.Food -> inventory |> List.filter (fun item -> match item.Type with ItemType.Food _ -> true | _ -> false)
|
||||||
| ItemType.Accessory -> inventory |> List.filter (fun item -> match item with Accessory _ -> true | _ -> false)
|
| ItemType.Accessory -> inventory |> List.filter (fun item -> match item.Type with ItemType.Accessory _ -> true | _ -> false)
|
||||||
|
| ItemType.Jpeg -> inventory |> List.filter (fun item -> match item.Type with ItemType.Jpeg _ -> true | _ -> false)
|
||||||
|
| ItemType.Misc -> inventory |> List.filter (fun item -> match item.Type with ItemType.Misc _ -> true | _ -> false)
|
||||||
|
|
||||||
let findItemById id (inventory : Inventory) = inventory |> List.find (fun item -> item.Id = id)
|
let findItemById id (inventory : Inventory) = inventory |> List.find (fun item -> item.Id = id)
|
||||||
|
|
||||||
let findHackById id inventory =
|
let getHackItem item =
|
||||||
inventory |> List.pick (fun item -> match item with | Hack h -> (if h.Item.Id = id then Some h else None) | _ -> None)
|
match item.Type , item.Attributes with
|
||||||
let findShieldById id inventory =
|
| ItemType.Hack , CanBuy buyPrice & CanSell _ & CanAttack power & CanExpire cooldown & CanClass ``class``->
|
||||||
inventory |> List.pick (fun item -> match item with | Shield s -> (if s.Item.Id = id then Some s else None) | _ -> None)
|
Some { Id = item.Id
|
||||||
let findFoodById id inventory =
|
Name = item.Name
|
||||||
inventory |> List.pick (fun item -> match item with | Food f -> (if f.Item.Id = id then Some f else None) | _ -> None)
|
Price = buyPrice
|
||||||
let findAccessoryById id inventory =
|
Cooldown = cooldown
|
||||||
inventory |> List.pick (fun item -> match item with | Accessory a -> (if a.Item.Id = id then Some a else None) | _ -> None)
|
Power = power
|
||||||
|
Class = ``class`` }
|
||||||
let getHacks inventory =
|
| _ -> None
|
||||||
inventory |> List.choose (fun item -> match item with | Hack h -> Some h | _ -> None)
|
|
||||||
let getShields inventory =
|
let getShieldItem item =
|
||||||
inventory |> List.choose (fun item -> match item with | Shield s -> Some s | _ -> None)
|
match item.Type , item.Attributes with
|
||||||
|
| ItemType.Shield , CanBuy buyPrice & CanSell _ & CanDefend resistance & CanExpire cooldown & CanClass ``class`` ->
|
||||||
|
Some { Id = item.Id
|
||||||
|
Name = item.Name
|
||||||
|
Price = buyPrice
|
||||||
|
Cooldown = cooldown
|
||||||
|
Resistance = resistance
|
||||||
|
Class = ``class`` }
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
|
// let findHackById id inventory =
|
||||||
|
// inventory |> List.pick (fun item -> match item with | Hack h -> (if h.Item.Id = id then Some h else None) | _ -> None)
|
||||||
|
// let findShieldById id inventory =
|
||||||
|
// inventory |> List.pick (fun item -> match item with | Shield s -> (if s.Item.Id = id then Some s else None) | _ -> None)
|
||||||
|
// let findFoodById id inventory =
|
||||||
|
// inventory |> List.pick (fun item -> match item with | Food f -> (if f.Item.Id = id then Some f else None) | _ -> None)
|
||||||
|
// let findAccessoryById id inventory =
|
||||||
|
// inventory |> List.pick (fun item -> match item with | Accessory a -> (if a.Item.Id = id then Some a else None) | _ -> None)
|
||||||
|
//
|
||||||
|
let getHacks inventory = inventory |> List.choose getHackItem
|
||||||
|
let getShields inventory = inventory |> List.choose getShieldItem
|
||||||
let getFoods inventory =
|
let getFoods inventory =
|
||||||
inventory |> List.choose (fun item -> match item with | Food f -> Some f | _ -> None)
|
inventory |> List.choose (fun item -> match item.Type with | ItemType.Food -> Some item | _ -> None)
|
||||||
let getAccessories inventory =
|
let getAccessories inventory =
|
||||||
inventory |> List.choose (fun item -> match item with | Accessory a -> Some a | _ -> None)
|
inventory |> List.choose (fun item -> match item.Type with | ItemType.Accessory -> Some item | _ -> None)
|
||||||
|
|
||||||
module WeaponClass =
|
module WeaponClass =
|
||||||
let SameTargetAttackCooldown = TimeSpan.FromHours(4)
|
let SameTargetAttackCooldown = TimeSpan.FromHours(4)
|
||||||
|
|
||||||
let getClassButtonColor item =
|
let getClassButtonColor item =
|
||||||
match ItemDetails.getClass item with
|
match item.Attributes with
|
||||||
| 0 -> ButtonStyle.Danger
|
| CanClass "0" -> ButtonStyle.Danger
|
||||||
| 1 -> ButtonStyle.Primary
|
| CanClass "1" -> ButtonStyle.Primary
|
||||||
| 2 -> ButtonStyle.Success
|
| CanClass "2" -> ButtonStyle.Success
|
||||||
| _ -> ButtonStyle.Primary
|
| _ -> ButtonStyle.Primary
|
||||||
|
|
||||||
let getClassEmbedColor item =
|
let getClassEmbedColor item =
|
||||||
match ItemDetails.getClass item with
|
match item.Attributes with
|
||||||
| 0 -> DiscordColor.Red
|
| CanClass "0" -> DiscordColor.Red
|
||||||
| 1 -> DiscordColor.Blurple
|
| CanClass "1" -> DiscordColor.Blurple
|
||||||
| 2 -> DiscordColor.Green
|
| CanClass "2" -> DiscordColor.Green
|
||||||
| _ -> DiscordColor.Blurple
|
| _ -> DiscordColor.Blurple
|
||||||
|
|
||||||
let getGoodAgainst = function
|
let getGoodAgainst = function
|
||||||
| 0 -> ( ItemId.Firewall , ItemId.Virus )
|
| "0" -> ( ItemId.Firewall , ItemId.Virus )
|
||||||
| 1 -> ( ItemId.Encryption , ItemId.RemoteAccess )
|
| "1" -> ( ItemId.Encryption , ItemId.RemoteAccess )
|
||||||
| _ -> ( ItemId.Cypher , ItemId.Worm )
|
| _ -> ( ItemId.Cypher , ItemId.Worm )
|
||||||
|
|
||||||
module Player =
|
module Player =
|
||||||
let getHackEvents player =
|
let getHackEvents player =
|
||||||
@ -82,32 +96,48 @@ module Player =
|
|||||||
let modifyBank (player : PlayerData) amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
|
let modifyBank (player : PlayerData) amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
|
||||||
|
|
||||||
module PlayerStats =
|
module PlayerStats =
|
||||||
// 4.17f would go from 100 to 0 in roughly 24 hours
|
// 2.09f would go from 100 to 0 in roughly 48 hours
|
||||||
let Strength = { Id = StatId.Strength ; BaseDecayRate = 4.17 ; BaseRange = Range.normalized }
|
let Strength = { Id = StatId.Strength ; BaseDecayRate = 2.09 ; BaseRange = Range.normalized }
|
||||||
let Focus = { Id = StatId.Focus ; BaseDecayRate = 4.17 ; BaseRange = Range.normalized }
|
let Focus = { Id = StatId.Focus ; BaseDecayRate = 2.09 ; BaseRange = Range.normalized }
|
||||||
let Luck = { Id = StatId.Luck ; BaseDecayRate = 4.17 ; BaseRange = Range.normalized }
|
let Luck = { Id = StatId.Luck ; BaseDecayRate = 2.09 ; BaseRange = Range.normalized }
|
||||||
let Charisma = { Id = StatId.Charisma ; BaseDecayRate = 4.17 ; BaseRange = Range.normalized }
|
let Charisma = { Id = StatId.Charisma ; BaseDecayRate = 2.09 ; BaseRange = Range.normalized }
|
||||||
|
|
||||||
let stats = [ Strength ; Focus ; Luck ; Charisma ]
|
let stats = [ Strength ; Focus ; Charisma ; Luck ]
|
||||||
|
|
||||||
|
let getPlayerStat (statConfig : StatConfig) player =
|
||||||
|
match statConfig.Id with
|
||||||
|
| StatId.Strength -> player.Stats.Strength
|
||||||
|
| StatId.Focus -> player.Stats.Focus
|
||||||
|
| StatId.Charisma -> player.Stats.Charisma
|
||||||
|
| StatId.Luck -> player.Stats.Luck
|
||||||
|
| _ -> player.Stats.Luck
|
||||||
|
|
||||||
let calculateActiveStat statId amount items =
|
let calculateActiveStat statId amount items =
|
||||||
let statConfig = stats |> List.find (fun s -> s.Id = statId)
|
let statConfig = stats |> List.find (fun s -> s.Id = statId)
|
||||||
// let hoursElapsed = (DateTime.UtcNow - lastRead).Hours
|
// let hoursElapsed = (DateTime.UtcNow - lastRead).Hours
|
||||||
// let totalDecay = float hoursElapsed * statConfig.BaseDecayRate
|
// let totalDecay = float hoursElapsed * statConfig.BaseDecayRate
|
||||||
let modMinMax =
|
let modMinMax =
|
||||||
let min = items |> List.sumBy (fun item -> match item with | Accessory a -> a.FloorBoost | _ -> 0)
|
let min =
|
||||||
let max = items |> List.sumBy (fun item -> match item with | Accessory a -> a.CeilBoost | _ -> 0)
|
items
|
||||||
|
|> List.choose (fun i -> match i.Attributes with CanModify fx -> Some fx | _ -> None)
|
||||||
|
|> List.concat
|
||||||
|
|> List.sumBy (fun fx -> match fx.Effect with | Min x -> x | _ -> 0)
|
||||||
|
let max =
|
||||||
|
items
|
||||||
|
|> List.choose (fun i -> match i.Attributes with CanModify fx -> Some fx | _ -> None)
|
||||||
|
|> List.concat
|
||||||
|
|> List.sumBy (fun fx -> match fx.Effect with | Max x -> x | _ -> 0)
|
||||||
Range.create (statConfig.BaseRange.Min + min) (statConfig.BaseRange.Max + max)
|
Range.create (statConfig.BaseRange.Min + min) (statConfig.BaseRange.Max + max)
|
||||||
let amountAfterDecay = modMinMax |> Range.constrain amount
|
let amountAfterDecay = modMinMax |> Range.constrain amount
|
||||||
{ Id = statId ; Amount = amountAfterDecay ; ModRange = modMinMax ; LastRead = DateTime.UtcNow }
|
{ Id = statId ; Amount = amountAfterDecay ; ModRange = modMinMax ; LastRead = DateTime.UtcNow }
|
||||||
|
|
||||||
module Arsenal =
|
module Arsenal =
|
||||||
let battleItemFormat (items : ItemDetails list) =
|
let battleItemFormat (items : Inventory) =
|
||||||
match items with
|
match items with
|
||||||
| [] -> "None"
|
| [] -> "None"
|
||||||
| _ -> items |> List.map (fun item -> item.Name) |> String.concat ", "
|
| _ -> items |> List.map (fun item -> item.Name) |> String.concat ", "
|
||||||
|
|
||||||
let actionFormat (actions : PlayerEvent List) =
|
let actionFormat items (actions : PlayerEvent List) =
|
||||||
match actions with
|
match actions with
|
||||||
| [] -> "None"
|
| [] -> "None"
|
||||||
| acts ->
|
| acts ->
|
||||||
@ -115,13 +145,13 @@ module Arsenal =
|
|||||||
|> List.map (fun event ->
|
|> List.map (fun event ->
|
||||||
match event.Type with
|
match event.Type with
|
||||||
| Hacking h ->
|
| Hacking h ->
|
||||||
let item = Armory.weapons |> Inventory.findHackById h.HackId
|
let item = items |> Inventory.findItemById h.HackId
|
||||||
let cooldown = Messaging.getTimeText false WeaponClass.SameTargetAttackCooldown event.Timestamp
|
let cooldown = Messaging.getTimeText false WeaponClass.SameTargetAttackCooldown event.Timestamp
|
||||||
$"Hacked {h.Adversary.Name} with {item.Item.Name} {cooldown} ago"
|
$"Hacked {h.Adversary.Name} with {item.Name} {cooldown} ago"
|
||||||
| Shielding id ->
|
| Shielding id ->
|
||||||
let item = Armory.weapons |> Inventory.findShieldById id
|
let item = items |> Inventory.findItemById id
|
||||||
let cooldown = Messaging.getTimeText true (TimeSpan.FromMinutes(int event.Cooldown)) event.Timestamp
|
let cooldown = Messaging.getTimeText true (TimeSpan.FromMinutes(int event.Cooldown)) event.Timestamp
|
||||||
$"{item.Item.Name} Shield active for {cooldown}"
|
$"{item.Name} Shield active for {cooldown}"
|
||||||
| _ -> "")
|
| _ -> "")
|
||||||
|> List.filter (String.IsNullOrWhiteSpace >> not)
|
|> List.filter (String.IsNullOrWhiteSpace >> not)
|
||||||
|> String.concat "\n"
|
|> String.concat "\n"
|
||||||
@ -130,5 +160,5 @@ module Arsenal =
|
|||||||
let hacks = Player.getHackEvents p
|
let hacks = Player.getHackEvents p
|
||||||
$"**Hacks:** {Inventory.getItemsByType ItemType.Hack p.Inventory |> battleItemFormat}\n
|
$"**Hacks:** {Inventory.getItemsByType ItemType.Hack p.Inventory |> battleItemFormat}\n
|
||||||
**Shields:** {Inventory.getItemsByType ItemType.Shield p.Inventory |> battleItemFormat}\n
|
**Shields:** {Inventory.getItemsByType ItemType.Shield p.Inventory |> battleItemFormat}\n
|
||||||
**Hack Attacks:**\n{hacks |> List.take (min hacks.Length 10) |> actionFormat}\n
|
**Hack Attacks:**\n{hacks |> List.take (min hacks.Length 10) |> actionFormat p.Inventory}\n
|
||||||
**Active Shields:**\n{Player.getShieldEvents p |> actionFormat}"
|
**Active Shields:**\n{Player.getShieldEvents p |> actionFormat p.Inventory}"
|
||||||
|
127
Bot/GameTypes.fs
127
Bot/GameTypes.fs
@ -2,7 +2,6 @@
|
|||||||
module Degenz.Types
|
module Degenz.Types
|
||||||
|
|
||||||
open System
|
open System
|
||||||
open Degenz
|
|
||||||
|
|
||||||
[<Measure>]
|
[<Measure>]
|
||||||
type mins
|
type mins
|
||||||
@ -89,84 +88,83 @@ type ItemType =
|
|||||||
| Shield
|
| Shield
|
||||||
| Food
|
| Food
|
||||||
| Accessory
|
| Accessory
|
||||||
|
| Jpeg
|
||||||
type ItemAttributes = {
|
| Misc
|
||||||
CanBuy : bool
|
|
||||||
CanSell : bool
|
type Effect =
|
||||||
CanConsume : bool
|
| Min of int
|
||||||
CanTrade : bool
|
| Max of int
|
||||||
CanDrop : bool
|
| Add of int
|
||||||
|
| RateMultiplier of float
|
||||||
|
|
||||||
|
type StatEffect = {
|
||||||
|
TargetStat : StatId
|
||||||
|
Effect : Effect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ItemAttribute =
|
||||||
|
| Buyable of price : int<GBT>
|
||||||
|
| Sellable of price : int<GBT>
|
||||||
|
| RateLimitable of cooldown : int<mins>
|
||||||
|
| Expireable of lifetime : int<mins>
|
||||||
|
| Consumable
|
||||||
|
| Modifiable of effects : StatEffect list
|
||||||
|
| Droppable of chance : float
|
||||||
|
| Tradeable
|
||||||
|
| Attackable of power : int
|
||||||
|
| Defendable of resistance : int
|
||||||
|
| Classable of className : string
|
||||||
|
| Stackable of max : int
|
||||||
|
|
||||||
|
let (|CanBuy|_|) itemAttrs = itemAttrs |> List.tryPick (function Buyable p -> Some p | _ -> None)
|
||||||
|
let (|CanSell|_|) itemAttrs = itemAttrs |> List.tryPick (function Sellable p -> Some p | _ -> None)
|
||||||
|
let (|CanExpire|_|) itemAttrs = itemAttrs |> List.tryPick (function Expireable l -> Some l | _ -> None)
|
||||||
|
let (|CanRateLimit|_|) itemAttrs = itemAttrs |> List.tryPick (function RateLimitable l -> Some l | _ -> None)
|
||||||
|
let (|CanConsume|_|) itemAttrs = itemAttrs |> List.tryPick (function Consumable -> Some () | _ -> None)
|
||||||
|
let (|CanModify|_|) itemAttrs = itemAttrs |> List.tryPick (function Modifiable es -> Some es | _ -> None)
|
||||||
|
let (|CanDrop|_|) itemAttrs = itemAttrs |> List.tryPick (function Droppable c -> Some c | _ -> None)
|
||||||
|
let (|CanTrade|_|) itemAttrs = itemAttrs |> List.tryPick (function Tradeable -> Some () | _ -> None)
|
||||||
|
let (|CanAttack|_|) itemAttrs = itemAttrs |> List.tryPick (function Attackable p -> Some p | _ -> None)
|
||||||
|
let (|CanDefend|_|) itemAttrs = itemAttrs |> List.tryPick (function Defendable r -> Some r | _ -> None)
|
||||||
|
let (|CanClass|_|) itemAttrs = itemAttrs |> List.tryPick (function Classable c -> Some c | _ -> None)
|
||||||
|
let (|CanStack|_|) itemAttrs = itemAttrs |> List.tryPick (function Stackable m -> Some m | _ -> None)
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
Id : int
|
Id : int
|
||||||
Name : string
|
Name : string
|
||||||
Price : int<GBT>
|
Description : string
|
||||||
Attributes : ItemAttributes
|
Type : ItemType
|
||||||
|
Symbol : string
|
||||||
|
IconUrl : string
|
||||||
|
Attributes : ItemAttribute list
|
||||||
|
}
|
||||||
|
|
||||||
|
type StoreItem = {
|
||||||
|
Stock : int
|
||||||
|
LimitStock : bool
|
||||||
|
Available : bool
|
||||||
|
Item : Item
|
||||||
}
|
}
|
||||||
|
|
||||||
type HackItem = {
|
type HackItem = {
|
||||||
Power : int
|
Id : int
|
||||||
Class : int
|
Name : string
|
||||||
|
Price : int<GBT>
|
||||||
Cooldown : int<mins>
|
Cooldown : int<mins>
|
||||||
Item : Item
|
Power : int
|
||||||
|
Class : string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShieldItem = {
|
type ShieldItem = {
|
||||||
Class : int
|
Id : int
|
||||||
|
Name : string
|
||||||
|
Price : int<GBT>
|
||||||
Cooldown : int<mins>
|
Cooldown : int<mins>
|
||||||
Item : Item
|
Resistance : int
|
||||||
|
Class : string
|
||||||
}
|
}
|
||||||
|
|
||||||
type FoodItem = {
|
type Inventory = Item list
|
||||||
TargetStat : StatId
|
|
||||||
BoostAmount : int
|
|
||||||
Item : Item
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccessoryItem = {
|
|
||||||
TargetStat : StatId
|
|
||||||
FloorBoost : int
|
|
||||||
CeilBoost : int
|
|
||||||
Item : Item
|
|
||||||
}
|
|
||||||
|
|
||||||
type ItemDetails =
|
|
||||||
| Hack of HackItem
|
|
||||||
| Shield of ShieldItem
|
|
||||||
| Food of FoodItem
|
|
||||||
| Accessory of AccessoryItem
|
|
||||||
member this.Id =
|
|
||||||
match this with
|
|
||||||
| Hack i -> i.Item.Id
|
|
||||||
| Shield i -> i.Item.Id
|
|
||||||
| Food i -> i.Item.Id
|
|
||||||
| Accessory i -> i.Item.Id
|
|
||||||
member this.Name =
|
|
||||||
match this with
|
|
||||||
| Hack i -> i.Item.Name
|
|
||||||
| Shield i -> i.Item.Name
|
|
||||||
| Food i -> i.Item.Name
|
|
||||||
| Accessory i -> i.Item.Name
|
|
||||||
member this.Price =
|
|
||||||
match this with
|
|
||||||
| Hack i -> i.Item.Price
|
|
||||||
| Shield i -> i.Item.Price
|
|
||||||
| Food i -> i.Item.Price
|
|
||||||
| Accessory i -> i.Item.Price
|
|
||||||
member this.getItem =
|
|
||||||
match this with
|
|
||||||
| Hack i -> i.Item
|
|
||||||
| Shield i -> i.Item
|
|
||||||
| Food i -> i.Item
|
|
||||||
| Accessory i -> i.Item
|
|
||||||
static member getClass = function
|
|
||||||
| Hack i -> i.Class
|
|
||||||
| Shield i -> i.Class
|
|
||||||
| Food _ -> -1
|
|
||||||
| Accessory _ -> -1
|
|
||||||
|
|
||||||
type Inventory = ItemDetails list
|
|
||||||
|
|
||||||
type PlayerData = {
|
type PlayerData = {
|
||||||
DiscordId : uint64
|
DiscordId : uint64
|
||||||
@ -190,3 +188,4 @@ with member this.toDiscordPlayer = { Id = this.DiscordId ; Name = this.Name }
|
|||||||
// XP = 0
|
// XP = 0
|
||||||
Bank = 0<GBT>
|
Bank = 0<GBT>
|
||||||
Active = false }
|
Active = false }
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ let checkHasEmptyHacks (attacker : PlayerData) =
|
|||||||
let checkPlayerOwnsWeapon (item : Item) player =
|
let checkPlayerOwnsWeapon (item : Item) player =
|
||||||
match player.Inventory |> List.exists (fun i -> i.Id = item.Id) with
|
match player.Inventory |> List.exists (fun i -> i.Id = item.Id) with
|
||||||
| true -> Ok player
|
| true -> Ok player
|
||||||
| false -> Error $"You sold your weapon already, you cheeky bastard..."
|
| false -> Error $"You sold your {item.Name} already, you cheeky bastard..."
|
||||||
|
|
||||||
let checkPlayerHasShieldSlotsAvailable player =
|
let checkPlayerHasShieldSlotsAvailable player =
|
||||||
let updatedPlayer = player |> Player.removeExpiredActions
|
let updatedPlayer = player |> Player.removeExpiredActions
|
||||||
@ -82,7 +82,11 @@ let runHackerBattle defender (hack : HackItem) =
|
|||||||
|> fun p -> p.Events
|
|> fun p -> p.Events
|
||||||
|> List.exists (fun event ->
|
|> List.exists (fun event ->
|
||||||
match event.Type with
|
match event.Type with
|
||||||
| Shielding id -> hack.Class = (Inventory.findShieldById id Armory.weapons).Class
|
| Shielding id ->
|
||||||
|
let item = Inventory.findItemById id Trainer.weapons
|
||||||
|
match item.Attributes with
|
||||||
|
| CanClass c -> hack.Class = c
|
||||||
|
| _ -> false
|
||||||
| _ -> false)
|
| _ -> false)
|
||||||
|
|
||||||
let updateCombatants successfulHack (attacker : PlayerData) (defender : PlayerData) (hack : HackItem) prize =
|
let updateCombatants successfulHack (attacker : PlayerData) (defender : PlayerData) (hack : HackItem) prize =
|
||||||
@ -90,7 +94,7 @@ let updateCombatants successfulHack (attacker : PlayerData) (defender : PlayerDa
|
|||||||
{ p with Events = attack::p.Events ; Bank = max (p.Bank + amount) 0<GBT> }
|
{ p with Events = attack::p.Events ; Bank = max (p.Bank + amount) 0<GBT> }
|
||||||
let event isDefenderEvent =
|
let event isDefenderEvent =
|
||||||
let hackEvent = {
|
let hackEvent = {
|
||||||
HackId = hack.Item.Id
|
HackId = hack.Id
|
||||||
Adversary = if isDefenderEvent then attacker.toDiscordPlayer else defender.toDiscordPlayer
|
Adversary = if isDefenderEvent then attacker.toDiscordPlayer else defender.toDiscordPlayer
|
||||||
IsInstigator = not isDefenderEvent
|
IsInstigator = not isDefenderEvent
|
||||||
Success = successfulHack
|
Success = successfulHack
|
||||||
@ -161,25 +165,27 @@ let handleAttack (ctx : IDiscordContext) =
|
|||||||
executePlayerAction ctx (fun attacker -> async {
|
executePlayerAction ctx (fun attacker -> async {
|
||||||
let tokens = ctx.GetInteractionId().Split("-")
|
let tokens = ctx.GetInteractionId().Split("-")
|
||||||
let hackId = int tokens.[1]
|
let hackId = int tokens.[1]
|
||||||
let hack = Armory.weapons |> Inventory.findHackById hackId
|
// TODO: This sucks
|
||||||
|
let item = Trainer.weapons |> Inventory.findItemById hackId
|
||||||
|
let hackItem = (Inventory.getHackItem item).Value
|
||||||
let resultId , targetId = UInt64.TryParse tokens.[2]
|
let resultId , targetId = UInt64.TryParse tokens.[2]
|
||||||
let! resultTarget = DbService.tryFindPlayer targetId
|
let! resultTarget = DbService.tryFindPlayer targetId
|
||||||
|
|
||||||
match resultTarget , true , resultId with
|
match resultTarget , resultId with
|
||||||
| Some defender , true , true ->
|
| Some defender , true ->
|
||||||
do! attacker
|
do! attacker
|
||||||
|> Player.removeExpiredActions
|
|> Player.removeExpiredActions
|
||||||
|> checkAlreadyHackedTarget defender
|
|> checkAlreadyHackedTarget defender
|
||||||
>>= checkPlayerOwnsWeapon hack.Item
|
>>= checkPlayerOwnsWeapon item
|
||||||
>>= checkTargetHasFunds defender
|
>>= checkTargetHasFunds defender
|
||||||
>>= checkWeaponHasCooldown hack.Item
|
>>= checkWeaponHasCooldown item
|
||||||
|> function
|
|> function
|
||||||
| Ok atkr -> async {
|
| Ok attacker -> async {
|
||||||
let result = runHackerBattle defender hack
|
let result = runHackerBattle defender hackItem
|
||||||
match result with
|
match result with
|
||||||
| false -> do! successfulHack ctx atkr defender hack
|
| false -> do! successfulHack ctx attacker defender hackItem
|
||||||
| true -> do! failedHack ctx attacker defender hack
|
| true -> do! failedHack ctx attacker defender hackItem
|
||||||
do! Analytics.hackedTarget (ctx.GetDiscordMember()) hack.Item.Name (not result)
|
do! Analytics.hackedTarget (ctx.GetDiscordMember()) hackItem.Name (not result)
|
||||||
}
|
}
|
||||||
| Error msg -> Messaging.sendFollowUpMessage ctx msg
|
| Error msg -> Messaging.sendFollowUpMessage ctx msg
|
||||||
| _ -> do! Messaging.sendFollowUpMessage ctx "Error occurred processing attack"
|
| _ -> do! Messaging.sendFollowUpMessage ctx "Error occurred processing attack"
|
||||||
@ -201,18 +207,19 @@ let handleDefense (ctx : IDiscordContext) =
|
|||||||
executePlayerAction ctx (fun player -> async {
|
executePlayerAction ctx (fun player -> async {
|
||||||
let tokens = ctx.GetInteractionId().Split("-")
|
let tokens = ctx.GetInteractionId().Split("-")
|
||||||
let shieldId = int tokens.[1]
|
let shieldId = int tokens.[1]
|
||||||
let shield = Armory.weapons |> Inventory.findShieldById shieldId
|
let item = Trainer.weapons |> Inventory.findItemById shieldId
|
||||||
|
let shieldItem = (Inventory.getShieldItem item).Value
|
||||||
|
|
||||||
do! player
|
do! player
|
||||||
|> checkPlayerOwnsWeapon shield.Item
|
|> checkPlayerOwnsWeapon item
|
||||||
>>= checkPlayerHasShieldSlotsAvailable
|
>>= checkPlayerHasShieldSlotsAvailable
|
||||||
>>= checkWeaponHasCooldown shield.Item
|
>>= checkWeaponHasCooldown item
|
||||||
|> handleResultWithResponse ctx (fun p -> async {
|
|> handleResultWithResponse ctx (fun p -> async {
|
||||||
let embed = Embeds.responseCreatedShield shield
|
let embed = Embeds.responseCreatedShield shieldItem
|
||||||
do! ctx.FollowUp embed |> Async.AwaitTask
|
do! ctx.FollowUp embed |> Async.AwaitTask
|
||||||
let defense = {
|
let defense = {
|
||||||
Type = Shielding shieldId
|
Type = Shielding shieldId
|
||||||
Cooldown = shield.Cooldown
|
Cooldown = shieldItem.Cooldown
|
||||||
Timestamp = DateTime.UtcNow
|
Timestamp = DateTime.UtcNow
|
||||||
}
|
}
|
||||||
do! DbService.updatePlayer p |> Async.Ignore
|
do! DbService.updatePlayer p |> Async.Ignore
|
||||||
@ -223,7 +230,7 @@ let handleDefense (ctx : IDiscordContext) =
|
|||||||
do! channel.SendMessageAsync(builder)
|
do! channel.SendMessageAsync(builder)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|> Async.Ignore
|
|> Async.Ignore
|
||||||
do! Analytics.shieldActivated (ctx.GetDiscordMember()) shield.Item.Name
|
do! Analytics.shieldActivated (ctx.GetDiscordMember()) shieldItem.Name
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -103,30 +103,6 @@ let reel1 = getReel (fun s -> s.reel1Count)
|
|||||||
let reel2 = getReel (fun s -> s.reel2Count)
|
let reel2 = getReel (fun s -> s.reel2Count)
|
||||||
let reel3 = getReel (fun s -> s.reel3Count)
|
let reel3 = getReel (fun s -> s.reel3Count)
|
||||||
|
|
||||||
let slots =
|
|
||||||
[| "https://s7.gifyu.com/images/aneye.png"
|
|
||||||
"https://s7.gifyu.com/images/anonmask.png"
|
|
||||||
"https://s7.gifyu.com/images/circuitboard.png"
|
|
||||||
"https://s7.gifyu.com/images/obey.png"
|
|
||||||
"https://s7.gifyu.com/images/oldtv.png"
|
|
||||||
"https://s7.gifyu.com/images/pills.png"
|
|
||||||
"https://s7.gifyu.com/images/pizza0d47578733961746.png"
|
|
||||||
"https://s7.gifyu.com/images/ramen0515f00869e1f4eb.png"
|
|
||||||
"https://s7.gifyu.com/images/rat69609f842a0eb9f5.png"
|
|
||||||
"https://s7.gifyu.com/images/alcohol.png"
|
|
||||||
"https://s7.gifyu.com/images/bigbrother.png"
|
|
||||||
"https://s7.gifyu.com/images/sushi.png" |]
|
|
||||||
// [| "https://s7.gifyu.com/images/A-bottle-of-pills0a3006d0170e08df.png"
|
|
||||||
// "https://s7.gifyu.com/images/an-eyec362d8152ae2382b.png"
|
|
||||||
// "https://s7.gifyu.com/images/anon-face-mask6c7624821c89fc08.png"
|
|
||||||
// "https://s7.gifyu.com/images/a-piece-of-sushi77071d30f60a89c6.png"
|
|
||||||
// "https://s7.gifyu.com/images/Circuit-board89056017b80f1d13.png"
|
|
||||||
// "https://s7.gifyu.com/images/OBEYf2a8234109836c03.png"
|
|
||||||
// "https://s7.gifyu.com/images/old-tv-screendc6bc9d4b6c1fd65.png"
|
|
||||||
// "https://s7.gifyu.com/images/pizza030ffc00ff50da0e.png"
|
|
||||||
// "https://s7.gifyu.com/images/ramen08336d448018c98f.png"
|
|
||||||
// "https://s7.gifyu.com/images/rat14f65f54f0d75036.png" |]
|
|
||||||
|
|
||||||
let slotEmojiNames =
|
let slotEmojiNames =
|
||||||
[| "sushi"
|
[| "sushi"
|
||||||
"bigbrother"
|
"bigbrother"
|
||||||
@ -152,7 +128,10 @@ let mutable anyEmoji : DiscordEmoji option = None
|
|||||||
let getJackpotAmount () =
|
let getJackpotAmount () =
|
||||||
GuildEnvironment.connectionString
|
GuildEnvironment.connectionString
|
||||||
|> Sql.connect
|
|> Sql.connect
|
||||||
|> Sql.query "SELECT stock FROM item WHERE symbol = 'JACKPOT'"
|
|> Sql.query """
|
||||||
|
SELECT stock FROM store_item
|
||||||
|
WHERE store_item.item_id = (SELECT id FROM item WHERE symbol = 'JACKPOT')
|
||||||
|
"""
|
||||||
|> Sql.executeRowAsync (fun read -> (read.int "stock") * 1<GBT>)
|
|> Sql.executeRowAsync (fun read -> (read.int "stock") * 1<GBT>)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
@ -160,7 +139,10 @@ let incrementJackpot amount =
|
|||||||
GuildEnvironment.connectionString
|
GuildEnvironment.connectionString
|
||||||
|> Sql.connect
|
|> Sql.connect
|
||||||
|> Sql.parameters [ ( "amount" , Sql.int (int amount) ) ]
|
|> Sql.parameters [ ( "amount" , Sql.int (int amount) ) ]
|
||||||
|> Sql.query "UPDATE item SET stock = stock + @amount WHERE symbol = 'JACKPOT'"
|
|> Sql.query """
|
||||||
|
UPDATE store_item SET stock = stock + @amount
|
||||||
|
WHERE store_item.item_id = (SELECT id FROM item WHERE symbol = 'JACKPOT')
|
||||||
|
"""
|
||||||
|> Sql.executeNonQueryAsync
|
|> Sql.executeNonQueryAsync
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
@ -168,10 +150,43 @@ let resetJackpot amount =
|
|||||||
GuildEnvironment.connectionString
|
GuildEnvironment.connectionString
|
||||||
|> Sql.connect
|
|> Sql.connect
|
||||||
|> Sql.parameters [ ( "amount" , Sql.int (int amount) ) ]
|
|> Sql.parameters [ ( "amount" , Sql.int (int amount) ) ]
|
||||||
|> Sql.query "UPDATE item SET stock = @amount WHERE symbol = 'JACKPOT'"
|
|> Sql.query """
|
||||||
|
UPDATE store_item SET stock = @amount
|
||||||
|
WHERE store_item.item_id = (SELECT id FROM item WHERE symbol = 'JACKPOT')
|
||||||
|
"""
|
||||||
|> Sql.executeNonQueryAsync
|
|> Sql.executeNonQueryAsync
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let getLastPlayedSlotFromPlayer (did : uint64) = async {
|
||||||
|
let! events =
|
||||||
|
GuildEnvironment.connectionString
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "did", Sql.string (string did) ]
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT player_event.updated_at FROM player_event
|
||||||
|
JOIN "user" u on u.id = player_event.user_id
|
||||||
|
WHERE u.discord_id = @did AND event_type = 'PlayingSlot'
|
||||||
|
"""
|
||||||
|
|> Sql.executeAsync (fun read -> read.dateTime "updated_at" )
|
||||||
|
|> Async.AwaitTask
|
||||||
|
match events with
|
||||||
|
| [] -> return None
|
||||||
|
| es -> return Some (List.head es)
|
||||||
|
}
|
||||||
|
|
||||||
|
let updateSlotPlayedFromPlayer (did : uint64) =
|
||||||
|
GuildEnvironment.connectionString
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "did", Sql.string (string did) ]
|
||||||
|
|> Sql.query """
|
||||||
|
WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did)
|
||||||
|
UPDATE player_event SET updated_at = now() at time zone 'utc'
|
||||||
|
FROM usr WHERE usr.id = user_id AND player_event.event_type = 'PlayingSlot';
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|> Async.Ignore
|
||||||
|
|
||||||
let handlePrizeTable (ctx : IDiscordContext) =
|
let handlePrizeTable (ctx : IDiscordContext) =
|
||||||
task {
|
task {
|
||||||
do! Messaging.defer ctx
|
do! Messaging.defer ctx
|
||||||
@ -321,10 +336,10 @@ let spin multiplier (ctx : IDiscordContext) =
|
|||||||
}
|
}
|
||||||
PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
||||||
if player.Bank >= playAmount then
|
if player.Bank >= playAmount then
|
||||||
match! DbService.getLastPlayedSlotFromPlayer player.DiscordId with
|
match! getLastPlayedSlotFromPlayer player.DiscordId with
|
||||||
| Some timestamp ->
|
| Some timestamp ->
|
||||||
if DateTime.UtcNow - timestamp > TimeSpan.FromSeconds(8) then
|
if DateTime.UtcNow - timestamp > TimeSpan.FromSeconds(8) then
|
||||||
do! DbService.updateSlotPlayedFromPlayer player.DiscordId
|
do! updateSlotPlayedFromPlayer player.DiscordId
|
||||||
do! execute player
|
do! execute player
|
||||||
else
|
else
|
||||||
do! Messaging.sendFollowUpMessage ctx "Wait till you finish the current spin!"
|
do! Messaging.sendFollowUpMessage ctx "Wait till you finish the current spin!"
|
||||||
|
@ -10,97 +10,128 @@ open Degenz
|
|||||||
open Degenz.Messaging
|
open Degenz.Messaging
|
||||||
open Degenz.PlayerInteractions
|
open Degenz.PlayerInteractions
|
||||||
|
|
||||||
let getBuyItemsEmbed (playerInventory : Inventory) (storeInventory : Inventory) =
|
let getItemEmbeds owned (items : StoreItem list) =
|
||||||
let embeds , buttons =
|
items
|
||||||
|
|> List.countBy (fun item -> item.Item.Id)
|
||||||
|
|> List.map (fun (id,count) -> items |> List.find (fun i -> i.Item.Id = id) , count )
|
||||||
|
|> List.map (fun (item,count) ->
|
||||||
|
let embed = DiscordEmbedBuilder()
|
||||||
|
if not owned && item.LimitStock then
|
||||||
|
embed.AddField("Stock", $"{item.Stock}", true) |> ignore
|
||||||
|
item.Item.Attributes
|
||||||
|
|> List.iter (function
|
||||||
|
| Buyable price -> embed.AddField("Price 💰", (if price = 0<GBT> then "Free" else $"{price} $GBT"), true) |> ignore
|
||||||
|
| Attackable power ->
|
||||||
|
let title = match item.Item.Type with ItemType.Hack -> "$GBT Reward" | _ -> "Power"
|
||||||
|
embed.AddField($"{title} |", string power, true) |> ignore
|
||||||
|
| RateLimitable time ->
|
||||||
|
let title = match item.Item.Type with ItemType.Hack -> "Cooldown" | ItemType.Shield -> "Active For" | _ -> "Expires"
|
||||||
|
let ts = TimeSpan.FromMinutes(int time)
|
||||||
|
let timeStr = if ts.Hours = 0 then $"{ts.Minutes} mins" else $"{ts.Hours} hours"
|
||||||
|
embed.AddField($"{title} |", timeStr, true) |> ignore
|
||||||
|
| Stackable max ->
|
||||||
|
if owned then
|
||||||
|
embed.AddField($"Total Owned |", $"{count}", true) |> ignore
|
||||||
|
else
|
||||||
|
embed.AddField($"Max Allowed |", $"{max}", true) |> ignore
|
||||||
|
| Modifiable effects ->
|
||||||
|
let fx =
|
||||||
|
effects
|
||||||
|
|> List.map (fun f ->
|
||||||
|
match f.Effect with
|
||||||
|
| Min i -> $"{f.TargetStat} Min + {i}"
|
||||||
|
| Max i -> $"{f.TargetStat} Max + {i}"
|
||||||
|
| Add i ->
|
||||||
|
let str = if i > 0 then "Boost" else "Penalty"
|
||||||
|
$"{f.TargetStat} {str} + {i}"
|
||||||
|
| RateMultiplier i -> $"{f.TargetStat} Multiplier - i")
|
||||||
|
|> String.concat "\n"
|
||||||
|
embed.AddField($"Effect - Amount ", $"{fx}", true) |> ignore
|
||||||
|
| _ -> ())
|
||||||
|
embed
|
||||||
|
.WithColor(WeaponClass.getClassEmbedColor item.Item)
|
||||||
|
.WithTitle($"{item.Item.Name}")
|
||||||
|
|> ignore
|
||||||
|
match Embeds.getItemIcon item.Item.Id with
|
||||||
|
| Some url -> embed.WithThumbnail(url)
|
||||||
|
| None -> if String.IsNullOrWhiteSpace(item.Item.IconUrl) then embed else embed.WithThumbnail(item.Item.IconUrl))
|
||||||
|
|> List.map (fun e -> e.Build())
|
||||||
|
|> Seq.ofList
|
||||||
|
|
||||||
|
let getBuyItemsEmbed (playerInventory : Inventory) (storeInventory : StoreItem list) =
|
||||||
|
let embeds = getItemEmbeds false storeInventory
|
||||||
|
let buttons =
|
||||||
storeInventory
|
storeInventory
|
||||||
|> List.map (fun item ->
|
|> List.map (fun item ->
|
||||||
let embed = DiscordEmbedBuilder()
|
let owned = playerInventory |> List.exists (fun i -> i.Id = item.Item.Id)
|
||||||
match item with
|
let inStock = item.Available && (item.Stock > 0 || item.LimitStock = false)
|
||||||
| Hack hack ->
|
match owned , inStock with
|
||||||
embed.AddField($"$GBT Reward |", string hack.Power, true)
|
| false , true -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}", $"Buy {item.Item.Name}")
|
||||||
.AddField("Cooldown |", $"{TimeSpan.FromMinutes(int hack.Cooldown).Minutes} minutes", true)
|
| false , false -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}", $"{item.Item.Name} (Out of Stock)", true)
|
||||||
.WithThumbnail(Embeds.getItemIcon item.Id)
|
| true , _ -> DiscordButtonComponent(WeaponClass.getClassButtonColor item.Item, $"Buy-{item.Item.Id}", $"Own {item.Item.Name}", true)
|
||||||
|> ignore
|
:> DiscordComponent)
|
||||||
| Shield shield ->
|
|
||||||
embed.AddField($"Strong against |", WeaponClass.getGoodAgainst shield.Class |> snd |> string, true)
|
|
||||||
// .AddField($"Defensive Strength |", string item.Power, true)
|
|
||||||
.AddField("Active For |", $"{TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours", true)
|
|
||||||
.WithThumbnail(Embeds.getItemIcon item.Id)
|
|
||||||
|> ignore
|
|
||||||
| Food food ->
|
|
||||||
embed.AddField($"Stat |", $"{food.TargetStat}", true)
|
|
||||||
.AddField($"Amount |", $"+{food.BoostAmount}", true) |> ignore
|
|
||||||
| Accessory accessory ->
|
|
||||||
embed.AddField($"Stat |", $"{accessory.TargetStat}", true) |> ignore
|
|
||||||
if accessory.FloorBoost > 0 then
|
|
||||||
embed.AddField($"Min Boost |", $"+{accessory.FloorBoost}", true) |> ignore
|
|
||||||
if accessory.CeilBoost > 0 then
|
|
||||||
embed.AddField($"Max Boost |", $"+{accessory.CeilBoost}", true) |> ignore
|
|
||||||
embed
|
|
||||||
.AddField("Price 💰", (if item.Price = 0<GBT> then "Free" else $"{item.Price} $GBT"), true)
|
|
||||||
.WithColor(WeaponClass.getClassEmbedColor item)
|
|
||||||
.WithTitle($"{item.Name}")
|
|
||||||
|> ignore
|
|
||||||
let button =
|
|
||||||
if playerInventory |> List.exists (fun i -> i.Id = item.Id)
|
|
||||||
then DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Buy-{item.Id}", $"Own {item.Name}", true)
|
|
||||||
else DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Buy-{item.Id}", $"Buy {item.Name}")
|
|
||||||
( embed.Build() , button :> DiscordComponent ))
|
|
||||||
|> List.unzip
|
|
||||||
|
|
||||||
DiscordFollowupMessageBuilder()
|
let builder =
|
||||||
.AddEmbeds(embeds)
|
DiscordFollowupMessageBuilder()
|
||||||
.AddComponents(buttons)
|
.AddEmbeds(embeds)
|
||||||
.AsEphemeral(true)
|
.AsEphemeral(true)
|
||||||
|
buttons
|
||||||
|
|> List.chunkBySize 5
|
||||||
|
|> List.iter (fun btns -> builder.AddComponents(btns) |> ignore)
|
||||||
|
builder
|
||||||
|
|
||||||
|
|
||||||
let getSellEmbed (items : ItemDetails list) =
|
let getSellEmbed (items : Inventory) =
|
||||||
let embeds , buttons =
|
let embeds , buttons =
|
||||||
items
|
items
|
||||||
|> List.map (fun item ->
|
|> List.choose (fun item ->
|
||||||
DiscordEmbedBuilder()
|
match item.Attributes with
|
||||||
.AddField("Sell For 💰", $"{item.Price} $GBT", true)
|
| CanSell price ->
|
||||||
.WithTitle($"{item.Name}")
|
let builder =
|
||||||
.WithColor(WeaponClass.getClassEmbedColor item)
|
DiscordEmbedBuilder()
|
||||||
.Build()
|
.AddField("Sell For 💰", $"{price} $GBT", true)
|
||||||
, DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Sell-{item.Id}", $"Sell {item.Name}") :> DiscordComponent)
|
.WithTitle($"{item.Name}")
|
||||||
|
.WithColor(WeaponClass.getClassEmbedColor item)
|
||||||
|
.Build()
|
||||||
|
let button = DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Sell-{item.Id}", $"Sell {item.Name}") :> DiscordComponent
|
||||||
|
Some ( builder , button )
|
||||||
|
| _ -> None)
|
||||||
|> List.unzip
|
|> List.unzip
|
||||||
|
|
||||||
|
// TODO: We should alert the user that they have no sellable items
|
||||||
DiscordFollowupMessageBuilder()
|
DiscordFollowupMessageBuilder()
|
||||||
.AddEmbeds(embeds)
|
.AddEmbeds(embeds)
|
||||||
.AddComponents(buttons)
|
.AddComponents(buttons)
|
||||||
.AsEphemeral(true)
|
.AsEphemeral(true)
|
||||||
|
|
||||||
let getConsumeEmbed (items : ItemDetails list) =
|
let checkHasStock (item : StoreItem) player =
|
||||||
let embeds , buttons =
|
if item.Stock > 0 || item.LimitStock = false
|
||||||
items
|
then Ok player
|
||||||
|> List.groupBy (fun item -> item.Id)
|
else Error $"{item.Item.Name} is out of stock! Check back later to purchase"
|
||||||
|> List.map (fun (itemId , items ) ->
|
|
||||||
let item = List.head items
|
|
||||||
let foodItem = Inventory.findFoodById itemId items
|
|
||||||
DiscordEmbedBuilder()
|
|
||||||
.AddField($"{foodItem.Item.Name}", $"Total {items.Length}\nBoosts {foodItem.TargetStat} +{foodItem.BoostAmount}", true)
|
|
||||||
.WithTitle($"Food Items")
|
|
||||||
.WithColor(WeaponClass.getClassEmbedColor item)
|
|
||||||
.Build()
|
|
||||||
, DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Sell-{id}", $"Sell {item.Name}") :> DiscordComponent)
|
|
||||||
|> List.unzip
|
|
||||||
|
|
||||||
DiscordFollowupMessageBuilder()
|
|
||||||
.AddEmbeds(embeds)
|
|
||||||
.AddComponents(buttons)
|
|
||||||
.AsEphemeral(true)
|
|
||||||
|
|
||||||
let checkHasSufficientFunds (item : Item) player =
|
let checkHasSufficientFunds (item : Item) player =
|
||||||
if player.Bank - item.Price >= 0<GBT>
|
match item.Attributes with
|
||||||
then Ok player
|
| CanBuy price ->
|
||||||
else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT"
|
if player.Bank - price >= 0<GBT>
|
||||||
|
then Ok player
|
||||||
|
else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT"
|
||||||
|
| _ -> Error $"{item.Name} item cannot be bought"
|
||||||
|
|
||||||
let checkAlreadyOwnsItem (item : Item) player =
|
let checkDoesntExceedStackCap (item : Item) player =
|
||||||
if player.Inventory |> List.exists (fun w -> item.Id = w.Id)
|
let itemCount =
|
||||||
then Error $"You already own {item.Name}!"
|
player.Inventory
|
||||||
else Ok player
|
|> List.countBy (fun i -> i.Id)
|
||||||
|
|> List.tryFind (fst >> ((=) item.Id))
|
||||||
|
|> Option.map snd
|
||||||
|
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
|
||||||
|
|
||||||
let checkSoldItemAlready item player =
|
let checkSoldItemAlready (item : Item) player =
|
||||||
if player.Inventory |> List.exists (fun i -> item.Id = i.Id)
|
if player.Inventory |> List.exists (fun i -> item.Id = i.Id)
|
||||||
then Ok player
|
then Ok player
|
||||||
else Error $"{item.Name} not found in your inventory! Looks like you sold it already."
|
else Error $"{item.Name} not found in your inventory! Looks like you sold it already."
|
||||||
@ -110,13 +141,19 @@ let checkHasItemsInArsenal itemType items player =
|
|||||||
then Ok player
|
then Ok player
|
||||||
else Error $"You currently have no {itemType} in your arsenal to sell!"
|
else Error $"You currently have no {itemType} in your arsenal to sell!"
|
||||||
|
|
||||||
let buy itemType (ctx : IDiscordContext) =
|
let buy (ctx : IDiscordContext) =
|
||||||
executePlayerAction ctx (fun player -> async {
|
executePlayerAction ctx (fun player -> async {
|
||||||
let playerItems = Inventory.getItemsByType itemType player.Inventory
|
try
|
||||||
let armoryItems = Inventory.getItemsByType itemType Armory.weapons
|
let channelId = ctx.GetChannel().Id
|
||||||
let itemStore = getBuyItemsEmbed playerItems armoryItems
|
let! items = DbService.getStoreItems channelId
|
||||||
do! ctx.FollowUp itemStore |> Async.AwaitTask
|
if items.Length > 0 then
|
||||||
do! Analytics.buyWeaponCommand (ctx.GetDiscordMember()) itemType
|
let itemStore = getBuyItemsEmbed player.Inventory items
|
||||||
|
do! ctx.FollowUp itemStore |> Async.AwaitTask
|
||||||
|
let! storeSymbol = DbService.getStoreSymbol channelId
|
||||||
|
do! Analytics.buyItemCommand (ctx.GetDiscordMember()) storeSymbol
|
||||||
|
else
|
||||||
|
do! Messaging.sendFollowUpMessage ctx "This channel doesn't have anything to sell"
|
||||||
|
with ex -> printfn $"{ex.Message}"
|
||||||
})
|
})
|
||||||
|
|
||||||
let sell itemType getItems (ctx : IDiscordContext) =
|
let sell itemType getItems (ctx : IDiscordContext) =
|
||||||
@ -126,47 +163,123 @@ let sell itemType getItems (ctx : IDiscordContext) =
|
|||||||
| Ok _ -> let itemStore = getSellEmbed items
|
| Ok _ -> let itemStore = getSellEmbed items
|
||||||
do! ctx.FollowUp(itemStore) |> Async.AwaitTask
|
do! ctx.FollowUp(itemStore) |> Async.AwaitTask
|
||||||
| Error e -> do! sendFollowUpMessage ctx e
|
| Error e -> do! sendFollowUpMessage ctx e
|
||||||
do! Analytics.sellWeaponCommand (ctx.GetDiscordMember()) itemType
|
do! Analytics.buyItemCommand (ctx.GetDiscordMember()) itemType
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let purchaseItemEmbed (item : Item) =
|
||||||
|
let embed = DiscordEmbedBuilder()
|
||||||
|
embed.ImageUrl <- item.IconUrl
|
||||||
|
embed.Title <- $"Purchased {item.Name}"
|
||||||
|
match item.Type with
|
||||||
|
| ItemType.Jpeg ->
|
||||||
|
if item.Symbol.Contains "RAFFLE" then
|
||||||
|
embed.Description <- $"Congratulations! You are in the draw for the {item.Name}. The winner will be announced shortly"
|
||||||
|
embed.ImageUrl <- item.Description
|
||||||
|
else
|
||||||
|
embed.Description <- $"Congratulations! You own the rights to the {item.Name} NFT. Please create a ticket in the support channel and we will transfer to your wallet"
|
||||||
|
| _ -> embed.Description <- $"Purchased {item.Name}"
|
||||||
|
embed
|
||||||
|
|
||||||
// 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 (ctx : IDiscordContext) itemId =
|
let handleBuyItem (ctx : IDiscordContext) itemId =
|
||||||
executePlayerAction ctx (fun player -> async {
|
executePlayerAction ctx (fun player -> async {
|
||||||
let item = Armory.weapons |> Inventory.findItemById itemId
|
let! storeInventory = DbService.getStoreItems (ctx.GetChannel().Id)
|
||||||
|
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
|
do! player
|
||||||
|> checkHasSufficientFunds item.getItem
|
|> checkHasSufficientFunds item
|
||||||
>>= checkAlreadyOwnsItem item.getItem
|
>>= checkHasStock storeItem
|
||||||
|
>>= checkDoesntExceedStackCap item
|
||||||
|> handleResultWithResponse ctx (fun player -> async {
|
|> handleResultWithResponse ctx (fun player -> async {
|
||||||
let newBalance = player.Bank - item.Price
|
let price = match item.Attributes with CanBuy price -> price | _ -> 0<GBT>
|
||||||
let p = { player with Bank = newBalance ; Inventory = item::player.Inventory }
|
do! DbService.updatePlayerCurrency -price player |> Async.Ignore
|
||||||
do! DbService.updatePlayer p |> Async.Ignore
|
do! DbService.addToPlayerInventory player.DiscordId item |> Async.Ignore
|
||||||
do! sendFollowUpMessage ctx $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining"
|
do! DbService.decrementItemStock item
|
||||||
do! Analytics.buyWeaponButton (ctx.GetDiscordMember()) item
|
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
|
||||||
|
builder.AddEmbed(purchaseItemEmbed (item)) |> ignore
|
||||||
|
do! ctx.FollowUp builder |> Async.AwaitTask
|
||||||
|
do! Analytics.buyWeaponButton (ctx.GetDiscordMember()) item.Name price
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
let handleSell (ctx : IDiscordContext) itemId =
|
let handleSell (ctx : IDiscordContext) itemId =
|
||||||
executePlayerAction ctx (fun player -> async {
|
executePlayerAction ctx (fun player -> async {
|
||||||
let item = Armory.weapons |> Inventory.findItemById itemId
|
let item = player.Inventory |> Inventory.findItemById itemId
|
||||||
do!
|
do!
|
||||||
player
|
player
|
||||||
|> checkSoldItemAlready item.getItem
|
|> checkSoldItemAlready item
|
||||||
|> handleResultWithResponse ctx (fun player -> async {
|
|> handleResultWithResponse ctx (fun player -> async {
|
||||||
let updatedPlayer = {
|
match item.Attributes with
|
||||||
player with
|
| CanSell price ->
|
||||||
Bank = player.Bank + item.Price
|
let updatedPlayer = {
|
||||||
Inventory = player.Inventory |> List.filter (fun i -> i.Id <> itemId)
|
player with
|
||||||
}
|
Bank = player.Bank + price
|
||||||
do!
|
Inventory = player.Inventory |> List.filter (fun i -> i.Id <> itemId)
|
||||||
[ DbService.updatePlayer updatedPlayer |> Async.Ignore
|
}
|
||||||
DbService.removeShieldEvent updatedPlayer.DiscordId itemId |> Async.Ignore
|
do!
|
||||||
sendFollowUpMessage ctx $"Sold {item.Name} for {item.Price}! Current Balance: {updatedPlayer.Bank}"
|
[ DbService.updatePlayer updatedPlayer |> Async.Ignore
|
||||||
Analytics.sellWeaponButton (ctx.GetDiscordMember()) item ]
|
DbService.removeShieldEvent updatedPlayer.DiscordId itemId |> Async.Ignore
|
||||||
|> Async.Parallel
|
sendFollowUpMessage ctx $"Sold {item.Name} for {price}! Current Balance: {updatedPlayer.Bank}"
|
||||||
|> Async.Ignore
|
Analytics.sellWeaponButton (ctx.GetDiscordMember()) item price ]
|
||||||
|
|> Async.Parallel
|
||||||
|
|> Async.Ignore
|
||||||
|
| _ -> ()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let consume (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
||||||
|
match player.Inventory |> Inventory.getFoods with
|
||||||
|
| [] -> do! Messaging.sendFollowUpMessage ctx "You do not have any items to consume"
|
||||||
|
| items ->
|
||||||
|
let items' = items |> List.map (fun i -> { Item = i ; Stock = 1 ; LimitStock = false ; Available = true })
|
||||||
|
let embeds = getItemEmbeds true items'
|
||||||
|
let builder = DiscordFollowupMessageBuilder().AddEmbeds(embeds).AsEphemeral(true)
|
||||||
|
do! ctx.FollowUp builder |> Async.AwaitTask
|
||||||
|
})
|
||||||
|
|
||||||
|
let handleConsume (ctx : IDiscordContext) itemId = PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
||||||
|
let item = player.Inventory |> Inventory.findItemById itemId
|
||||||
|
match player.Inventory |> Inventory.getFoods with
|
||||||
|
| [] -> do! Messaging.sendFollowUpMessage ctx "You do not have any items to consume"
|
||||||
|
| items ->
|
||||||
|
let items' = items |> List.map (fun i -> { Item = i ; Stock = 1 ; LimitStock = false ; Available = true })
|
||||||
|
let embeds = getItemEmbeds true items'
|
||||||
|
let builder = DiscordFollowupMessageBuilder().AddEmbeds(embeds).AsEphemeral(true)
|
||||||
|
do! ctx.FollowUp builder |> Async.AwaitTask
|
||||||
|
})
|
||||||
|
|
||||||
|
let showJpegsEmbed (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
||||||
|
let jpegs =
|
||||||
|
player.Inventory
|
||||||
|
|> Inventory.getItemsByType ItemType.Jpeg
|
||||||
|
|> List.map (fun i -> { Item = i ; Stock = 1 ; LimitStock = false ; Available = true })
|
||||||
|
let embeds = getItemEmbeds true jpegs
|
||||||
|
let builder = DiscordFollowupMessageBuilder().AddEmbeds(embeds).AsEphemeral(true)
|
||||||
|
do! ctx.FollowUp builder |> Async.AwaitTask
|
||||||
|
})
|
||||||
|
|
||||||
|
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 handleStoreEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
|
let handleStoreEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
|
||||||
let ctx = DiscordEventContext event :> IDiscordContext
|
let ctx = DiscordEventContext event :> IDiscordContext
|
||||||
let id = ctx.GetInteractionId()
|
let id = ctx.GetInteractionId()
|
||||||
@ -174,6 +287,8 @@ let handleStoreEvents (_ : DiscordClient) (event : ComponentInteractionCreateEve
|
|||||||
match id with
|
match id with
|
||||||
| id when id.StartsWith("Buy") -> handleBuyItem ctx itemId
|
| id when id.StartsWith("Buy") -> handleBuyItem ctx itemId
|
||||||
| id when id.StartsWith("Sell") -> handleSell ctx itemId
|
| id when id.StartsWith("Sell") -> handleSell ctx itemId
|
||||||
|
| id when id.StartsWith("Consume") -> handleConsume ctx itemId
|
||||||
|
| id when id.StartsWith("ShowJpegInventory") -> buy ctx
|
||||||
| _ ->
|
| _ ->
|
||||||
task {
|
task {
|
||||||
let builder = DiscordInteractionResponseBuilder()
|
let builder = DiscordInteractionResponseBuilder()
|
||||||
@ -181,7 +296,28 @@ let handleStoreEvents (_ : DiscordClient) (event : ComponentInteractionCreateEve
|
|||||||
builder.Content <- $"Incorrect Action identifier {id}"
|
builder.Content <- $"Incorrect Action identifier {id}"
|
||||||
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
|
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sendInitialEmbed (ctx : IDiscordContext) =
|
||||||
|
async {
|
||||||
|
try
|
||||||
|
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelBackAlley)
|
||||||
|
let builder = DiscordMessageBuilder()
|
||||||
|
let embed = DiscordEmbedBuilder()
|
||||||
|
embed.ImageUrl <- "https://s7.gifyu.com/images/ezgif.com-gif-maker-23203b9dca779ba7cf.gif"
|
||||||
|
embed.Title <- "Degenz Game"
|
||||||
|
embed.Color <- DiscordColor.Black
|
||||||
|
embed.Description <- "Hey, what do you want kid? Did you come alone?"
|
||||||
|
builder.AddEmbed embed |> ignore
|
||||||
|
let button = DiscordButtonComponent(ButtonStyle.Success, $"ShowJpegInventory-0", $"Show me your stash") :> DiscordComponent
|
||||||
|
builder.AddComponents [| button |] |> ignore
|
||||||
|
|
||||||
|
do! GuildEnvironment.botClientStore.Value.SendMessageAsync(channel, builder)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|> Async.Ignore
|
||||||
|
with e ->
|
||||||
|
printfn $"Error trying to get channel Jpeg Alley\n\n{e.Message}"
|
||||||
|
} |> Async.RunSynchronously
|
||||||
|
|
||||||
type Store() =
|
type Store() =
|
||||||
inherit ApplicationCommandModule ()
|
inherit ApplicationCommandModule ()
|
||||||
|
|
||||||
@ -194,32 +330,30 @@ type Store() =
|
|||||||
do! Messaging.sendSimpleResponse ctx msg
|
do! Messaging.sendSimpleResponse ctx msg
|
||||||
}
|
}
|
||||||
|
|
||||||
let checkChannel (ctx : IDiscordContext) =
|
// let checkChannel (ctx : IDiscordContext) (storeFn : IDiscordContext -> Task) =
|
||||||
match ctx.GetChannel().Id with
|
// let checkChannel (ctx : IDiscordContext) =
|
||||||
// | id when id = GuildEnvironment.channelBackAlley -> buy (Inventory.getItemsByType ItemType.Hack) ctx
|
// match ctx.GetChannel().Id with
|
||||||
| id when id = GuildEnvironment.channelArmory -> buy ItemType.Shield ctx
|
// | id when id = GuildEnvironment.channelBackAlley -> buy ItemType.Hack ctx
|
||||||
// | id when id = GuildEnvironment.channelMarket -> buy (Inventory.getItemsByType ItemType.Food) ctx
|
// | id when id = GuildEnvironment.channelArmory -> buy ItemType.Shield ctx
|
||||||
// | id when id = GuildEnvironment.channelAccessoryShop -> buy (Inventory.getItemsByType ItemType.Accessory) ctx
|
// | id when id = GuildEnvironment.channelMarket -> buy ItemType.Food ctx
|
||||||
| _ ->
|
// | id when id = GuildEnvironment.channelAccessoryShop -> buy ItemType.Accessory ctx
|
||||||
task {
|
// | _ ->
|
||||||
let msg = $"This channel doesn't have any items to sell"
|
// task {
|
||||||
do! Messaging.sendSimpleResponse ctx msg
|
// let msg = $"This channel doesn't have any items to sell. Try <#{GuildEnvironment.channelArmory}>"
|
||||||
}
|
// do! Messaging.sendSimpleResponse ctx msg
|
||||||
|
// }
|
||||||
|
|
||||||
|
[<SlashCommand("buy-item", "Purchase an item")>]
|
||||||
|
member _.BuyItem (ctx : InteractionContext) = buy (DiscordInteractionContext ctx)
|
||||||
|
|
||||||
// [<SlashCommand("buy-item", "Purchase an item")>]
|
|
||||||
// member _.BuyItem (ctx : InteractionContext) = checkChannel (DiscordInteractionContext(ctx))
|
|
||||||
//
|
|
||||||
[<SlashCommand("buy-hack", "Purchase a hack so you can take money from other Degenz")>]
|
[<SlashCommand("buy-hack", "Purchase a hack so you can take money from other Degenz")>]
|
||||||
member _.BuyHack (ctx : InteractionContext) =
|
member _.BuyHack (ctx : InteractionContext) =
|
||||||
enforceChannel (DiscordInteractionContext(ctx)) (buy ItemType.Hack)
|
enforceChannel (DiscordInteractionContext(ctx)) buy
|
||||||
|
|
||||||
[<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GBT")>]
|
[<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GBT")>]
|
||||||
member this.BuyShield (ctx : InteractionContext) =
|
member this.BuyShield (ctx : InteractionContext) =
|
||||||
enforceChannel (DiscordInteractionContext(ctx)) (buy ItemType.Shield)
|
enforceChannel (DiscordInteractionContext(ctx)) buy
|
||||||
|
|
||||||
// [<SlashCommand("buy-food", "Purchase a food item to help boost your stats")>]
|
|
||||||
// member this.BuyFood (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Food))
|
|
||||||
//
|
|
||||||
[<SlashCommand("sell-hack", "Sell a hack for GoodBoyTokenz")>]
|
[<SlashCommand("sell-hack", "Sell a hack for GoodBoyTokenz")>]
|
||||||
member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" (Inventory.getItemsByType ItemType.Hack))
|
member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" (Inventory.getItemsByType ItemType.Hack))
|
||||||
|
|
||||||
@ -227,10 +361,13 @@ type Store() =
|
|||||||
member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Shield))
|
member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Shield))
|
||||||
|
|
||||||
[<SlashCommand("consume", "Consume a food item")>]
|
[<SlashCommand("consume", "Consume a food item")>]
|
||||||
member this.Consume (ctx : InteractionContext) =
|
member this.Consume (ctx : InteractionContext) = consume (DiscordInteractionContext ctx)
|
||||||
enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Food))
|
|
||||||
|
|
||||||
// [<SlashCommand("inventory", "Check your inventory")>]
|
[<SlashCommand("jpegs", "Check your inventory")>]
|
||||||
// member this.Inventory (ctx : InteractionContext) =
|
member this.Inventory (ctx : InteractionContext) =
|
||||||
// enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType))
|
showJpegsEmbed (DiscordInteractionContext ctx)
|
||||||
|
|
||||||
|
[<SlashCommand("stats", "Check your stats")>]
|
||||||
|
member this.Stats (ctx : InteractionContext) =
|
||||||
|
showStats (DiscordInteractionContext ctx)
|
||||||
|
|
||||||
|
@ -9,8 +9,11 @@ open Degenz.Messaging
|
|||||||
|
|
||||||
let TrainerAchievement = "FINISHED_TRAINER"
|
let TrainerAchievement = "FINISHED_TRAINER"
|
||||||
let Sensei = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
|
let Sensei = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
|
||||||
let defaultHack = Armory.weapons |> Inventory.findHackById (int ItemId.Virus)
|
let weapons = DbService.getWeapons () |> Async.RunSynchronously
|
||||||
let defaultShield = Armory.weapons |> Inventory.findShieldById (int ItemId.Firewall)
|
let hackItem = weapons |> Inventory.findItemById (int ItemId.Virus)
|
||||||
|
let shieldItem = weapons |> Inventory.findItemById (int ItemId.Firewall)
|
||||||
|
let defaultHack = (Inventory.getHackItem hackItem).Value
|
||||||
|
let defaultShield = (Inventory.getShieldItem shieldItem ).Value
|
||||||
let CurrencyGift = 250<GBT>
|
let CurrencyGift = 250<GBT>
|
||||||
let BeginnerProtectionHours = 24
|
let BeginnerProtectionHours = 24
|
||||||
|
|
||||||
@ -21,7 +24,7 @@ let HackEvent () = {
|
|||||||
Adversary = Sensei
|
Adversary = Sensei
|
||||||
Success = true
|
Success = true
|
||||||
IsInstigator = true
|
IsInstigator = true
|
||||||
HackId = defaultHack.Item.Id
|
HackId = defaultHack.Id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let ShieldEvents () = [
|
let ShieldEvents () = [
|
||||||
@ -63,7 +66,7 @@ let handleTrainerStep1 (ctx : IDiscordContext) =
|
|||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
let msg = "Beautopia© is a dangerous place... quick, put up a SHIELD 🛡 before another Degen hacks you, and takes your 💰$GBT.\n\n"
|
let msg = "Beautopia© is a dangerous place... quick, put up a SHIELD 🛡 before another Degen hacks you, and takes your 💰$GBT.\n\n"
|
||||||
+ "To enable it, you need to run the `/shield` slash command.\n\n"
|
+ "To enable it, you need to run the `/shield` slash command.\n\n"
|
||||||
+ $"Type the `/shield` command now, then select - `{defaultShield.Item.Name}`\n"
|
+ $"Type the `/shield` command now, then select - `{defaultShield.Name}`\n"
|
||||||
let builder =
|
let builder =
|
||||||
DiscordInteractionResponseBuilder()
|
DiscordInteractionResponseBuilder()
|
||||||
.WithContent(msg)
|
.WithContent(msg)
|
||||||
@ -78,7 +81,7 @@ let defend (ctx : IDiscordContext) =
|
|||||||
do! Messaging.defer ctx
|
do! Messaging.defer ctx
|
||||||
let m = ctx.GetDiscordMember()
|
let m = ctx.GetDiscordMember()
|
||||||
let name = if System.String.IsNullOrEmpty m.Nickname then m.DisplayName else m.Nickname
|
let name = if System.String.IsNullOrEmpty m.Nickname then m.DisplayName else m.Nickname
|
||||||
let embed = Embeds.pickDefense "Trainer-2" { PlayerData.empty with Inventory = [ Shield defaultShield ] ; Name = name } true
|
let embed = Embeds.pickDefense "Trainer-2" { PlayerData.empty with Inventory = [ shieldItem ] ; Name = name } true
|
||||||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||||||
do! Analytics.trainingDojoStep "DefendCommand" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
do! Analytics.trainingDojoStep "DefendCommand" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||||
} |> Async.StartAsTask :> Task
|
} |> Async.StartAsTask :> Task
|
||||||
@ -101,11 +104,11 @@ let handleDefense (ctx : IDiscordContext) =
|
|||||||
let embed = Embeds.responseCreatedShield defaultShield
|
let embed = Embeds.responseCreatedShield defaultShield
|
||||||
do! ctx.FollowUp embed |> Async.AwaitTask
|
do! ctx.FollowUp embed |> Async.AwaitTask
|
||||||
do! Async.Sleep 1000
|
do! Async.Sleep 1000
|
||||||
do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{defaultHack.Item.Name}**"
|
do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{defaultHack.Name}**"
|
||||||
do! Async.Sleep 3000
|
do! Async.Sleep 3000
|
||||||
do! sendMessage' $"❌ HACKING FAILED!\n\n{playerName} defended hack from <@{Sensei.Id}>!"
|
do! sendMessage' $"❌ HACKING FAILED!\n\n{playerName} defended hack from <@{Sensei.Id}>!"
|
||||||
do! Async.Sleep 1500
|
do! Async.Sleep 1500
|
||||||
do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHack.Item.Name)
|
do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHack.Name)
|
||||||
do! Analytics.trainingDojoStep "ShieldActivated" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
do! Analytics.trainingDojoStep "ShieldActivated" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||||
} |> Async.StartAsTask :> Task
|
} |> Async.StartAsTask :> Task
|
||||||
|
|
||||||
@ -117,7 +120,7 @@ let handleTrainerStep3 (ctx : IDiscordContext) =
|
|||||||
.WithContent
|
.WithContent
|
||||||
( "Now let’s **HACK** 💻... I want you to **HACK ME**!\n\n"
|
( "Now let’s **HACK** 💻... I want you to **HACK ME**!\n\n"
|
||||||
+ "To **hack**, you need to run the `/hack` slash command.\n"
|
+ "To **hack**, you need to run the `/hack` slash command.\n"
|
||||||
+ $"Type the `/hack` command now, then choose me - <@{Sensei.Id}> as your target, and select `{defaultHack.Item.Name}`")
|
+ $"Type the `/hack` command now, then choose me - <@{Sensei.Id}> as your target, and select `{defaultHack.Name}`")
|
||||||
|
|
||||||
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
|
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
|
||||||
do! Analytics.trainingDojoStep "LetsHack" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
do! Analytics.trainingDojoStep "LetsHack" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||||
@ -130,7 +133,7 @@ let hack (target : DiscordUser) (ctx : IDiscordContext) =
|
|||||||
let isRightTarget = target.Id = Sensei.Id
|
let isRightTarget = target.Id = Sensei.Id
|
||||||
match isRightTarget with
|
match isRightTarget with
|
||||||
| true ->
|
| true ->
|
||||||
let player = { PlayerData.empty with Inventory = [ Hack defaultHack ] }
|
let player = { PlayerData.empty with Inventory = [ hackItem ] }
|
||||||
let bot = { PlayerData.empty with DiscordId = Sensei.Id ; Name = Sensei.Name }
|
let bot = { PlayerData.empty with DiscordId = Sensei.Id ; Name = Sensei.Name }
|
||||||
let embed = Embeds.pickHack "Trainer-4" player bot true
|
let embed = Embeds.pickHack "Trainer-4" player bot true
|
||||||
|
|
||||||
@ -166,8 +169,8 @@ let handleHack (ctx : IDiscordContext) =
|
|||||||
**🎁 FREE GIFTS:**
|
**🎁 FREE GIFTS:**
|
||||||
Here, I'm going to gift you:
|
Here, I'm going to gift you:
|
||||||
|
|
||||||
1. A hack - `{defaultHack.Item.Name}`,
|
1. A hack - `{defaultHack.Name}`,
|
||||||
2. A shield - `{defaultShield.Item.Name}`,
|
2. A shield - `{defaultShield.Name}`,
|
||||||
3. `{CurrencyGift} 💰 $GBT`,
|
3. `{CurrencyGift} 💰 $GBT`,
|
||||||
|
|
||||||
I'm also going to give you some **TEMPORARY SHIELDS** until you buy your own.
|
I'm also going to give you some **TEMPORARY SHIELDS** until you buy your own.
|
||||||
@ -194,7 +197,7 @@ let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerActi
|
|||||||
let! completed = DbService.checkHasAchievement player.DiscordId TrainerAchievement
|
let! completed = DbService.checkHasAchievement player.DiscordId TrainerAchievement
|
||||||
if not completed then
|
if not completed then
|
||||||
do! DbService.addAchievement player.DiscordId TrainerAchievement |> Async.Ignore
|
do! DbService.addAchievement player.DiscordId TrainerAchievement |> Async.Ignore
|
||||||
let addIfDoesntExist (weapon : ItemDetails) (inventory : Inventory) =
|
let addIfDoesntExist (weapon : Item) (inventory : Inventory) =
|
||||||
if inventory |> List.exists (fun i -> i.Id = weapon.Id)
|
if inventory |> List.exists (fun i -> i.Id = weapon.Id)
|
||||||
then inventory
|
then inventory
|
||||||
else weapon::inventory
|
else weapon::inventory
|
||||||
@ -209,8 +212,8 @@ let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerActi
|
|||||||
|> List.consTo (HackEvent())
|
|> List.consTo (HackEvent())
|
||||||
Inventory =
|
Inventory =
|
||||||
player.Inventory
|
player.Inventory
|
||||||
|> addIfDoesntExist (Hack defaultHack)
|
|> addIfDoesntExist hackItem
|
||||||
|> addIfDoesntExist (Shield defaultShield)
|
|> addIfDoesntExist shieldItem
|
||||||
Bank = player.Bank + CurrencyGift
|
Bank = player.Bank + CurrencyGift
|
||||||
}
|
}
|
||||||
do! DbService.updatePlayer updatedPlayer |> Async.Ignore
|
do! DbService.updatePlayer updatedPlayer |> Async.Ignore
|
||||||
@ -227,7 +230,7 @@ let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerActi
|
|||||||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||||||
|
|
||||||
do! Async.Sleep 2000
|
do! Async.Sleep 2000
|
||||||
let rewards = [ $"{defaultHack.Item.Name} Hack" ; $"{defaultShield.Item.Name} Shield" ; $"{CurrencyGift} 💰$GBT"]
|
let rewards = [ $"{defaultHack.Name} Hack" ; $"{defaultShield.Name} Shield" ; $"{CurrencyGift} 💰$GBT"]
|
||||||
let embed = Embeds.getAchievementEmbed rewards "You completed the Training Dojo and collected some gifts." TrainerAchievement
|
let embed = Embeds.getAchievementEmbed rewards "You completed the Training Dojo and collected some gifts." TrainerAchievement
|
||||||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||||||
|
|
||||||
|
@ -35,9 +35,9 @@ let channelWhitelist = getId "CHANNEL_WHITELIST"
|
|||||||
let channelShelters = getId "CHANNEL_SHELTERS"
|
let channelShelters = getId "CHANNEL_SHELTERS"
|
||||||
let channelSlots = getId "CHANNEL_SLOTS"
|
let channelSlots = getId "CHANNEL_SLOTS"
|
||||||
//let channelJackpotNum = getId "CHANNEL_JACKPOTNUM"
|
//let channelJackpotNum = getId "CHANNEL_JACKPOTNUM"
|
||||||
//let channelBackAlley = getId "CHANNEL_BACKALLEY"
|
let channelBackAlley = getId "CHANNEL_BACKALLEY"
|
||||||
//let channelMarket = getId "CHANNEL_MARKET"
|
let channelMarket = getId "CHANNEL_MARKET"
|
||||||
//let channelAccessoryShop = getId "CHANNEL_ACCESSORIES"
|
let channelAccessoryShop = getId "CHANNEL_ACCESSORIES"
|
||||||
|
|
||||||
//let channelThievery = getId "CHANNEL_THIEVERY"
|
//let channelThievery = getId "CHANNEL_THIEVERY"
|
||||||
let botIdHackerBattle = getId "BOT_HACKER_BATTLE"
|
let botIdHackerBattle = getId "BOT_HACKER_BATTLE"
|
||||||
@ -52,3 +52,4 @@ let roleAdmin = getId "ROLE_ADMIN"
|
|||||||
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
|
||||||
|
let mutable botClientStore : DiscordClient option = None
|
||||||
|
@ -178,6 +178,48 @@ let getInvitedUserCount userId =
|
|||||||
|> Sql.executeRowAsync (fun read -> read.int "count")
|
|> Sql.executeRowAsync (fun read -> read.int "count")
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let getWhitelistItem () =
|
||||||
|
connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT stock, buy_price FROM store_item
|
||||||
|
JOIN item i on store_item.item_id = i.id
|
||||||
|
WHERE i.symbol = 'WHITELIST'
|
||||||
|
"""
|
||||||
|
|> Sql.executeRowAsync (fun read -> {| Stock = read.int "stock" ; Price = (read.int "buy_price") * 1<GBT> |})
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let updateWhitelistStock () = async {
|
||||||
|
try
|
||||||
|
do! connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.query """
|
||||||
|
UPDATE store_item SET stock = GREATEST(stock - 1, 0)
|
||||||
|
WHERE store_item.item_id = (SELECT id FROM item WHERE symbol = 'WHITELIST')
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|> Async.Ignore
|
||||||
|
return true
|
||||||
|
with _ -> return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let setWhitelistStock amount = async {
|
||||||
|
try
|
||||||
|
do! connStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ ( "amount" , Sql.int amount ) ]
|
||||||
|
|> Sql.query """
|
||||||
|
UPDATE store_item SET stock = @amount
|
||||||
|
WHERE store_item.item_id = (SELECT id FROM item WHERE symbol = 'WHITELIST')
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|> Async.Ignore
|
||||||
|
return true
|
||||||
|
with _ -> return false
|
||||||
|
}
|
||||||
|
|
||||||
let guildInviteEmbed =
|
let guildInviteEmbed =
|
||||||
let rewardMsg =
|
let rewardMsg =
|
||||||
$"**Your Mission:**\nCLICK THE BUTTON below, then share your **UNIQUE LINK** with any Degenz you want to invite into the Server.\n\n" +
|
$"**Your Mission:**\nCLICK THE BUTTON below, then share your **UNIQUE LINK** with any Degenz you want to invite into the Server.\n\n" +
|
||||||
@ -431,7 +473,7 @@ let handleGimmeWhitelist (ctx : IDiscordContext) =
|
|||||||
|
|
||||||
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
|
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
|
||||||
|
|
||||||
let! wlItem = DbService.getWhitelistItem ()
|
let! wlItem = getWhitelistItem ()
|
||||||
let! availability = tryGrantWhitelist ctx wlItem.Stock wlItem.Price
|
let! availability = tryGrantWhitelist ctx wlItem.Stock wlItem.Price
|
||||||
match availability with
|
match availability with
|
||||||
| NotAHacker -> whitelistEmbed.Description <- notAHackerMsg
|
| NotAHacker -> whitelistEmbed.Description <- notAHackerMsg
|
||||||
@ -480,7 +522,7 @@ let handleBuyWhitelist (ctx : IDiscordContext) =
|
|||||||
let builder = DiscordInteractionResponseBuilder().AsEphemeral(true)
|
let builder = DiscordInteractionResponseBuilder().AsEphemeral(true)
|
||||||
do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder)
|
do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder)
|
||||||
|
|
||||||
let! wlItem = DbService.getWhitelistItem ()
|
let! wlItem = getWhitelistItem ()
|
||||||
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
|
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
|
||||||
match! tryGrantWhitelist ctx wlItem.Stock wlItem.Price with
|
match! tryGrantWhitelist ctx wlItem.Stock wlItem.Price with
|
||||||
| NotAHacker ->
|
| NotAHacker ->
|
||||||
@ -499,7 +541,7 @@ let handleBuyWhitelist (ctx : IDiscordContext) =
|
|||||||
builder.Content <- $"We just ran out of stock, tough shit"
|
builder.Content <- $"We just ran out of stock, tough shit"
|
||||||
do! ctx.FollowUp(builder)
|
do! ctx.FollowUp(builder)
|
||||||
| Granted player ->
|
| Granted player ->
|
||||||
match! DbService.updateWhitelistStock () with
|
match! updateWhitelistStock () with
|
||||||
| true ->
|
| true ->
|
||||||
let embed = DiscordEmbedBuilder()
|
let embed = DiscordEmbedBuilder()
|
||||||
embed.Description <- buyWhitelistMsg
|
embed.Description <- buyWhitelistMsg
|
||||||
@ -588,10 +630,10 @@ let handleGuildMemberAdded _ (eventArgs : GuildMemberAddEventArgs) =
|
|||||||
do! processNewUser eventArgs
|
do! processNewUser eventArgs
|
||||||
} :> Task
|
} :> Task
|
||||||
|
|
||||||
let rec setWhitelistStock amount (ctx : IDiscordContext) =
|
let setCurrentWhitelistStock amount (ctx : IDiscordContext) =
|
||||||
task {
|
task {
|
||||||
do! Messaging.defer ctx
|
do! Messaging.defer ctx
|
||||||
let! result = DbService.setWhitelistStock amount
|
let! result = setWhitelistStock amount
|
||||||
if result then
|
if result then
|
||||||
do! Messaging.sendFollowUpMessage ctx $"Set Whitelist stock to {amount}"
|
do! Messaging.sendFollowUpMessage ctx $"Set Whitelist stock to {amount}"
|
||||||
else
|
else
|
||||||
|
304
Bot/Items.json
304
Bot/Items.json
@ -1,304 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"Case": "Hack",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"Power": 20,
|
|
||||||
"Class": 0,
|
|
||||||
"Cooldown": 2,
|
|
||||||
"Item": {
|
|
||||||
"Id": 0,
|
|
||||||
"Name": "Virus",
|
|
||||||
"Price": 250,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Hack",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"Power": 20,
|
|
||||||
"Class": 1,
|
|
||||||
"Cooldown": 2,
|
|
||||||
"Item": {
|
|
||||||
"Id": 1,
|
|
||||||
"Name": "Remote Access",
|
|
||||||
"Price": 250,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Hack",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"Power": 20,
|
|
||||||
"Class": 2,
|
|
||||||
"Cooldown": 2,
|
|
||||||
"Item": {
|
|
||||||
"Id": 2,
|
|
||||||
"Name": "Worm",
|
|
||||||
"Price": 250,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Shield",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"Class": 0,
|
|
||||||
"Cooldown": 300,
|
|
||||||
"Item": {
|
|
||||||
"Id": 6,
|
|
||||||
"Name": "Firewall",
|
|
||||||
"Price": 100,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Shield",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"Class": 1,
|
|
||||||
"Cooldown": 300,
|
|
||||||
"Item": {
|
|
||||||
"Id": 7,
|
|
||||||
"Name": "Encryption",
|
|
||||||
"Price": 100,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Shield",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"Class": 2,
|
|
||||||
"Cooldown": 300,
|
|
||||||
"Item": {
|
|
||||||
"Id": 8,
|
|
||||||
"Name": "Cypher",
|
|
||||||
"Price": 100,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Food",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"TargetStat" : 0,
|
|
||||||
"BoostAmount" : 30,
|
|
||||||
"Item": {
|
|
||||||
"Id": 12,
|
|
||||||
"Name": "Protein Powder",
|
|
||||||
"Price": 50,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : false,
|
|
||||||
"CanConsume" : true,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Food",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"TargetStat" : 1,
|
|
||||||
"BoostAmount" : 30,
|
|
||||||
"Item": {
|
|
||||||
"Id": 13,
|
|
||||||
"Name": "Toro Loco",
|
|
||||||
"Price": 50,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : false,
|
|
||||||
"CanConsume" : true,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Food",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"TargetStat" : 2,
|
|
||||||
"BoostAmount" : 30,
|
|
||||||
"Item": {
|
|
||||||
"Id": 14,
|
|
||||||
"Name": "Oldports Cigs",
|
|
||||||
"Price": 50,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : false,
|
|
||||||
"CanConsume" : true,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Food",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"TargetStat" : 3,
|
|
||||||
"BoostAmount" : 30,
|
|
||||||
"Item": {
|
|
||||||
"Id": 15,
|
|
||||||
"Name": "Moon Pie",
|
|
||||||
"Price": 50,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : false,
|
|
||||||
"CanConsume" : true,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Accessory",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"TargetStat" : 0,
|
|
||||||
"FloorBoost" : 25,
|
|
||||||
"CeilBoost" : 0,
|
|
||||||
"Item": {
|
|
||||||
"Id": 20,
|
|
||||||
"Name": "Kettlebell",
|
|
||||||
"Price": 250,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Accessory",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"TargetStat" : 1,
|
|
||||||
"FloorBoost" : 25,
|
|
||||||
"CeilBoost" : 0,
|
|
||||||
"Item": {
|
|
||||||
"Id": 21,
|
|
||||||
"Name": "Headphones",
|
|
||||||
"Price": 250,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Accessory",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"TargetStat" : 2,
|
|
||||||
"FloorBoost" : 0,
|
|
||||||
"CeilBoost" : 25,
|
|
||||||
"Item": {
|
|
||||||
"Id": 22,
|
|
||||||
"Name": "Rolox Watch",
|
|
||||||
"Price": 250,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Case": "Accessory",
|
|
||||||
"Fields": [
|
|
||||||
{
|
|
||||||
"TargetStat" : 3,
|
|
||||||
"FloorBoost" : 0,
|
|
||||||
"CeilBoost" : 25,
|
|
||||||
"Item": {
|
|
||||||
"Id": 23,
|
|
||||||
"Name": "Buddha Keychain",
|
|
||||||
"Price": 250,
|
|
||||||
"Attributes": {
|
|
||||||
"CanBuy" : true,
|
|
||||||
"CanSell" : true,
|
|
||||||
"CanConsume" : false,
|
|
||||||
"CanTrade" : false,
|
|
||||||
"CanDrop" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
@ -5,9 +5,9 @@ framework: net6.0, netstandard2.0, netstandard2.1
|
|||||||
|
|
||||||
nuget FSharp.Core >= 6.0.0
|
nuget FSharp.Core >= 6.0.0
|
||||||
|
|
||||||
nuget DSharpPlus >= 4.2.0-nightly-01105
|
nuget DSharpPlus >= 4.2.0-nightly-01125
|
||||||
nuget DSharpPlus.Interactivity >= 4.2.0-nightly-01105
|
nuget DSharpPlus.Interactivity >= 4.2.0-nightly-01125
|
||||||
nuget DSharpPlus.SlashCommands >= 4.2.0-nightly-01105
|
nuget DSharpPlus.SlashCommands >= 4.2.0-nightly-01125
|
||||||
|
|
||||||
nuget MongoDB.Driver
|
nuget MongoDB.Driver
|
||||||
nuget dotenv.net 3.1.1
|
nuget dotenv.net 3.1.1
|
||||||
|
10
paket.lock
10
paket.lock
@ -8,7 +8,7 @@ NUGET
|
|||||||
System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net471)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net471)) (&& (== netstandard2.1) (< netstandard2.0))
|
System.Buffers (>= 4.5.1) - restriction: || (&& (== net6.0) (>= net471)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net471)) (&& (== netstandard2.1) (< netstandard2.0))
|
||||||
dotenv.net (3.1.1)
|
dotenv.net (3.1.1)
|
||||||
System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (< netstandard2.0))
|
System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (< netstandard2.0))
|
||||||
DSharpPlus (4.2.0-nightly-01105)
|
DSharpPlus (4.3.0-nightly-01125)
|
||||||
Emzi0767.Common (>= 2.6.2)
|
Emzi0767.Common (>= 2.6.2)
|
||||||
Microsoft.Extensions.Logging.Abstractions (>= 5.0)
|
Microsoft.Extensions.Logging.Abstractions (>= 5.0)
|
||||||
Newtonsoft.Json (>= 13.0.1)
|
Newtonsoft.Json (>= 13.0.1)
|
||||||
@ -18,11 +18,11 @@ NUGET
|
|||||||
System.Net.WebSockets.Client (>= 4.3.2)
|
System.Net.WebSockets.Client (>= 4.3.2)
|
||||||
System.Runtime.InteropServices.RuntimeInformation (>= 4.3)
|
System.Runtime.InteropServices.RuntimeInformation (>= 4.3)
|
||||||
System.Threading.Channels (>= 5.0)
|
System.Threading.Channels (>= 5.0)
|
||||||
DSharpPlus.Interactivity (4.2.0-nightly-01105)
|
DSharpPlus.Interactivity (4.3.0-nightly-01125)
|
||||||
ConcurrentHashSet (>= 1.1)
|
ConcurrentHashSet (>= 1.1)
|
||||||
DSharpPlus (>= 4.2.0-nightly-01105)
|
DSharpPlus (>= 4.3.0-nightly-01125)
|
||||||
DSharpPlus.SlashCommands (4.2.0-nightly-01105)
|
DSharpPlus.SlashCommands (4.3.0-nightly-01125)
|
||||||
DSharpPlus (>= 4.2.0-nightly-01105)
|
DSharpPlus (>= 4.3.0-nightly-01125)
|
||||||
Microsoft.Extensions.DependencyInjection (>= 5.0.1)
|
Microsoft.Extensions.DependencyInjection (>= 5.0.1)
|
||||||
Emzi0767.Common (2.6.2)
|
Emzi0767.Common (2.6.2)
|
||||||
System.Collections.Immutable (>= 5.0)
|
System.Collections.Immutable (>= 5.0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user