Enable the bot to access database stuff!

So much database stuff. An absurd amount of database stuff.
main
Mari 8 months ago
parent d09c503add
commit 88018a0f16
  1. 10
      .env.example
  2. 0
      liquibase-changelog.yml
  3. 5
      liquibase.properties
  4. 180
      migrations/0001-characters.sql
  5. 223
      package-lock.json
  6. 13
      package.json
  7. 0
      src/bin/bot.spec.ts
  8. 74
      src/bin/bot.ts
  9. 79
      src/bin/migrate.ts
  10. 38
      src/commands/bot/index.ts
  11. 23
      src/commands/bot/rebuild.ts
  12. 23
      src/commands/bot/restart.ts
  13. 26
      src/commands/bot/shutdown.ts
  14. 65
      src/commands/character/create.ts
  15. 26
      src/commands/character/index.ts
  16. 6
      src/commands/index.spec.ts
  17. 72
      src/commands/index.ts
  18. 42
      src/commands/types.ts
  19. 16
      src/database/database.ts
  20. 10
      src/database/inmemory/database.ts
  21. 46
      src/database/inmemory/users.ts
  22. 110
      src/database/users.ts
  23. 6
      src/ipc/restart.ts
  24. 6
      src/types/interactions.ts

@ -1 +1,9 @@
DISCORD_TOKEN=
DISCORD_TOKEN=
BOT_OWNER_ID=
PGHOST=free-tier11.gcp-us-east1.cockroachlabs.cloud
PGPORT=26257
PGDATABASE=nomrpg-chesting
PGUSER=
PGPASSWORD=
PGSSLMODE=verify-full
PGOPTIONS=--cluster=deliciousreya-nom-rpg-2220

@ -1,7 +1,4 @@
# PostgreSQL
classpath: lib/postgresql-42.5.0.jar
driver: org.postgresql.Driver
url: jdbc:postgresql://free-tier11.gcp-us-east1.cockroachlabs.cloud:26257/nomrpg-chesting?options=--cluster%3Ddeliciousreya-nom-rpg-2220&sslmode=verify-full
username: reya
changeLogFile: configuration.yml
liquibase.hub.mode=off
liquibase.databaseClass=liquibase.database.core.CockroachDatabase

