diff --git a/.gitignore b/.gitignore index 7f7267b..3e834c1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ !/src/commands/bots-playground/command.template.ts /node_modules/ /src/config.ts +/supabase/seed.sql build/ package-lock.json diff --git a/README.md b/README.md index b3ed99a..896e573 100644 --- a/README.md +++ b/README.md @@ -22,24 +22,31 @@ Bring Hans to your Discord server and start using his available features right a The list of commands & plugins can be found [here 🔗](https://github.com/en3sis/hans/wiki/Commands-&-Plugins) -### Developing Hans +## Developing Hans > 🪬 **NOTE**: Please consider opening an issue and PR for bugs, suggestions or new features. --- -## 🔅 Prepare environment +### 🔅 Prepare environment -Before running any command, run `npm install` & `cp .env.template .env`, fill all the env variables needed. To create your application, visit [Discord's Developer Portal](https://discord.com/developers/docs/intro) +Before running any command, run `npm install && cp .env.template .env`, fill all the env variables needed. To create your application, visit [Discord's Developer Portal](https://discord.com/developers/docs/intro) > 🪬 **IMPORTANT**: A Supabase instance is needed for the bot to work. A free cluster should be more than enough (even for small bots & communities) for development. -There's a `supabase/schema.sql` file with the necessary tables and columns needed for the bot to work. You can use it to create the tables in your Supabase instance. +### Supabase Local Development. + +Supabase is used for storing the bot's configs, guilds, and users. + +You can work with supabase local, just follow the instructions [here 🔗](https://supabase.io/docs/guides/local-development). +Once you run `supabase start` the local supabase will be populate with the latest schema (have a look at the `supabase/seed.template.sql` file for more configuration) More information related to working with Supabase local development can be found [📹 here 🔗](https://www.youtube.com/watch?v=N0Wb85m3YMI) ## 👩🏼‍💻 Development +Once the `Prepare environment` section is done, you can start developing your bot. + ### `npm run dev` Will start a development server with `ts-node` and `nodemon` for livereload. A bot Invite link will be displayed in the console. Use it to invite the bot to your server. diff --git a/src/controllers/plugins/timezones.controller.ts b/src/controllers/plugins/timezones.controller.ts index 07b54f7..c1d50b5 100644 --- a/src/controllers/plugins/timezones.controller.ts +++ b/src/controllers/plugins/timezones.controller.ts @@ -8,6 +8,7 @@ import { TIME_ZONES_REGEX } from '../../utils/regex' export const timezonesController = async (interaction: ChatInputCommandInteraction) => { try { const command = interaction.options.getSubcommand() + if (command === 'set') { const timezone = interaction.options.getString('zone', true).trim() @@ -59,6 +60,15 @@ export const timezonesController = async (interaction: ChatInputCommandInteracti }, ], }) + } else if (command === 'unset') { + // Deletes the user's timezone configuration from the database + await supabase + .from('users_settings') + .delete() + .eq('user_id', interaction.user.id) + .eq('type', 'timezone') + + await interaction.editReply({ content: 'Your timezone has been unset.' }) } else if (command === 'diff') { // Get the target & author user const targetUser = interaction.options.getUser('user', true) @@ -92,7 +102,9 @@ export const timezonesController = async (interaction: ChatInputCommandInteracti embeds: [ { title: '🕑 Timezone difference', - description: `**${targetUser.username}** is currently **${timeDifference} ${ + description: `**${ + targetUser.displayName ?? targetUser.username + }** is currently **${timeDifference} ${ targetTimezoneIsInFuture ? 'ahead' : 'behind' }** with his local time being **${extractHours( getTimeZones.targetLocalTime, @@ -104,7 +116,7 @@ export const timezonesController = async (interaction: ChatInputCommandInteracti inline: true, }, { - name: `${targetUser.username}'s timezone`, + name: `${targetUser.displayName ?? targetUser.username}'s timezone`, value: `${targetUserTimezone} (${extractHours(getTimeZones.targetLocalTime)})`, inline: true, }, diff --git a/supabase/.gitignore b/supabase/.gitignore index 773c7c3..a207596 100644 --- a/supabase/.gitignore +++ b/supabase/.gitignore @@ -1,3 +1,4 @@ # Supabase .branches .temp +.seed.sql diff --git a/supabase/schema.sql b/supabase/migrations/20230913081518_current.sql similarity index 57% rename from supabase/schema.sql rename to supabase/migrations/20230913081518_current.sql index f9d6e98..8d15db0 100644 --- a/supabase/schema.sql +++ b/supabase/migrations/20230913081518_current.sql @@ -18,8 +18,8 @@ create table "public"."configs" ( "monitoring_channel_id" text ); -alter table - "public"."configs" enable row level security; + +alter table "public"."configs" enable row level security; create table "public"."guilds" ( "id" integer generated by default as identity not null, @@ -30,8 +30,8 @@ create table "public"."guilds" ( "created_at" text ); -alter table - "public"."guilds" enable row level security; + +alter table "public"."guilds" enable row level security; create table "public"."guilds_plugins" ( "id" integer generated by default as identity not null, @@ -42,8 +42,8 @@ create table "public"."guilds_plugins" ( "created_at" text ); -alter table - "public"."guilds_plugins" enable row level security; + +alter table "public"."guilds_plugins" enable row level security; create table "public"."plugins" ( "id" integer generated by default as identity not null, @@ -51,13 +51,12 @@ create table "public"."plugins" ( "description" text, "enabled" boolean, "premium" boolean, - "category" text default 'miscellaneous' :: text, + "category" text default 'miscellaneous'::text, "created_at" text ); -alter table - "public"."plugins" enable row level security; +alter table "public"."plugins" enable row level security; create table "public"."users_settings" ( "id" bigint generated by default as identity not null, @@ -67,11 +66,8 @@ create table "public"."users_settings" ( "type" user_settings_type ); -alter table "public"."users_settings" enable row level security; - -CREATE UNIQUE INDEX users_settings_pkey ON public.users_settings USING btree (id, user_id); -alter table "public"."users_settings" add constraint "users_settings_pkey" PRIMARY KEY using index "users_settings_pkey"; +alter table "public"."users_settings" enable row level security; CREATE UNIQUE INDEX configs_pkey ON public.configs USING btree (id); @@ -85,57 +81,36 @@ CREATE UNIQUE INDEX plugins_name_key ON public.plugins USING btree (name); CREATE UNIQUE INDEX plugins_pkey ON public.plugins USING btree (id); -alter table - "public"."configs" -add - constraint "configs_pkey" PRIMARY KEY using index "configs_pkey"; - -alter table - "public"."guilds" -add - constraint "guilds_pkey" PRIMARY KEY using index "guilds_pkey"; - -alter table - "public"."guilds_plugins" -add - constraint "guilds_plugins_pkey" PRIMARY KEY using index "guilds_plugins_pkey"; - -alter table - "public"."plugins" -add - constraint "plugins_pkey" PRIMARY KEY using index "plugins_pkey"; - -alter table - "public"."guilds" -add - constraint "guilds_guild_id_key" UNIQUE using index "guilds_guild_id_key"; - -alter table - "public"."guilds_plugins" -add - constraint "guilds_plugins_name_fkey" FOREIGN KEY (name) REFERENCES plugins(name) not valid; - -alter table - "public"."guilds_plugins" validate constraint "guilds_plugins_name_fkey"; - -alter table - "public"."guilds_plugins" -add - constraint "guilds_plugins_owner_fkey" FOREIGN KEY (owner) REFERENCES guilds(guild_id) ON -DELETE - CASCADE not valid; - -alter table - "public"."guilds_plugins" validate constraint "guilds_plugins_owner_fkey"; - -alter table - "public"."plugins" -add - constraint "plugins_name_key" UNIQUE using index "plugins_name_key"; - --- Functions -CREATE -OR REPLACE FUNCTION reset_chat_gpt_plugin() RETURNS void AS $$ BEGIN +CREATE UNIQUE INDEX users_settings_pkey ON public.users_settings USING btree (id, user_id); + +alter table "public"."configs" add constraint "configs_pkey" PRIMARY KEY using index "configs_pkey"; + +alter table "public"."guilds" add constraint "guilds_pkey" PRIMARY KEY using index "guilds_pkey"; + +alter table "public"."guilds_plugins" add constraint "guilds_plugins_pkey" PRIMARY KEY using index "guilds_plugins_pkey"; + +alter table "public"."plugins" add constraint "plugins_pkey" PRIMARY KEY using index "plugins_pkey"; + +alter table "public"."users_settings" add constraint "users_settings_pkey" PRIMARY KEY using index "users_settings_pkey"; + +alter table "public"."guilds" add constraint "guilds_guild_id_key" UNIQUE using index "guilds_guild_id_key"; + +alter table "public"."guilds_plugins" add constraint "guilds_plugins_name_fkey" FOREIGN KEY (name) REFERENCES plugins(name) not valid; + +alter table "public"."guilds_plugins" validate constraint "guilds_plugins_name_fkey"; + +alter table "public"."guilds_plugins" add constraint "guilds_plugins_owner_fkey" FOREIGN KEY (owner) REFERENCES guilds(guild_id) ON DELETE CASCADE not valid; + +alter table "public"."guilds_plugins" validate constraint "guilds_plugins_owner_fkey"; + +alter table "public"."plugins" add constraint "plugins_name_key" UNIQUE using index "plugins_name_key"; + +set check_function_bodies = off; + +CREATE OR REPLACE FUNCTION public.reset_chat_gpt_plugin() + RETURNS void + LANGUAGE plpgsql +AS $function$ BEGIN UPDATE guilds_plugins SET @@ -145,8 +120,7 @@ OR REPLACE FUNCTION reset_chat_gpt_plugin() RETURNS void AS $$ BEGIN END; -$$ LANGUAGE plpgsql; +$function$ +; + --- Crons -SELECT - cron.schedule('0 0 * * *', 'SELECT reset_chat_gpt_plugin()'); diff --git a/supabase/seed.sql b/supabase/seed.sql deleted file mode 100644 index e69de29..0000000 diff --git a/supabase/seed.template.sql b/supabase/seed.template.sql new file mode 100644 index 0000000..fef0bf6 --- /dev/null +++ b/supabase/seed.template.sql @@ -0,0 +1,7 @@ +-- This file is the template for your local supabase database. Remove the .template from the filename to use for seeding your local database. + +-- Seed data for 'timezones' table, change the USER_ID_ONE and USER_ID_TWO to the user ids you want to seed. + +INSERT INTO users_settings (id, created_at, user_id, metadata, type) VALUES +(1, '2023-09-13 08:05:31+00', USER_ID_ONE, '{"timezone":"Europe/Berlin"}', 'timezone'), +(2, '2023-09-13 08:06:04.711699+00', USER_ID_TWO, '{"timezone":"Africa/Ceuta"}', 'timezone'); diff --git a/tests/utils/regex.spec.ts b/tests/utils/regex.spec.ts index bff7383..94a2152 100644 --- a/tests/utils/regex.spec.ts +++ b/tests/utils/regex.spec.ts @@ -23,7 +23,7 @@ const invalidTimeZones = [ 'Invalid_Timezone/Invalid_Timezone22', ] -describe('TIME_ZONES_REGEX', () => { +describe('Utils: Timezone', () => { it('should match valid time zones', () => { validTimeZones.forEach((timeZone) => { expect(TIME_ZONES_REGEX.test(timeZone) && TIMEZONES_LIST.includes(timeZone)).eq(true)