From 1fd98e04c6d6bcd67a4623ccb2dd393d76673040 Mon Sep 17 00:00:00 2001 From: Reya C Date: Fri, 4 Nov 2022 11:13:29 -0400 Subject: [PATCH] Beginnings of character creation commands. --- migrations/0001-characters.sql | 238 +++++++++++++++++------------ src/bin/bot.ts | 7 +- src/database/battle_types.ts | 18 +++ src/database/character_creation.ts | 64 ++++++++ src/database/database.ts | 65 +++++++- src/database/pronouns.ts | 22 +++ src/database/users.ts | 35 +++-- 7 files changed, 330 insertions(+), 119 deletions(-) create mode 100644 src/database/battle_types.ts create mode 100644 src/database/character_creation.ts create mode 100644 src/database/pronouns.ts diff --git a/migrations/0001-characters.sql b/migrations/0001-characters.sql index 40e9da5..f24d31c 100644 --- a/migrations/0001-characters.sql +++ b/migrations/0001-characters.sql @@ -13,130 +13,171 @@ CREATE TABLE IF NOT EXISTS users ); --rollback DROP TABLE IF EXISTS users; ---changeset reya:types_table runInTransaction:false -CREATE TABLE IF NOT EXISTS types +--changeset reya:battle_types_table runInTransaction:false +CREATE TABLE IF NOT EXISTS battle_types ( id INT NOT NULL PRIMARY KEY, - name STRING NOT NULL, + name STRING NOT NULL UNIQUE, + emoji STRING NOT NULL UNIQUE, color STRING NOT NULL, display_order INT NOT NULL, immunities INT[] NOT NULL DEFAULT '{}', resistances INT[] NOT NULL DEFAULT '{}', weaknesses INT[] NOT NULL DEFAULT '{}' ); ---rollback DROP TABLE IF EXISTS types; +--rollback DROP TABLE IF EXISTS battle_types; ---changeset reya:types_values runInTransaction:true -INSERT INTO types (id, name, color, display_order) -VALUES (0, 'Basic', '', 0), - (1, 'Sassy', '', 1), - (2, 'Gentle', '', 2), - (3, 'Sexy', '', 3), - (4, 'Muscle', '', 4), - (5, 'Glam', '', 5), - (6, 'Punk', '', 6), - (7, 'Glutton', '', 7), - (8, 'Dumb', '', 8), - (9, 'Drone', '', 9), - (10, 'Spooky', '', 10), - (11, 'Lively', '', 11), - (12, 'Smart', '', 12), - (13, 'Cool', '', 13), - (14, 'Bully', '', 14), - (15, 'Mythic', '', 15), - (16, 'Toy', '', 16), - (17, 'Cute', '', 17) +--changeset reya:battle_types_values runInTransaction:true +INSERT INTO battle_types (id, name, emoji, color, display_order) +VALUES (0, 'Basic', '🔲', '', 0), + (1, 'Sassy', '🔥', '', 1), + (2, 'Gentle', '💙', '', 2), + (3, 'Sexy', '💋', '', 3), + (4, 'Muscle', '💪', '', 4), + (5, 'Glam', '✨', '', 5), + (6, 'Punk', '🎸', '', 6), + (7, 'Glutton', '🍗', '', 7), + (8, 'Dumb', '🪨', '', 8), + (9, 'Drone', '🤖', '', 9), + (10, 'Spooky', '👻', '', 10), + (11, 'Lively', '🎉', '', 11), + (12, 'Smart', '🧠', '', 12), + (13, 'Cool', '🧊', '', 13), + (14, 'Bully', '😈', '', 14), + (15, 'Mythic', '🔱', '', 15), + (16, 'Toy', '🧸', '', 16), + (17, 'Cute', '🌺', '', 17) ON CONFLICT DO NOTHING; ---rollback DELETE FROM types WHERE id in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17) +--rollback DELETE FROM battle_types WHERE id in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17) --changeset reya:difficulties_table runInTransaction:false CREATE TABLE IF NOT EXISTS difficulties ( - id INT NOT NULL PRIMARY KEY, - name STRING NOT NULL UNIQUE, - description STRING NOT NULL, - display_order INT NOT NULL, - endo_only BOOLEAN NOT NULL DEFAULT false, - regenerate_talent BOOLEAN NOT NULL DEFAULT false, - lose_proficiency_percent FLOAT NOT NULL DEFAULT 0, - lose_exp_percent FLOAT NOT NULL DEFAULT 0, - lose_money_percent FLOAT NOT NULL DEFAULT 0, - allow_reformation BOOLEAN NOT NULL DEFAULT true + id INT NOT NULL PRIMARY KEY, + name STRING NOT NULL UNIQUE, + emoji STRING NOT NULL UNIQUE, + short_description STRING NOT NULL, + long_description STRING NOT NULL, + display_order INT NOT NULL, + allow_digestion BOOLEAN NOT NULL DEFAULT true, + regenerate_talent BOOLEAN NOT NULL DEFAULT false, + reformation_health_percent FLOAT NOT NULL DEFAULT 100, + reformation_stamina_percent FLOAT NOT NULL DEFAULT 100, + lose_proficiency_percent FLOAT NOT NULL DEFAULT 0, + lose_exp_percent FLOAT NOT NULL DEFAULT 0, + lose_money_percent FLOAT NOT NULL DEFAULT 0 ); --rollback DROP TABLE IF EXISTS difficulties; --changeset reya:difficulties_values runInTransaction:true INSERT INTO difficulties -(id, name, description, display_order, endo_only, regenerate_talent, lose_proficiency_percent, lose_exp_percent, - lose_money_percent, allow_reformation) -VALUES (0, 'Endo Only', 'You can never be digested. You always come out sooner or later.', 0, true, false, 0, 0, 0, - true), - (1, 'Very Safe', - 'You''ll be completely safe. The only downsides to being digested? Inconvenience. And smug predators.', 1, - false, false, 0, 0, 0, true), - (2, 'Safe', - 'Your noggin will get rattled around, sure, and you might drop some of your cash, but it''s a small price to pay for being able to come back.', - 2, false, true, 10, 0, 10, true), - (3, 'Risky', - 'Getting digested is going to mess with your head, for sure. You''ll come back a bit weakened from the experience, and your wallet will notice. This is the recommended difficulty.', - 3, false, true, 25, 10, 25, true), - (4, 'Dangerous', +(id, name, emoji, short_description, long_description, display_order, allow_digestion, regenerate_talent, + reformation_health_percent, reformation_stamina_percent, lose_proficiency_percent, lose_exp_percent, + lose_money_percent) +VALUES (0, 'Indigestible', '🔰', + 'You can''t be digested. Stomachs just make you sleepy, and you always come out none the worse for wear.', + 'Stomachs have no effect on you. When your health is reduced to 0, you simply become tired and drift off to ' + || + 'sleep. You don''t need to be reformed after battles when this happens, and you won''t lose anything for ' + || 'falling asleep this way. You''ll be ready to go right away as soon as you''re released!' + || e'\n\n' + || 'Suitable for players who play with the Pred Only or Spectator preferences, or who don''t want their ' + || 'character to die even temporarily.', + 0, false, false, 100, 100, 0, 0, 0), + (1, 'Very Safe', '☀', + 'You''ll be completely safe. The only downsides to being digested? Inconvenience. And smug predators.', + 'Reformation is a cakewalk for you. You can be digested, but you can reform after battle with no penalties ' + || 'of any kind. Your body and mind are completely unaffected by the visit to someone else''s gut.' + || e'\n\n' + || 'Reformation leaves you a little bit weary, but in good health.' + || e'\n\n' + || 'Suitable for players who prefer a digestive end, but don''t want to lose any progress.', + 1, true, false, 100, 90, 0, 0, 0), + (2, 'Safe', '🌤', + 'There''s a small price to being digested, but you didn''t think cheating death would be free, right?', + 'Digestion takes a little bit of a toll on your body and mind. And while you do come back, you don''t come ' + || 'back _quite_ the way you left.' + || 'Your proficiencies will drop by 10% as a little of what you''ve taken from your past exploits fades ' + || 'from you. You''ll also lose 10% of your money as a cost for the reformation process.' + || e'\n\n' + || 'Reformation leaves you a bit weakened and fairly drowsy. You should rest a bit before continuing.' + || e'\n\n' + || 'Suitable for players who want to add a little risk to being digested, but don''t want to lose levels.', + 2, true, false, 90, 75, 10, 0, 10), + (3, 'Risky', '🌥', + 'Reformation is a painful experience. It''s no laughing matter to be digested, so watch your back.', + 'Digestion is no walk in the park for you. When you come back, you leave some of yourself in the predator who ' + || 'turned you from a person to a snack. Your proficiencies drop by 25% as some of what you''ve taken from ' + || 'your past conquests fades from you, and your talents will be re-randomized, as the process of ' + || 'reformation is more art than science and your body and mind will not be quite the same. You''ll also ' + || 'lose 25% of your money as the cost of returning from being belly paunch. Worst of all, you''ll lose ' + || '10% of your experience points as your memories are clouded by your brush with fatality.' + || e'\n\n' + || '' + || e'\n\n' + || 'Suitable for players who want a balance between risk and maintaining their progress.', + 3, true, true, 25, 10, 25), + (4, 'Dangerous', '🌦', + 'Digestion pushes your', 'Digestion is something to be avoided at all costs. You''ll lose half your money, your stats will have atrophied, and you''ll lose some of your memories.', - 4, false, true, 50, 25, 50, true), - (5, 'Very Dangerous', - 'Being a meal is not just humiliating - it''s a nightmare. You''re lucky you get to hold on to anything.', 5, - false, true, 90, 50, 90, true), - (6, 'Extremely Dangerous', - 'If you get devoured and you don''t get out, you''ll lose just about everything. Be very, very careful...', 6, - false, true, 100, 100, 100, true) + 4, true, true, 50, 25, 50), + (5, 'Very Dangerous', '🌧', '', + 'Being a meal is not just humiliating - it''s a nightmare. You''re lucky you get to hold on to anything.', + 5, true, true, 90, 50, 90), + (6, 'Extremely Dangerous', '⛈', '', + 'If you get devoured and you don''t get out, you''ll lose just about everything. Be very, very careful...', + 6, true, true, 100, 100, 100) ON CONFLICT DO NOTHING; --rollback DELETE FROM difficulties WHERE id IN (0, 1, 2, 3, 4, 5, 6, 7); --changeset reya:preferences_table runInTransaction:false CREATE TABLE IF NOT EXISTS preferences ( - id INT NOT NULL PRIMARY KEY, - name STRING NOT NULL UNIQUE, - description STRING NOT NULL, - display_order INT NOT NULL, - can_use_vore BOOLEAN NOT NULL, - can_receive_vore BOOLEAN NOT NULL + id INT NOT NULL PRIMARY KEY, + name STRING NOT NULL UNIQUE, + emoji STRING NOT NULL UNIQUE, + short_description STRING NOT NULL, + long_description STRING NOT NULL, + display_order INT NOT NULL, + can_use_vore BOOLEAN NOT NULL, + can_receive_vore BOOLEAN NOT NULL ); --rollback DROP TABLE IF EXISTS preferences; --changeset reya:preferences_values runInTransaction:true -INSERT INTO preferences (id, name, description, display_order, can_use_vore, can_receive_vore) -VALUES (0, 'Observer', 'You can neither eat nor be eaten.', 0, false, false), - (1, 'Prey Only', 'You can only be eaten, not eat.', 1, false, true), - (2, 'Pred Only', 'You can only eat, not be eaten.', 2, true, false), - (3, 'Switch', 'You can both eat and be eaten.', 3, true, true) +INSERT INTO preferences (id, name, emoji, short_description, long_description, display_order, can_use_vore, + can_receive_vore) +VALUES (0, 'Observer', '', 'You can neither eat nor be eaten.', '', 0, false, false), + (1, 'Prey Only', '', 'You can only be eaten, not eat.', '', 1, false, true), + (2, 'Pred Only', '', 'You can only eat, not be eaten.', '', 2, true, false), + (3, 'Switch', '', 'You can both eat and be eaten.', '', 3, true, true) ON CONFLICT DO NOTHING; --rollback DELETE FROM preferences WHERE id IN (0, 1, 2, 3); ---changeset reya:genders_table runInTransaction:false -CREATE TABLE IF NOT EXISTS genders +--changeset reya:pronouns_table runInTransaction:false +CREATE TABLE IF NOT EXISTS pronouns ( - id INT NOT NULL PRIMARY KEY, - name STRING NOT NULL, - pronouns STRING NOT NULL UNIQUE, - display_order INT NOT NULL, - use_plural BOOLEAN NOT NULL, - subjective STRING NOT NULL, - adjective STRING NOT NULL, - possessive STRING NOT NULL, - reflexive STRING NOT NULL, - objective STRING NOT NULL + id INT NOT NULL PRIMARY KEY, + default_gender STRING NOT NULL, + pronouns STRING NOT NULL UNIQUE, + display_order INT NOT NULL, + use_plural BOOLEAN NOT NULL, + subjective STRING NOT NULL, + adjective STRING NOT NULL, + possessive STRING NOT NULL, + reflexive STRING NOT NULL, + objective STRING NOT NULL ); ---rollback DROP TABLE IF EXISTS genders; +--rollback DROP TABLE IF EXISTS pronouns; ---changeset reya:genders_values runInTransaction:true -INSERT INTO genders (id, name, pronouns, display_order, use_plural, subjective, adjective, possessive, reflexive, - objective) -VALUES (0, 'Non-binary', 'name only', 0, false, '@@', '@@''s', '@@''s', '@@''s self', '@@'), +--changeset reya:pronouns_values runInTransaction:true +INSERT INTO pronouns (id, default_gender, pronouns, display_order, use_plural, subjective, adjective, possessive, + reflexive, objective) +VALUES (0, 'Genderless', 'none', 0, false, '@@', '@@''s', '@@''s', '@@''s self', '@@'), (1, 'Female', 'she/her', 1, false, 'she', 'her', 'hers', 'herself', 'her'), (2, 'Non-binary', 'they/them', 2, true, 'they', 'their', 'theirs', 'themself', 'them'), (3, 'Male', 'he/him', 3, false, 'he', 'his', 'his', 'himself', 'him'), - (4, 'Object', 'it/its', 4, false, 'it', 'its', 'its', 'itself', 'it'), + (4, 'Genderless', 'it/its', 4, false, 'it', 'its', 'its', 'itself', 'it'), (5, 'Herm', 'shi/hir', 5, false, 'shi', 'hir', 'hirs', 'hirself', 'hir'), (6, 'Non-binary', 'ae/aer', 6, false, 'ae', 'aer', 'aers', 'aerself', 'aer'), (7, 'Non-binary', 'fae/faer', 7, false, 'fae', 'faer', 'faers', 'faerself', 'faer'), @@ -151,7 +192,7 @@ VALUES (0, 'Non-binary', 'name only', 0, false, '@@', '@@''s', '@@''s', '@@''s s (16, 'Non-binary', 'sie/sie', 16, false, 'sie', 'hir', 'hirs', 'hirself', 'sie'), (17, 'Non-binary', 'te/ter', 17, false, 'te', 'tem', 'ters', 'terself', 'ter') ON CONFLICT DO NOTHING; ---rollback DELETE FROM genders WHERE id IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); +--rollback DELETE FROM pronouns WHERE id IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); --changeset reya:characters_table runInTransaction:false CREATE TABLE IF NOT EXISTS characters @@ -162,11 +203,12 @@ CREATE TABLE IF NOT EXISTS characters discriminator INT NULL DEFAULT NULL, title STRING NOT NULL, profile STRING NOT NULL, - gender_id INT NOT NULL REFERENCES genders (id) ON DELETE RESTRICT, + pronouns_id INT NOT NULL REFERENCES pronouns (id) ON DELETE RESTRICT, + gender_name STRING NOT NULL, difficulty_id INT NOT NULL REFERENCES difficulties (id) ON DELETE RESTRICT, preference_id INT NOT NULL REFERENCES preferences (id) ON DELETE RESTRICT, - type1_id INT NOT NULL REFERENCES types (id) ON DELETE RESTRICT, - type2_id INT NOT NULL REFERENCES types (id) ON DELETE RESTRICT, + battle_type1_id INT NOT NULL REFERENCES battle_types (id) ON DELETE RESTRICT, + battle_type2_id INT NOT NULL REFERENCES battle_types (id) ON DELETE RESTRICT, experience INT NOT NULL DEFAULT 0, money INT NOT NULL DEFAULT 0, @@ -208,7 +250,8 @@ CREATE TABLE IF NOT EXISTS characters speed_proficiency INT NOT NULL DEFAULT 0, UNIQUE (user_id, name, discriminator), - FAMILY character_base (id, user_id, name, title, profile, gender_id, type1_id, type2_id, base_confidence, + FAMILY character_base (id, user_id, name, title, profile, pronouns_id, battle_type1_id, battle_type2_id, + base_confidence, base_health, base_stamina, base_brawn, base_durability, base_intensity, base_resilience, base_speed), FAMILY character_reformation_stats (min_confidence_talent, min_health_talent, min_stamina_talent, min_brawn_talent, @@ -230,11 +273,12 @@ CREATE TABLE IF NOT EXISTS character_creation name STRING NULL, title STRING NULL, profile STRING NULL, - gender_id INT NULL REFERENCES genders (id) ON DELETE RESTRICT, + pronouns_id INT NULL REFERENCES pronouns (id) ON DELETE RESTRICT, + gender_name STRING NULL, difficulty_id INT NULL REFERENCES difficulties (id) ON DELETE RESTRICT, preference_id INT NULL REFERENCES preferences (id) ON DELETE RESTRICT, - type1_id INT NULL REFERENCES types (id) ON DELETE RESTRICT, - type2_id INT NULL REFERENCES types (id) ON DELETE RESTRICT, + battle_type1_id INT NULL REFERENCES battle_types (id) ON DELETE RESTRICT, + battle_type2_id INT NULL REFERENCES battle_types (id) ON DELETE RESTRICT, base_confidence INT NULL DEFAULT 70, base_health INT NULL DEFAULT 70, base_stamina INT NULL DEFAULT 70, @@ -250,10 +294,12 @@ CREATE TABLE IF NOT EXISTS character_creation --changeset reya:userDefaultDifficultyPreferenceGender ALTER TABLE users - ADD COLUMN default_gender_id INT NULL REFERENCES genders (id) ON DELETE RESTRICT DEFAULT NULL + ADD COLUMN default_pronouns_id INT NULL REFERENCES pronouns (id) ON DELETE RESTRICT DEFAULT NULL CREATE IF NOT EXISTS FAMILY character_defaults, - ADD COLUMN default_difficulty_id INT NULL REFERENCES difficulties (id) ON DELETE RESTRICT DEFAULT NULL + ADD COLUMN default_gender_name STRING NULL DEFAULT NULL FAMILY character_defaults, - ADD COLUMN default_preference_id INT NULL REFERENCES preferences (id) ON DELETE RESTRICT DEFAULT NULL + ADD COLUMN default_difficulty_id INT NULL REFERENCES difficulties (id) ON DELETE RESTRICT DEFAULT NULL + FAMILY character_defaults, + ADD COLUMN default_preference_id INT NULL REFERENCES preferences (id) ON DELETE RESTRICT DEFAULT NULL FAMILY character_defaults; --rollback ALTER \ No newline at end of file diff --git a/src/bin/bot.ts b/src/bin/bot.ts index 89ff1e8..60a6bd2 100644 --- a/src/bin/bot.ts +++ b/src/bin/bot.ts @@ -5,7 +5,7 @@ import {Commands} from "../commands/index.js" import {checkIsRestart, reportFailed, reportReady, reportStarted} from "../ipc/restart.js" import {defaultPresence} from "../defaultPresence.js" import {Pool} from "pg" -import {Database, DatabaseImpl} from "../database/database.js" +import {Database, DatabaseImpl, makeTransactable} from "../database/database.js" async function bot() { await checkIsRestart() @@ -22,7 +22,10 @@ async function bot() { c.destroy() } - const db: Database = new DatabaseImpl(p.query.bind(p)) + const db: Database = new DatabaseImpl({ + query: p.query.bind(p), + transactable: makeTransactable(p), + }) const cmd = new Commands({users: db.users, cleanUp}) c.on("ready", async () => { diff --git a/src/database/battle_types.ts b/src/database/battle_types.ts new file mode 100644 index 0000000..3170810 --- /dev/null +++ b/src/database/battle_types.ts @@ -0,0 +1,18 @@ +export interface BattleType { + id: number, + name: string, + emoji: string, + color: string, + displayOrder: number, + immmunities: BattleTypeStub[] + resistances: BattleTypeStub[] + weaknesses: BattleTypeStub[] +} + +export interface BattleTypeStub { + id: number, + name: string, + emoji: string, + color: string, + displayOrder: number +} \ No newline at end of file diff --git a/src/database/character_creation.ts b/src/database/character_creation.ts new file mode 100644 index 0000000..544facf --- /dev/null +++ b/src/database/character_creation.ts @@ -0,0 +1,64 @@ +import {QueryType, Transactable} from "./database.js" +import {Snowflake} from "discord-api-types/globals.js" +import {Pronouns} from "./pronouns.js" + +export interface CharacterCreationState { + name: string | null + title: string | null + profile: string | null + gender: string | null + pronouns: Pronouns | null + difficulty: Difficulty | null + preference: Preference | null + type1: BattleType | null, + type2: BattleType | null, + baseConfidence: number | null, + baseHealth: number | null, + baseStamina: number | null, + baseBrawn: number | null, + baseDurability: number | null, + baseIntensity: number | null, + baseResilience: number | null, + baseSpeed: number | null, +} + +export interface CharacterCreationInput { + name?: string + title?: string + profile?: string + gender?: string + pronouns?: string + difficultyName?: string + preferenceName?: string + type1Name?: string + type2Name?: string + baseConfidence?: number + baseHealth?: number + baseStamina?: number + baseBrawn?: number + baseDurability?: number + baseIntensity?: number + baseResilience?: number + baseSpeed?: number +} + +export interface CharacterCreationTable { + startOrContinueCreatingCharacter(snowflake: Snowflake, + input: CharacterCreationInput): Promise +} + +export class CharacterCreationTableImpl implements CharacterCreationTable { + private readonly _query: QueryType + private readonly _transactable: Transactable + + constructor({query, transactable}: { query: QueryType, transactable: Transactable }) { + this._query = query + this._transactable = transactable + } + + async startOrContinueCreatingCharacter( + owner: Snowflake, + input: CharacterCreationInput): Promise { + + } +} \ No newline at end of file diff --git a/src/database/database.ts b/src/database/database.ts index bebbe99..2e2ef14 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,16 +1,71 @@ -import {Client} from "pg" +import {QueryResult, QueryResultRow} from "pg" import {UsersTable, UsersTableImpl} from "./users.js" +import {CharacterCreationTable, CharacterCreationTableImpl} from "./character_creation.js" + +export interface PoolLike { + connect(): Promise +} + +export interface PoolClientLike extends Queryable { + release(err?: boolean): void +} + +export function makeTransactable(p: PoolLike): Transactable { + return async function transactable(callback: (q: QueryType, attempts: number) => Promise): Promise { + const client = await p.connect() + const query = client.query.bind(client) + let committed = false + let attempts = 0 + while (!committed) { + try { + await client.query("BEGIN") + } catch (err) { + client.release() + throw err + } + try { + await callback(query, attempts) + } catch (err) { + try { + await client.query("ROLLBACK") + client.release() + } catch (err) { + client.release(true) + } + throw err + } + try { + await client.query("COMMIT") + committed = true + } catch (err) { + attempts += 1 + } + } + client.release() + } +} export interface Database { readonly users: UsersTable + readonly characterCreation: CharacterCreationTable } +export interface Queryable { + query(queryText: string): Promise> + + query(queryText: string, + params: ParamT): Promise> +} + +export type QueryType = Queryable["query"] +export type Transactable = (transaction: (client: QueryType, attempts: number) => Promise) => Promise + export class DatabaseImpl implements Database { readonly users: UsersTableImpl - private readonly _query: Client["query"] + readonly characterCreation: CharacterCreationTableImpl - constructor(query: Client["query"]) { - this._query = query - this.users = new UsersTableImpl(this._query) + constructor({query, transactable}: { query: QueryType, transactable: Transactable }) { + this.users = new UsersTableImpl({query}) + this.characterCreation = new CharacterCreationTableImpl({query, transactable}) } } \ No newline at end of file diff --git a/src/database/pronouns.ts b/src/database/pronouns.ts new file mode 100644 index 0000000..9c0a2ac --- /dev/null +++ b/src/database/pronouns.ts @@ -0,0 +1,22 @@ +export interface Pronouns { + id: number + defaultGender: string + pronouns: string + displayOrder: number + usePlural: boolean + subjective: string + adjective: string + possessive: string + reflexive: string + objective: string +} + +export interface PronounsAutocomplete { + pronouns: string + usePlural: boolean + subjective: string + adjective: string + possessive: string + reflexive: string + objective: string +} \ No newline at end of file diff --git a/src/database/users.ts b/src/database/users.ts index 286280d..c8b40ca 100644 --- a/src/database/users.ts +++ b/src/database/users.ts @@ -2,7 +2,7 @@ import {Snowflake} from "discord-api-types/globals.js" import {fast1a52 as fnvFast1a52} from "fnv-plus" import {parse as uuidParse, v1 as uuidV1, validate as uuidValidate, version as uuidVersion} from "uuid" import {SnowflakeUtil} from "discord.js" -import {Client} from "pg" +import {QueryType} from "./database.js" export interface UsersTable { getUuidForActiveSnowflake(snowflake: Snowflake): Promise @@ -13,25 +13,27 @@ export interface UsersTable { } export class UsersTableImpl implements UsersTable { - private readonly _query: Client["query"] + private readonly _query: QueryType - constructor(query: Client["query"]) { + constructor({query}: { query: QueryType }) { this._query = query } async getUuidForActiveSnowflake(snowflake: Snowflake): Promise { - const result = await this._query(`INSERT INTO users (id, is_admin, created_at, updated_at, active_at) - VALUES ($1, FALSE, now(), now(), now()) - ON CONFLICT (id) DO UPDATE SET active_at = now() - RETURNING id;`, [userSnowflakeToUuid(snowflake)]) + const result = await this._query<{ id: string }, [string]>( + `INSERT INTO users (id, is_admin, created_at, updated_at, active_at) + VALUES ($1, FALSE, now(), now(), now()) + ON CONFLICT (id) DO UPDATE SET active_at = now() + RETURNING id;`, [userSnowflakeToUuid(snowflake)]) return result.rows[0].id } async getActiveSnowflakeIsAdmin(snowflake: Snowflake): Promise { - const result = await this._query(`UPDATE users - SET active_at = NOW() - WHERE id = $1 - RETURNING is_admin;`, [userSnowflakeToUuid(snowflake)]) + const result = await this._query<{ is_admin: boolean }, [string]>( + `UPDATE users + SET active_at = NOW() + WHERE id = $1 + RETURNING is_admin;`, [userSnowflakeToUuid(snowflake)]) if (result.rowCount === 0) { return false } @@ -39,11 +41,12 @@ export class UsersTableImpl implements UsersTable { } async createBotOwnerAsAdmin(snowflake: Snowflake): Promise { - await this._query(`INSERT INTO users (id, is_admin, created_at, updated_at) - VALUES ($1, TRUE, now(), now()) - ON CONFLICT (id) DO UPDATE SET is_admin = TRUE, - updated_at = NOW() - WHERE users.is_admin = FALSE;`, [userSnowflakeToUuid(snowflake)]) + await this._query<{}, [string]>( + `INSERT INTO users (id, is_admin, created_at, updated_at) + VALUES ($1, TRUE, now(), now()) + ON CONFLICT (id) DO UPDATE SET is_admin = TRUE, + updated_at = NOW() + WHERE users.is_admin = FALSE;`, [userSnowflakeToUuid(snowflake)]) } }