@ -3,9 +3,13 @@
--changeset reya:users_table runInTransaction:false
CREATE TABLE IF NOT EXISTS users
(
id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(),
discord_snowflake INT NOT NULL,
INDEX discord_users (discord_snowflake)
id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(),
is_admin BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
active_at TIMESTAMPTZ NULL DEFAULT NULL,
FAMILY essentials (id, is_admin),
FAMILY timestamps (created_at, updated_at, active_at)
);
--rollback DROP TABLE IF EXISTS users;
@ -41,14 +45,15 @@ VALUES (0, 'Basic', '', 0),
(14, 'Bully', '', 14),
(15, 'Mythic', '', 15),
(16, 'Toy', '', 16),
(17, 'Cute', '', 17);
(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)
--changeset reya:difficulties_table runInTransaction:false
CREATE TABLE IF NOT EXISTS difficulties
(
id INT NOT NULL PRIMARY KEY,
name STRING NOT NULL,
name STRING NOT NULL UNIQUE,
description STRING NOT NULL,
display_order INT NOT NULL,
endo_only BOOLEAN NOT NULL DEFAULT false,
@ -83,17 +88,15 @@ VALUES (0, 'Endo Only', 'You can never be digested. You always come out sooner o
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),
(7, 'Impossible',
'There is nothing to look forward to after digestion but an eternity on someone else''s thighs. When you''re digested, your life is over. There''s no coming back. Ever.',
7, false, true, 100, 100, 100, false);
false, true, 100, 100, 100, true)
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,
name STRING NOT NULL UNIQUE,
description STRING NOT NULL,
display_order INT NOT NULL,
can_use_vore BOOLEAN NOT NULL,
@ -106,7 +109,8 @@ INSERT INTO preferences (id, name, description, display_order, can_use_vore, can
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);
(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
@ -114,6 +118,7 @@ CREATE TABLE IF NOT EXISTS genders
(
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,
@ -125,33 +130,36 @@ CREATE TABLE IF NOT EXISTS genders
--rollback DROP TABLE IF EXISTS genders;
--changeset reya:genders_values runInTransaction:true
INSERT INTO genders (id, name, display_order, use_plural, subjective, adjective, possessive, reflexive, objective)
VALUES (0, 'Non-binary (name only)', 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'),
(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'),
(8, 'Non-binary (e/em)', 8, false, 'e', 'eir', 'eirs', 'emself', 'em'),
(9, 'Non-binary (ey/em)', 9, false, 'ey', 'eir', 'eirs', 'emself', 'em'),
(10, 'Non-binary (per/per)', 10, false, 'per', 'pers', 'pers', 'perself', 'per'),
(11, 'Non-binary (ve/ver)', 11, false, 've', 'vis', 'vis', 'verself', 'ver'),
(12, 'Non-binary (xe/xem)', 12, false, 'xe', 'xyr', 'xyrs', 'xemself', 'xem'),
(13, 'Non-binary (ze/hir)', 13, false, 'ze', 'hir', 'hirs', 'hirself', 'hir'),
(14, 'Non-binary (zie/hir)', 14, false, 'zie', 'hir', 'hirs', 'hirself', 'hir'),
(15, 'Non-binary (zie/zim)', 15, false, 'zie', 'zir', 'zis', 'zieself', 'zim'),
(16, 'Non-binary (sie/sie)', 16, false, 'sie', 'hir', 'hirs', 'hirself', 'sie'),
(17, 'Non-binary (te/ter)', 17, false, 'te', 'tem', 'ters', 'terself', 'ter');
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', '@@'),
(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'),
(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'),
(8, 'Non-binary', 'e/em', 8, false, 'e', 'eir', 'eirs', 'emself', 'em'),
(9, 'Non-binary', 'ey/em', 9, false, 'ey', 'eir', 'eirs', 'emself', 'em'),
(10, 'Non-binary', 'per/per', 10, false, 'per', 'pers', 'pers', 'perself', 'per'),
(11, 'Non-binary', 've/ver', 11, false, 've', 'vis', 'vis', 'verself', 'ver'),
(12, 'Non-binary', 'xe/xem', 12, false, 'xe', 'xyr', 'xyrs', 'xemself', 'xem'),
(13, 'Non-binary', 'ze/hir', 13, false, 'ze', 'hir', 'hirs', 'hirself', 'hir'),
(14, 'Non-binary', 'zie/hir', 14, false, 'zie', 'hir', 'hirs', 'hirself', 'hir'),
(15, 'Non-binary', 'zie/zim', 15, false, 'zie', 'zir', 'zis', 'zieself', 'zim'),
(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);
--changeset reya:characters_table runInTransaction:false
CREATE TABLE IF NOT EXISTS characters
(
id UUID NOT NULL PRIMARY KEY,
id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE,
name STRING NOT NULL,
discriminator INT NULL DEFAULT NULL,
title STRING NOT NULL,
profile STRING NOT NULL,
gender_id INT NOT NULL REFERENCES genders (id) ON DELETE RESTRICT,
@ -159,51 +167,51 @@ CREATE TABLE IF NOT EXISTS characters
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,
experience INT NOT NULL DEFAULT 0,
money INT NOT NULL DEFAULT 0,
experience INT NOT NULL DEFAULT 0,
money INT NOT NULL DEFAULT 0,
-- stats: base
base_confidence INT NOT NULL DEFAULT 70,
base_health INT NOT NULL DEFAULT 70,
base_stamina INT NOT NULL DEFAULT 70,
base_brawn INT NOT NULL DEFAULT 70,
base_durability INT NOT NULL DEFAULT 70,
base_intensity INT NOT NULL DEFAULT 70,
base_resilience INT NOT NULL DEFAULT 70,
base_speed INT NOT NULL DEFAULT 70,
base_confidence INT NOT NULL DEFAULT 70,
base_health INT NOT NULL DEFAULT 70,
base_stamina INT NOT NULL DEFAULT 70,
base_brawn INT NOT NULL DEFAULT 70,
base_durability INT NOT NULL DEFAULT 70,
base_intensity INT NOT NULL DEFAULT 70,
base_resilience INT NOT NULL DEFAULT 70,
base_speed INT NOT NULL DEFAULT 70,
-- stats: banked talent points
min_confidence_talent INT NOT NULL DEFAULT 0,
min_health_talent INT NOT NULL DEFAULT 0,
min_stamina_talent INT NOT NULL DEFAULT 0,
min_brawn_talent INT NOT NULL DEFAULT 0,
min_durability_talent INT NOT NULL DEFAULT 0,
min_intensity_talent INT NOT NULL DEFAULT 0,
min_resilience_talent INT NOT NULL DEFAULT 0,
min_speed_talent INT NOT NULL DEFAULT 0,
min_confidence_talent INT NOT NULL DEFAULT 0,
min_health_talent INT NOT NULL DEFAULT 0,
min_stamina_talent INT NOT NULL DEFAULT 0,
min_brawn_talent INT NOT NULL DEFAULT 0,
min_durability_talent INT NOT NULL DEFAULT 0,
min_intensity_talent INT NOT NULL DEFAULT 0,
min_resilience_talent INT NOT NULL DEFAULT 0,
min_speed_talent INT NOT NULL DEFAULT 0,
-- stats: current talent points
confidence_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_confidence_talent + floor(random() * (32 - min_confidence_talent)),
health_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_health_talent + floor(random() * (32 - min_health_talent)),
stamina_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_stamina_talent + floor(random() * (32 - min_stamina_talent)),
brawn_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_brawn_talent + floor(random() * (32 - min_brawn_talent)),
durability_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_durability_talent + floor(random() * (32 - min_durability_talent)),
intensity_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_intensity_talent + floor(random() * (32 - min_intensity_talent)),
resilience_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_resilience_talent + floor(random() * (32 - min_resilience_talent)),
speed_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_speed_talent + floor(random() * (32 - min_speed_talent)),
confidence_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_confidence_talent + floor(random() * (32 - min_confidence_talent)),
health_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_health_talent + floor(random() * (32 - min_health_talent)),
stamina_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_stamina_talent + floor(random() * (32 - min_stamina_talent)),
brawn_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_brawn_talent + floor(random() * (32 - min_brawn_talent)),
durability_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_durability_talent + floor(random() * (32 - min_durability_talent)),
intensity_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_intensity_talent + floor(random() * (32 - min_intensity_talent)),
resilience_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_resilience_talent + floor(random() * (32 - min_resilience_talent)),
speed_talent INT NOT NULL DEFAULT floor(random() * 32)::INT, --min_speed_talent + floor(random() * (32 - min_speed_talent)),
-- stats: current proficiency points
confidence_proficiency INT NOT NULL DEFAULT 0,
health_proficiency INT NOT NULL DEFAULT 0,
stamina_proficiency INT NOT NULL DEFAULT 0,
brawn_proficiency INT NOT NULL DEFAULT 0,
durability_proficiency INT NOT NULL DEFAULT 0,
intensity_proficiency INT NOT NULL DEFAULT 0,
resilience_proficiency INT NOT NULL DEFAULT 0,
speed_proficiency INT NOT NULL DEFAULT 0,
confidence_proficiency INT NOT NULL DEFAULT 0,
health_proficiency INT NOT NULL DEFAULT 0,
stamina_proficiency INT NOT NULL DEFAULT 0,
brawn_proficiency INT NOT NULL DEFAULT 0,
durability_proficiency INT NOT NULL DEFAULT 0,
intensity_proficiency INT NOT NULL DEFAULT 0,
resilience_proficiency INT NOT NULL DEFAULT 0,
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,
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,
FAMILY character_reformation_stats (min_confidence_talent, min_health_talent, min_stamina_talent, min_brawn_talent,
min_durability_talent, min_intensity_talent, min_resilience_talent,
min_speed_talent,
confidence_talent, health_talent, stamina_talent, brawn_talent,
@ -212,4 +220,40 @@ CREATE TABLE IF NOT EXISTS characters
health_proficiency, stamina_proficiency, brawn_proficiency, durability_proficiency,
intensity_proficiency, resilience_proficiency, speed_proficiency)
);
--rollback DROP TABLE IF EXISTS characters_table;
--rollback DROP TABLE IF EXISTS characters;
--changeset reya:character_creation_table runInTransaction:false
CREATE TABLE IF NOT EXISTS character_creation
(
user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE,
character_id UUID NULL REFERENCES characters (id) ON DELETE CASCADE,
name STRING NULL,
title STRING NULL,
profile STRING NULL,
gender_id INT NULL REFERENCES genders (id) ON DELETE RESTRICT,
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,
base_confidence INT NULL DEFAULT 70,
base_health INT NULL DEFAULT 70,
base_stamina INT NULL DEFAULT 70,
base_brawn INT NULL DEFAULT 70,
base_durability INT NULL DEFAULT 70,
base_intensity INT NULL DEFAULT 70,
base_resilience INT NULL DEFAULT 70,
base_speed INT NULL DEFAULT 70,
PRIMARY KEY (user_id, character_id)
);
--rollback DROP TABLE IF EXISTS character_creation;
--changeset reya:userDefaultDifficultyPreferenceGender
ALTER TABLE users
ADD COLUMN default_gender_id INT NULL REFERENCES genders (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
FAMILY character_defaults,
ADD COLUMN default_preference_id INT NULL REFERENCES preferences (id) ON DELETE RESTRICT DEFAULT NULL
FAMILY character_defaults;
--rollback ALTER

223
package-lock.json generated

@ -10,11 +10,15 @@
"license": "ISC",
"dependencies": {
"@reduxjs/toolkit": "^1.8.5",
"@types/fnv-plus": "^1.3.0",
"@types/uuid": "^8.3.4",
"discord-api-types": "^0.37.12",
"discord.js": "^14.5.0",
"dotenv": "^16.0.3",
"fnv-plus": "^1.3.1",
"liquibase": "^4.4.0",
"ts-postgres": "^1.3.0"
"pg": "^8.8.0",
"uuid": "^9.0.0"
},
"devDependencies": {
"@babel/cli": "^7.19.3",
@ -2429,6 +2433,11 @@
"@babel/types": "^7.3.0"
}
},
"node_modules/@types/fnv-plus": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/fnv-plus/-/fnv-plus-1.3.0.tgz",
"integrity": "sha512-ijls8MsO6Q9JUSd5w1v4y2ijM6S4D/nmOyI/FwcepvrZfym0wZhLdYGFD5TJID7tga0O3I7SmtK69RzpSJ1Fcw=="
},
"node_modules/@types/graceful-fs": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
@ -2490,6 +2499,11 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
"dev": true
},
"node_modules/@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="
},
"node_modules/@types/ws": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
@ -2833,6 +2847,14 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"node_modules/buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
"engines": {
"node": ">=4"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@ -3352,6 +3374,11 @@
"node": ">=8"
}
},
"node_modules/fnv-plus": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/fnv-plus/-/fnv-plus-1.3.1.tgz",
"integrity": "sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw=="
},
"node_modules/fs-readdir-recursive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
@ -4701,6 +4728,11 @@
"node": ">=6"
}
},
"node_modules/packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
},
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -4764,26 +4796,61 @@
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/pg": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz",
"integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==",
"dependencies": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.5.0",
"pg-pool": "^3.5.2",
"pg-protocol": "^1.5.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
},
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-connection-string": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
"integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"dev": true,
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz",
"integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==",
"dev": true
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dev": true,
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
@ -4795,6 +4862,14 @@
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@ -4847,7 +4922,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"dev": true,
"engines": {
"node": ">=4"
}
@ -4856,7 +4930,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -4865,7 +4938,6 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -4874,7 +4946,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dev": true,
"dependencies": {
"xtend": "^4.0.0"
},
@ -5197,6 +5268,14 @@
"source-map": "^0.6.0"
}
},
"node_modules/split2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
"integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -5551,22 +5630,6 @@
}
}
},
"node_modules/ts-postgres": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ts-postgres/-/ts-postgres-1.3.0.tgz",
"integrity": "sha512-YQY6omZM9RiMeJpzyVn36ankicZnTbSTkHaq+NTkqrLHSRYigsNW9JsPwrZXxLt1es3mV+V6/VUj0eOVcYXq1g==",
"dependencies": {
"ts-typed-events": "^3.0.0"
},
"engines": {
"node": ">=10.7.0"
}
},
"node_modules/ts-typed-events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ts-typed-events/-/ts-typed-events-3.0.0.tgz",
"integrity": "sha512-+2FZ0XPX+UPR7PO8ZQjuvnuDMYRhzrDaCRaNHaBG1xSL//0oPa3XMU5yxgDTzW67VzkE33fQpx1YxWBdkaF7Zw=="
},
"node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
@ -5688,6 +5751,14 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
@ -5792,7 +5863,6 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true,
"engines": {
"node": ">=0.4"
}
@ -7602,6 +7672,11 @@
"@babel/types": "^7.3.0"
}
},
"@types/fnv-plus": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/fnv-plus/-/fnv-plus-1.3.0.tgz",
"integrity": "sha512-ijls8MsO6Q9JUSd5w1v4y2ijM6S4D/nmOyI/FwcepvrZfym0wZhLdYGFD5TJID7tga0O3I7SmtK69RzpSJ1Fcw=="
},
"@types/graceful-fs": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
@ -7663,6 +7738,11 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
"dev": true
},
"@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="
},
"@types/ws": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
@ -7924,6 +8004,11 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
},
"busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@ -8303,6 +8388,11 @@
"path-exists": "^4.0.0"
}
},
"fnv-plus": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/fnv-plus/-/fnv-plus-1.3.1.tgz",
"integrity": "sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw=="
},
"fs-readdir-recursive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
@ -9307,6 +9397,11 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
},
"parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -9348,23 +9443,45 @@
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz",
"integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A=="
},
"pg": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz",
"integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==",
"requires": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.5.0",
"pg-pool": "^3.5.2",
"pg-protocol": "^1.5.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
}
},
"pg-connection-string": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
"integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
},
"pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"dev": true
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
},
"pg-pool": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz",
"integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==",
"requires": {}
},
"pg-protocol": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==",
"dev": true
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
},
"pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dev": true,
"requires": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
@ -9373,6 +9490,14 @@
"postgres-interval": "^1.1.0"
}
},
"pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"requires": {
"split2": "^4.1.0"
}
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@ -9409,26 +9534,22 @@
"postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"dev": true
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
},
"postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"dev": true
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
},
"postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"dev": true
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
},
"postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dev": true,
"requires": {
"xtend": "^4.0.0"
}
@ -9680,6 +9801,11 @@
"source-map": "^0.6.0"
}
},
"split2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
"integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ=="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -9901,19 +10027,6 @@
"yn": "3.1.1"
}
},
"ts-postgres": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ts-postgres/-/ts-postgres-1.3.0.tgz",
"integrity": "sha512-YQY6omZM9RiMeJpzyVn36ankicZnTbSTkHaq+NTkqrLHSRYigsNW9JsPwrZXxLt1es3mV+V6/VUj0eOVcYXq1g==",
"requires": {
"ts-typed-events": "^3.0.0"
}
},
"ts-typed-events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ts-typed-events/-/ts-typed-events-3.0.0.tgz",
"integrity": "sha512-+2FZ0XPX+UPR7PO8ZQjuvnuDMYRhzrDaCRaNHaBG1xSL//0oPa3XMU5yxgDTzW67VzkE33fQpx1YxWBdkaF7Zw=="
},
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
@ -9988,6 +10101,11 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
},
"v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
@ -10059,8 +10177,7 @@
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
"y18n": {
"version": "5.0.8",

@ -3,21 +3,26 @@
"version": "0.0.0",
"description": "Vore RPG core",
"private": true,
"main": "build/index.js",
"main": "build/bin/bot.js",
"scripts": {
"build": "tsc",
"build": "tsc && babel src --out-dir=build --extensions=.ts",
"migrate": "node build/bin/migrate.js",
"start": "node build/bin/bot.js",
"test": "jest"
},
"author": "",
"license": "ISC",
"type": "module",
"dependencies": {
"@reduxjs/toolkit": "^1.8.5",
"@types/fnv-plus": "^1.3.0",
"@types/uuid": "^8.3.4",
"discord-api-types": "^0.37.12",
"discord.js": "^14.5.0",
"dotenv": "^16.0.3",
"fnv-plus": "^1.3.1",
"liquibase": "^4.4.0",
"ts-postgres": "^1.3.0"
"pg": "^8.8.0",
"uuid": "^9.0.0"
},
"devDependencies": {
"@babel/cli": "^7.19.3",

@ -1,17 +1,37 @@
import {BaseInteraction, Client} from "discord.js"
import {config} from "dotenv"
import {isChatInputCommand} from "./types/interactions"
import {commandDefinitions, executeCommand, storeCachedCommands} from "./commands/index"
import {checkIsRestart, reportFailed, reportReady, reportStarted} from "./ipc/restart"
import {defaultPresence} from "./defaultPresence"
import {isAutocomplete, isChatInputCommand} from "../types/interactions.js"
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"
async function main() {
async function bot() {
await checkIsRestart()
config()
const c = new Client({
intents: [],
})
const p = new Pool({
application_name: "VoreRPG Bot",
})
async function cleanUp() {
await p.end()
c.destroy()
}
const db: Database = new DatabaseImpl(p.query.bind(p))
const cmd = new Commands({users: db.users, cleanUp})
c.on("ready", async () => {
try {
await db.users.createBotOwnerAsAdmin(process.env.BOT_OWNER_ID || "")
} catch (ex) {
await reportFailed(c, ex)
return
}
const app = c.application
if (!app) {
c.destroy()
@ -19,10 +39,10 @@ async function main() {
return
} else {
try {
storeCachedCommands(await app.commands.set(commandDefinitions))
cmd.setCache(await app.commands.set(cmd.definitions))
const g = await c.guilds.fetch()
for (const guild of g.values()) {
storeCachedCommands(await app.commands.set(commandDefinitions, guild.id))
cmd.setCache(await app.commands.set(cmd.definitions, guild.id))
}
} catch (ex) {
c.destroy()
@ -64,28 +84,44 @@ async function main() {
}
if (isChatInputCommand(ev)) {
try {
await executeCommand(ev)
await cmd.execute(ev)
} catch (ex) {
console.log("failed executing command", ev, ex)
if (!ev.replied) {
try {
await ev.reply({
ephemeral: true,
content: "Uuguuu... I can't think straight... try again later, 'kay?",
})
} catch (innerEx) {
console.log("failed sending error response", innerEx)
}
}
if (!ev.replied) {
console.log("never replied to command", ev)
try {
await ev.reply({
ephemeral: true,
content: "Uuguuu... I can't think straight... try again later, 'kay?",
})
} catch (ex) {
console.log("failed sending error reply", ex)
}
}
} else if (isAutocomplete(ev)) {
try {
await cmd.autocomplete(ev)
} catch (ex) {
console.log("failed autocompleting for command", ev, ex)
}
if (!ev.responded) {
console.log("never autocompleted for command", ev)
try {
await ev.respond([])
} catch (ex) {
console.log("failed sending error response", ex)
}
}
} else if (ev.isRepliable()) {
try {
console.log("unknown command", ev)
await ev.reply({
ephemeral: true,
content: "Huuuuuuuh? ... I don't know what to do with that yet.",
})
} catch (ex) {
console.log("failed sending unknown command response", ex)
console.log("failed sending unknown command reply", ex)
}
} else {
console.log("got an interaction but can't reply to it")
@ -95,6 +131,6 @@ async function main() {
await c.login(process.env.DISCORD_TOKEN || "")
}
main().catch((ex) => {
bot().catch((ex) => {
console.log("main thread failed", ex)
})

@ -0,0 +1,79 @@
import {config} from "dotenv"
import {Liquibase, LiquibaseLogLevels} from "liquibase"
import {resolve} from "path"
interface PostgresEnvVars {
host?: string
port?: string
db?: string
sslmode?: string
options?: string
user?: string
pass?: string
}
async function migrate() {
config()
const vars: PostgresEnvVars = {}
for (const variable of Object.keys(process.env)) {
const value = process.env[variable] || ""
if (variable.startsWith("PG")) {
switch (variable) {
case "PGHOST":
vars.host = value
break
case "PGPORT":
vars.port = value
break
case "PGDATABASE":
vars.db = value
break
case "PGUSER":
vars.user = value
break
case "PGPASSWORD":
vars.pass = value
break
case "PGOPTIONS":
vars.options = value
break
case "PGSSLMODE":
vars.sslmode = value
break
default:
console.log("Unknown PG* environment variable: " + variable)
}
}
}
const params = []
params.push(`ApplicationName=${encodeURIComponent("VoreRPG Migration")}`)
if (vars.options) {
params.push(`options=${encodeURIComponent(vars.options)}`)
}
if (vars.sslmode) {
params.push(`sslmode=${encodeURIComponent(vars.sslmode)}`)
}
const liquibase = new Liquibase({
changeLogFile: "liquibase-changelog.yml",
classpath: resolve("lib/postgresql-42.5.0.jar"),
liquibase: resolve("node_modules/liquibase/dist/liquibase/liquibase"),
liquibasePropertiesFile: resolve("liquibase.properties"),
username: vars.user || process.env.USER || process.env.USERNAME || "postgres",
password: vars.pass || "",
logLevel: LiquibaseLogLevels.Debug,
url: `jdbc:postgresql://`
+ `${vars.host ? encodeURIComponent(vars.host) : ""}${vars.port ? `:${encodeURIComponent(vars.port)}` : ""}`
+ `${vars.db ? `/${encodeURIComponent(vars.db)}` : ""}${params.length > 0 ? `?${params.join("&")}` : ""}`,
})
try {
await liquibase.update({})
} catch (err) {
console.log("Liquibase failed")
console.log(err)
}
}
migrate().catch((err) => {
console.log("Main thread failed", err)
})

@ -1,21 +1,31 @@
import {BaseChatInputCommandData, CommandWithSubcommandsData} from "../types"
import {BaseChatInputCommandData, CommandWithSubcommandsData, SubcommandData} from "../types"
import {ApplicationCommandType} from "discord.js"
import {commandBotRestart} from "./restart"
import {commandBotShutdown} from "./shutdown"
import {commandBotRebuild} from "./rebuild"
import {BotRestartCommand} from "./restart"
import {BotShutdownCommand} from "./shutdown"
import {BotRebuildCommand} from "./rebuild"
import {UsersTable} from "../../database/users.js"
export class BotCommand extends CommandWithSubcommandsData {
readonly rebuild: BotRebuildCommand
readonly restart: BotRestartCommand
readonly shutdown: BotShutdownCommand
readonly subcommands: SubcommandData[]
constructor({users, cleanUp}: { users: UsersTable, cleanUp: () => Promise<void> }) {
super()
this.rebuild = new BotRebuildCommand({users, cleanUp})
this.restart = new BotRestartCommand({users, cleanUp})
this.shutdown = new BotShutdownCommand({users, cleanUp})
this.subcommands = [
this.rebuild,
this.restart,
this.shutdown,
]
}
class BotCommandData extends CommandWithSubcommandsData {
readonly baseDefinition: BaseChatInputCommandData = {
name: "bot",
type: ApplicationCommandType.ChatInput,
description: "Commands to manage the bot's status.",
}
readonly subcommands = [
commandBotRebuild,
commandBotRestart,
commandBotShutdown,
]
}
export const commandBot = new BotCommandData()
}

@ -1,4 +1,4 @@
import {adminId, SubcommandData} from "../types"
import {SubcommandData} from "../types"
import {
ActivityType,
ApplicationCommandOptionType,
@ -9,8 +9,18 @@ import {wrappedRestart} from "../../ipc/restart"
import {fork} from "child_process"
import {defaultPresence} from "../../defaultPresence"
import {resolve as resolvePath} from "path"
import {UsersTable} from "../../database/users.js"
export class BotRebuildCommand extends SubcommandData {
private readonly _users: UsersTable
private readonly _cleanUp: () => Promise<void>
constructor({users, cleanUp}: { users: UsersTable, cleanUp(): Promise<void> }) {
super()
this._users = users
this._cleanUp = cleanUp
}
class RebuildCommand extends SubcommandData {
readonly definition: ApplicationCommandSubCommandData = {
name: "rebuild",
description: "Rebuilds and restarts the bot.",
@ -18,8 +28,7 @@ class RebuildCommand extends SubcommandData {
}
async execute(b: ChatInputCommandInteraction): Promise<void> {
const user = b.user || b.member
if (!!user && user.id === adminId) {
if (await this._users.getActiveSnowflakeIsAdmin(b.user.id)) {
await b.reply({
content: "I dunno... Let's check if this will work...",
ephemeral: true,
@ -159,7 +168,7 @@ class RebuildCommand extends SubcommandData {
ephemeral: true,
content: "Phewwww... now I'll just... take a quick nap after all that hard work...",
})
await wrappedRestart(b)
await wrappedRestart(b, this._cleanUp)
} else {
await b.reply({
ephemeral: true,
@ -167,6 +176,4 @@ class RebuildCommand extends SubcommandData {
})
}
}
}
export const commandBotRebuild = new RebuildCommand()
}

@ -1,4 +1,4 @@
import {adminId, SubcommandData} from "../types"
import {SubcommandData} from "../types"
import {
ActivityType,
ApplicationCommandOptionType,
@ -6,8 +6,18 @@ import {
ChatInputCommandInteraction,
} from "discord.js"
import {wrappedRestart} from "../../ipc/restart"
import {UsersTable} from "../../database/users.js"
export class BotRestartCommand extends SubcommandData {
private readonly _users: UsersTable
private readonly _cleanUp: () => Promise<void>
constructor({users, cleanUp}: { users: UsersTable, cleanUp(): Promise<void> }) {
super()
this._users = users
this._cleanUp = cleanUp
}
class RestartCommand extends SubcommandData {
readonly definition: ApplicationCommandSubCommandData = {
name: "restart",
type: ApplicationCommandOptionType.Subcommand,
@ -15,8 +25,7 @@ class RestartCommand extends SubcommandData {
}
async execute(b: ChatInputCommandInteraction): Promise<void> {
const user = b.user || b.member
if (!!user && user.id === adminId) {
if (await this._users.getActiveSnowflakeIsAdmin(b.user.id)) {
await b.reply({
ephemeral: true,
content: "Yaaaawwn... Okay... Just a quick nap then...",
@ -32,7 +41,7 @@ class RestartCommand extends SubcommandData {
},
],
})
await wrappedRestart(b)
await wrappedRestart(b, this._cleanUp)
} else {
await b.reply({
ephemeral: true,
@ -40,6 +49,4 @@ class RestartCommand extends SubcommandData {
})
}
}
}
export const commandBotRestart = new RestartCommand()
}