183 lines
7.4 KiB
Forth

module Degenz.DbService
open Degenz.Types
open System
open Npgsql.FSharp
type User = {
Name : string
DiscordId : uint64
Bank : int<GBT>
Strength : int
Inventory : int array
}
let mapBack user : PlayerData =
{ DiscordId = user.DiscordId
Name = user.Name
Inventory = user.Inventory |> Array.choose (fun id -> Armory.battleItems |> Array.tryFind (fun i -> i.Id = id))
Events = [||]
Traits = { PlayerTraits.empty with Strength = user.Strength }
Bank = user.Bank
}
let getPlayerEvents connStr (player : PlayerData) =
connStr
|> Sql.connect
|> Sql.parameters [ "did", Sql.string (string player.DiscordId) ]
|> 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" } )
| _ -> Imprison
|> fun t ->
let date = read.dateTimeOrNone "created_at" |> Option.defaultValue DateTime.UtcNow
{ Type = t ; Cooldown = read.int "cooldown" * 1<mins> ; Timestamp = date }
)
|> Async.AwaitTask
let tryFindPlayer connStr (discordId : uint64) =
async {
let! user =
connStr
|> Sql.connect
|> Sql.parameters [ "did", Sql.string (string discordId) ]
|> Sql.query """
SELECT discord_id, display_name, gbt, strength, inventory FROM "user" WHERE discord_id = @did
"""
|> Sql.executeAsync (fun read ->
{
DiscordId = read.string "discord_id" |> uint64
Name = read.string "display_name"
Bank = read.int "gbt" * 1<GBT>
Strength = read.int "strength"
Inventory = read.intArray "inventory"
})
|> Async.AwaitTask
match List.tryHead user with
| None -> return None
| Some u ->
let player = mapBack u
let! events = getPlayerEvents connStr player
return Some { player with Events = events |> List.toArray }
}
let updatePlayer connStr (player : PlayerData) =
connStr
|> Sql.connect
|> Sql.parameters [
"did", Sql.string (string player.DiscordId)
"gbt", Sql.int (int player.Bank)
"str", Sql.int (int player.Traits.Strength)
"inv", Sql.intArray (player.Inventory |> Array.map (fun i -> i.Id))
]
|> Sql.query """
UPDATE "user" SET gbt = @gbt, strength = @str, inventory = @inv
WHERE discord_id = @did
"""
|> Sql.executeNonQueryAsync
|> Async.AwaitTask
let addAchievement connStr (did : uint64) (achievement : string) =
connStr
|> Sql.connect
|> Sql.parameters
[ ( "did" , Sql.string (string did) )
( "symbol", Sql.string achievement ) ]
|> Sql.query """
WITH ach AS (INSERT INTO achievement (symbol) VALUES (@symbol) RETURNING id),
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 connStr (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 = @symbol),
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 addPlayerEvent connStr (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
"""
| 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