module Degenz.DbService open System open Npgsql.FSharp open Degenz let connStr = GuildEnvironment.connectionString let getPlayerEvents (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) SELECT event_type, success, is_instigator, item_id, cooldown, adversary_id, adversary_name, created_at FROM player_event ,usr WHERE user_id = usr.id and created_at > NOW() at time zone 'utc' - INTERVAL '24 HOURS'; """ |> Sql.executeAsync (fun read -> match read.string "event_type" with | "Hacking" -> Hacking { IsInstigator = read.bool "is_instigator" Success = read.bool "success" Adversary = { Id = read.string "adversary_id" |> uint64 ; Name = read.string "adversary_name" } HackId = read.int "item_id" } | "Shielding" -> Shielding (read.int "item_id") | "Stealing" -> Stealing ( read.bool "is_instigator" , { Id = read.string "adversary_id" |> uint64 ; Name = read.string "adversary_name" } ) | "PlayingSlot" -> PlayingSlot | _ -> Imprison |> fun t -> let date = read.dateTimeOrNone "created_at" |> Option.defaultValue DateTime.UtcNow { Type = t ; Cooldown = read.int "cooldown" * 1 ; Timestamp = date } ) |> 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) = connStr |> Sql.connect |> Sql.parameters [ ( "did" , Sql.string (string player.DiscordId) ) ( "strength", Sql.int player.Stats.Strength.Amount ) ( "focus", Sql.int player.Stats.Focus.Amount ) ( "charisma", Sql.int player.Stats.Charisma.Amount ) ( "luck", Sql.int player.Stats.Luck.Amount ) ] |> Sql.query """ WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did) UPDATE player_stat SET strength = @strength, focus = @focus, charisma = @charisma, luck = @luck, updated_at = now() at time zone 'utc' FROM usr WHERE usr.id = user_id; """ |> Sql.executeNonQueryAsync |> Async.AwaitTask let tryFindPlayer (discordId : uint64) = async { try let! user = connStr |> Sql.connect |> Sql.parameters [ "did", Sql.string (string discordId) ] |> Sql.query """ SELECT discord_id, display_name, gbt, in_game, inventory, strength, focus, charisma, luck FROM "user" WHERE discord_id = @did """ |> Sql.executeAsync (fun read -> let inv = read.intArray "inventory" {| DiscordId = read.string "discord_id" |> uint64 Name = read.string "display_name" Bank = read.intOrNone "gbt" |> Option.map ((*) 1) |> Option.defaultValue 0 Inventory = inv |> Array.toList Strength = read.intOrNone "strength" |> Option.defaultValue 0 Focus = read.intOrNone "focus" |> Option.defaultValue 0 Charisma = read.intOrNone "charisma" |> Option.defaultValue 0 Luck = read.intOrNone "luck" |> Option.defaultValue 0 Active = read.bool "in_game" |}) |> Async.AwaitTask match List.tryHead user with | None -> return None | Some u -> let! events = getPlayerEvents u.DiscordId let inventory = u.Inventory |> List.choose (fun id -> Armory.weapons |> List.tryFind (fun item -> item.Id = id)) let strength = PlayerStats.calculateActiveStat StatId.Strength u.Strength inventory let focus = PlayerStats.calculateActiveStat StatId.Focus u.Focus inventory let charisma = PlayerStats.calculateActiveStat StatId.Charisma u.Charisma inventory let luck = PlayerStats.calculateActiveStat StatId.Luck u.Luck inventory return Some { DiscordId = u.DiscordId Name = u.Name Inventory = inventory Events = events Stats = { Strength = strength ; Focus = focus ; Charisma = charisma ; Luck = luck } Bank = u.Bank Active = u.Active } with e -> printfn $"Got an error{e.Message}" return None } let updatePlayerCurrency (addAmount : int) (player : PlayerData) = connStr |> Sql.connect |> Sql.parameters [ "did", Sql.string (string player.DiscordId) "gbt", Sql.int (int addAmount) ] |> Sql.query """ UPDATE "user" SET gbt = gbt + @gbt WHERE discord_id = @did; """ |> Sql.executeNonQueryAsync |> Async.AwaitTask let updatePlayer (player : PlayerData) = connStr |> Sql.connect |> Sql.parameters [ "did", Sql.string (string player.DiscordId) "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 "focus", Sql.int player.Stats.Focus.Amount "charisma", Sql.int player.Stats.Charisma.Amount "luck", Sql.int player.Stats.Luck.Amount ] |> Sql.query """ UPDATE "user" SET gbt = @gbt, inventory = @inv, strength = @strength, focus = @focus, charisma = @charisma, luck = @luck WHERE discord_id = @did """ |> Sql.executeNonQueryAsync |> Async.AwaitTask let addAchievement (did : uint64) (achievement : string) = connStr |> Sql.connect |> Sql.parameters [ ( "did" , Sql.string (string did) ) ( "symbol", Sql.string achievement ) ] |> Sql.query """ WITH ach AS (SELECT id FROM achievement WHERE symbol = cast(@symbol AS achievement_symbol_enum)), usr AS (SELECT id FROM "user" WHERE discord_id = @did) INSERT INTO user_achievements_achievement (user_id, achievement_id) SELECT usr.id, ach.id FROM usr, ach; """ |> Sql.executeNonQueryAsync |> Async.AwaitTask let checkHasAchievement (did : uint64) (achievement : string) = async { let! result = connStr |> Sql.connect |> Sql.parameters [ ( "did" , Sql.string (string did) ) ( "symbol", Sql.string achievement ) ] |> Sql.query """ WITH ach AS (SELECT id FROM achievement WHERE symbol = cast(@symbol AS achievement_symbol_enum)), usr AS (SELECT id FROM "user" WHERE discord_id = @did) SELECT achievement_id FROM user_achievements_achievement, ach, usr WHERE user_id = usr.id and achievement_id = ach.id; """ |> Sql.executeAsync (fun read -> read.int "achievement_id") |> Async.AwaitTask return List.isEmpty result |> not } let removeShieldEvent (did : uint64) shieldId = connStr |> Sql.connect |> Sql.parameters [ ( "did" , Sql.string (string did) ) ( "shield_id", Sql.int shieldId ) ] |> Sql.query """ WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did) DELETE FROM player_event USING usr WHERE user_id = usr.id and event_type = 'Shielding' and item_id = @shield_id """ |> Sql.executeNonQueryAsync |> Async.AwaitTask let addPlayerEvent (did : uint64) (playerEvent : PlayerEvent) = let sqlParams , query = match playerEvent.Type with | Hacking h -> [ ( "did", Sql.string (string did) ) ( "success" , Sql.bool h.Success ) ( "is_instigator", Sql.bool h.IsInstigator ) ( "item_id", Sql.int h.HackId ) ( "cooldown", Sql.int (int playerEvent.Cooldown) ) ( "adversary_id", Sql.string (string h.Adversary.Id) ) ( "adversary_name", Sql.string (string h.Adversary.Name) ) ] , """ WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did) INSERT INTO player_event (event_type, success, is_instigator, item_id, cooldown, adversary_id, adversary_name, user_id) SELECT 'Hacking', @success, @is_instigator, @item_id, @cooldown, @adversary_id, @adversary_name, usr.id FROM usr """ | Shielding id -> [ ( "did" , Sql.string (string did) ) ( "item_id", Sql.int id ) ( "cooldown", Sql.int (int playerEvent.Cooldown) ) ] , """ WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did) INSERT INTO player_event (event_type, item_id, cooldown, user_id) SELECT 'Shielding', @item_id, @cooldown, usr.id FROM usr """ | Stealing(instigator, victim) -> [ ( "did" , Sql.string (string did) ) ( "is_instigator", Sql.bool instigator ) ( "adversary_id", Sql.string (string victim.Id) ) ( "adversary_name", Sql.string (string victim.Name) ) ( "cooldown", Sql.int (int playerEvent.Cooldown) ) ] , """ WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did) INSERT INTO player_event (event_type, is_instigator, adversary_id, adversary_name, cooldown, user_id) SELECT 'Stealing', @is_instigator, @adversary_id, @adversary_name, @cooldown, usr.id FROM usr """ | PlayingSlot -> [ ( "did" , Sql.string (string did) ) ] , """ WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did) INSERT INTO player_event (event_type, cooldown, user_id) SELECT 'PlayingSlot', 1, usr.id FROM usr """ | Imprison -> [ ( "did" , Sql.string (string did) ) ( "cooldown", Sql.int (int playerEvent.Cooldown) ) ] , """ WITH usr AS (SELECT id FROM "user" WHERE discord_id = @did) INSERT INTO player_event (event_type, cooldown, user_id) SELECT 'Imprison', @cooldown, usr.id FROM usr """ connStr |> Sql.connect |> Sql.parameters sqlParams |> Sql.query query |> Sql.executeNonQueryAsync |> Async.AwaitTask let getRandomHackablePlayers (did : uint64) = connStr |> Sql.connect |> Sql.parameters [ "did", Sql.string (string did) ] |> Sql.query """ SELECT discord_id, display_name FROM "user" JOIN user_achievements_achievement uaa ON "user".id = uaa.user_id JOIN achievement a ON uaa.achievement_id = a.id AND a.symbol = 'FINISHED_TRAINER' WHERE "user".in_game = true AND gbt > 20 AND "user".discord_id != @did ORDER BY random() LIMIT 10 """ |> Sql.executeAsync (fun read -> {| Id = read.string "discord_id" |> uint64 ; Name = read.string "display_name" |}) |> 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 |}) |> 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 }