module Degenz.DbService open Degenz.Types open System open MongoDB.Bson open MongoDB.Bson.Serialization open MongoDB.Driver open Npgsql.FSharp type User = { Name : string DiscordId : uint64 Bank : int Strength : int Inventory : int array } let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING")) let db = mongo.GetDatabase("degenz") let players = db.GetCollection("players") let tryWithDefault (bson : BsonDocument) field (defaultValue : 'a) (map : BsonValue -> 'a) = try let result , bval = bson.TryGetValue(field) if result then map bval else defaultValue with _ -> defaultValue let mapBack user (bson : BsonDocument) : PlayerData = { DiscordId = user.DiscordId Name = user.Name Inventory = user.Inventory |> Array.map (fun iId -> Armory.battleItems |> Array.tryFind (fun i -> i.Id = iId) |> function | Some i -> [| i |] | None -> [||]) |> Array.concat Events = tryWithDefault bson "Events" [||] (fun v -> v.AsBsonArray |> Seq.map (fun (bv : BsonValue) -> BsonSerializer.Deserialize(bv.ToBsonDocument())) |> Seq.toArray) Traits = { PlayerTraits.empty with Strength = user.Strength } // Achievements = // tryWithDefault bson "Achievements" [||] (fun v -> // v.AsBsonArray |> Seq.map (fun (bv : BsonValue) -> bv.AsString) |> Seq.toArray) // XP = tryWithDefault bson "XP" 0 (fun v -> v.AsInt32) Bank = user.Bank } let tryFindPlayer connStr (id : uint64) = async { let filter = Builders.Filter.Eq("Player.DiscordId", id) let! player = players.FindAsync(filter) |> Async.AwaitTask let! user = connStr |> Sql.connect |> Sql.query "SELECT * FROM users WHERE discordId = @did" |> Sql.parameters [ "did", Sql.int64 (int64 id) ] |> Sql.executeAsync (fun read -> { DiscordId = read.int64 "discordId" |> uint64 Name = read.string "displayName" Bank = read.int "gbt" * 1 Strength = read.int "strength" Inventory = read.intArray "inventory" }) |> Async.AwaitTask match player.FirstOrDefault() , List.tryHead user with | null , _ | _ , None -> return None | p , Some u -> let v = p.GetValue("Player") let playerData = mapBack u (v.ToBsonDocument()) return Some playerData } let insertNewPlayer (player : PlayerData) = async { do! BsonDocument("Player", player.ToBsonDocument()) |> players.InsertOneAsync |> Async.AwaitTask } let updatePlayer (player : PlayerData) = async { let filter = Builders.Filter.Eq("Player.DiscordId", player.DiscordId) let update = Builders.Update.Set("Player", player.ToBsonDocument()) do! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore } let addAchievement (id : uint64) (achievement : string) = async { let filter = Builders.Filter.Eq("Player.DiscordId", id) let update = Builders.Update.Push("Player.Achievements", achievement) return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore }