diff --git a/drizzle/0004_puzzling_omega_flight.sql b/drizzle/0004_puzzling_omega_flight.sql new file mode 100644 index 0000000..e765dc5 --- /dev/null +++ b/drizzle/0004_puzzling_omega_flight.sql @@ -0,0 +1,25 @@ +CREATE TABLE `nomination_approvals` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `nomination_id` integer NOT NULL, + `approver_id` text NOT NULL, + `created_at` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `idx_nomination_approvals_nomination_approver` ON `nomination_approvals` (`nomination_id`,`approver_id`);--> statement-breakpoint +CREATE INDEX `idx_nomination_approvals_nomination_id` ON `nomination_approvals` (`nomination_id`);--> statement-breakpoint +CREATE TABLE `nominations` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `guild_id` text NOT NULL, + `channel_id` text NOT NULL, + `nominee_id` text NOT NULL, + `nominator_id` text NOT NULL, + `target_role_id` text NOT NULL, + `required_approvals` integer NOT NULL, + `status` text DEFAULT 'submitted' NOT NULL, + `completed_at` text, + `created_at` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL, + `updated_at` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL +); +--> statement-breakpoint +CREATE INDEX `idx_nominations_guild_nominee_status` ON `nominations` (`guild_id`,`nominee_id`,`status`);--> statement-breakpoint +CREATE INDEX `idx_nominations_status` ON `nominations` (`status`); \ No newline at end of file diff --git a/drizzle/0005_lethal_senator_kelly.sql b/drizzle/0005_lethal_senator_kelly.sql new file mode 100644 index 0000000..d516768 --- /dev/null +++ b/drizzle/0005_lethal_senator_kelly.sql @@ -0,0 +1 @@ +ALTER TABLE `nominations` ADD `reason` text DEFAULT 'No reason provided.' NOT NULL; diff --git a/drizzle/0006_clean_prism.sql b/drizzle/0006_clean_prism.sql new file mode 100644 index 0000000..3580d07 --- /dev/null +++ b/drizzle/0006_clean_prism.sql @@ -0,0 +1,2 @@ +DROP INDEX `idx_nominations_guild_nominee_status`;--> statement-breakpoint +CREATE UNIQUE INDEX `idx_nominations_submitted_unique` ON `nominations` (`guild_id`,`nominee_id`,`target_role_id`) WHERE "nominations"."status" = 'submitted'; \ No newline at end of file diff --git a/drizzle/0007_overrated_mauler.sql b/drizzle/0007_overrated_mauler.sql new file mode 100644 index 0000000..ffe6280 --- /dev/null +++ b/drizzle/0007_overrated_mauler.sql @@ -0,0 +1,3 @@ +ALTER TABLE `nominations` ADD `message_id` text;--> statement-breakpoint +ALTER TABLE `nominations` ADD `expires_at` text;--> statement-breakpoint +UPDATE `nominations` SET `expires_at` = strftime('%Y-%m-%dT%H:%M:%fZ', `created_at`, '+48 hours') WHERE `expires_at` IS NULL; diff --git a/drizzle/meta/0004_snapshot.json b/drizzle/meta/0004_snapshot.json new file mode 100644 index 0000000..a6f154f --- /dev/null +++ b/drizzle/meta/0004_snapshot.json @@ -0,0 +1,1142 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "93678d3c-195c-4837-a453-e73296d955ed", + "prevId": "02ac93cc-e55a-447f-b697-586ca86e97bb", + "tables": { + "claim_requests": { + "name": "claim_requests", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "github_username": { + "name": "github_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "merged_pr_count": { + "name": "merged_pr_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_message_id": { + "name": "review_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_thread_id": { + "name": "review_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_at": { + "name": "decided_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_by_id": { + "name": "decided_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_claim_requests_guild_user": { + "name": "idx_claim_requests_guild_user", + "columns": [ + "guild_id", + "user_id" + ], + "isUnique": true + }, + "idx_claim_requests_user_id": { + "name": "idx_claim_requests_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_claim_requests_status": { + "name": "idx_claim_requests_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_cases": { + "name": "clawhub_content_rights_cases", + "columns": { + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "form_submission_id": { + "name": "form_submission_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "requester_name": { + "name": "requester_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clawhub_urls": { + "name": "clawhub_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "clawhub_content_rights_cases_form_submission_id_unique": { + "name": "clawhub_content_rights_cases_form_submission_id_unique", + "columns": [ + "form_submission_id" + ], + "isUnique": true + }, + "idx_clawhub_content_rights_cases_status": { + "name": "idx_clawhub_content_rights_cases_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_clawhub_content_rights_cases_email": { + "name": "idx_clawhub_content_rights_cases_email", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_events": { + "name": "clawhub_content_rights_events", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor": { + "name": "actor", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_clawhub_content_rights_events_case_id": { + "name": "idx_clawhub_content_rights_events_case_id", + "columns": [ + "case_id" + ], + "isUnique": false + }, + "idx_clawhub_content_rights_events_event_type": { + "name": "idx_clawhub_content_rights_events_event_type", + "columns": [ + "event_type" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_files": { + "name": "clawhub_content_rights_files", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "original_name": { + "name": "original_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "clawhub_content_rights_files_object_key_unique": { + "name": "clawhub_content_rights_files_object_key_unique", + "columns": [ + "object_key" + ], + "isUnique": true + }, + "idx_clawhub_content_rights_files_case_id": { + "name": "idx_clawhub_content_rights_files_case_id", + "columns": [ + "case_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "form_submissions": { + "name": "form_submissions", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "form_id": { + "name": "form_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "auth_provider": { + "name": "auth_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "applicant_id": { + "name": "applicant_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "applicant_username": { + "name": "applicant_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "review_channel_id": { + "name": "review_channel_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "review_message_id": { + "name": "review_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_thread_id": { + "name": "review_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_at": { + "name": "decided_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_by_id": { + "name": "decided_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action_result": { + "name": "action_result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_form_submissions_form_id": { + "name": "idx_form_submissions_form_id", + "columns": [ + "form_id" + ], + "isUnique": false + }, + "idx_form_submissions_status": { + "name": "idx_form_submissions_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_form_submissions_applicant_id": { + "name": "idx_form_submissions_applicant_id", + "columns": [ + "applicant_id" + ], + "isUnique": false + }, + "idx_form_submissions_review_message_id": { + "name": "idx_form_submissions_review_message_id", + "columns": [ + "review_message_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "helper_events": { + "name": "helper_events", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'helper_command'" + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "event_time": { + "name": "event_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "invoked_by_id": { + "name": "invoked_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoked_by_username": { + "name": "invoked_by_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoked_by_global_name": { + "name": "invoked_by_global_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_helper_events_event_time": { + "name": "idx_helper_events_event_time", + "columns": [ + "event_time" + ], + "isUnique": false + }, + "idx_helper_events_command": { + "name": "idx_helper_events_command", + "columns": [ + "command" + ], + "isUnique": false + }, + "idx_helper_events_thread_id": { + "name": "idx_helper_events_thread_id", + "columns": [ + "thread_id" + ], + "isUnique": false + }, + "idx_helper_events_invoked_by_id": { + "name": "idx_helper_events_invoked_by_id", + "columns": [ + "invoked_by_id" + ], + "isUnique": false + }, + "idx_helper_events_event_type": { + "name": "idx_helper_events_event_type", + "columns": [ + "event_type" + ], + "isUnique": false + }, + "idx_helper_events_thread_time": { + "name": "idx_helper_events_thread_time", + "columns": [ + "thread_id", + "event_time" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "keyValue": { + "name": "keyValue", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "nomination_approvals": { + "name": "nomination_approvals", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "nomination_id": { + "name": "nomination_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "approver_id": { + "name": "approver_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_nomination_approvals_nomination_approver": { + "name": "idx_nomination_approvals_nomination_approver", + "columns": [ + "nomination_id", + "approver_id" + ], + "isUnique": true + }, + "idx_nomination_approvals_nomination_id": { + "name": "idx_nomination_approvals_nomination_id", + "columns": [ + "nomination_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "nominations": { + "name": "nominations", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nominee_id": { + "name": "nominee_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nominator_id": { + "name": "nominator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_role_id": { + "name": "target_role_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "required_approvals": { + "name": "required_approvals", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "completed_at": { + "name": "completed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_nominations_guild_nominee_status": { + "name": "idx_nominations_guild_nominee_status", + "columns": [ + "guild_id", + "nominee_id", + "status" + ], + "isUnique": false + }, + "idx_nominations_status": { + "name": "idx_nominations_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "reddit_moderation_contexts": { + "name": "reddit_moderation_contexts", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "subreddit": { + "name": "subreddit", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'moderated'" + }, + "unaction": { + "name": "unaction", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'reviewed'" + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "moderator": { + "name": "moderator", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "banned_at": { + "name": "banned_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_reddit_moderation_contexts_subreddit_username": { + "name": "idx_reddit_moderation_contexts_subreddit_username", + "columns": [ + "subreddit", + "username" + ], + "isUnique": true + }, + "idx_reddit_moderation_contexts_username": { + "name": "idx_reddit_moderation_contexts_username", + "columns": [ + "username" + ], + "isUnique": false + }, + "idx_reddit_moderation_contexts_action": { + "name": "idx_reddit_moderation_contexts_action", + "columns": [ + "action" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tracked_threads": { + "name": "tracked_threads", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_checked": { + "name": "last_checked", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "solved": { + "name": "solved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "warning_level": { + "name": "warning_level", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "closed": { + "name": "closed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_message_count": { + "name": "last_message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "tracked_threads_thread_id_unique": { + "name": "tracked_threads_thread_id_unique", + "columns": [ + "thread_id" + ], + "isUnique": true + }, + "idx_tracked_threads_solved": { + "name": "idx_tracked_threads_solved", + "columns": [ + "solved" + ], + "isUnique": false + }, + "idx_tracked_threads_last_checked": { + "name": "idx_tracked_threads_last_checked", + "columns": [ + "last_checked" + ], + "isUnique": false + }, + "idx_tracked_threads_received_at": { + "name": "idx_tracked_threads_received_at", + "columns": [ + "received_at" + ], + "isUnique": false + }, + "idx_tracked_threads_closed": { + "name": "idx_tracked_threads_closed", + "columns": [ + "closed" + ], + "isUnique": false + }, + "idx_tracked_threads_warning_level": { + "name": "idx_tracked_threads_warning_level", + "columns": [ + "warning_level" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0005_snapshot.json b/drizzle/meta/0005_snapshot.json new file mode 100644 index 0000000..887df9c --- /dev/null +++ b/drizzle/meta/0005_snapshot.json @@ -0,0 +1,1150 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "b40ffb14-0380-48b0-90ab-1a97e3fbb7c0", + "prevId": "93678d3c-195c-4837-a453-e73296d955ed", + "tables": { + "claim_requests": { + "name": "claim_requests", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "github_username": { + "name": "github_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "merged_pr_count": { + "name": "merged_pr_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_message_id": { + "name": "review_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_thread_id": { + "name": "review_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_at": { + "name": "decided_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_by_id": { + "name": "decided_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_claim_requests_guild_user": { + "name": "idx_claim_requests_guild_user", + "columns": [ + "guild_id", + "user_id" + ], + "isUnique": true + }, + "idx_claim_requests_user_id": { + "name": "idx_claim_requests_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_claim_requests_status": { + "name": "idx_claim_requests_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_cases": { + "name": "clawhub_content_rights_cases", + "columns": { + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "form_submission_id": { + "name": "form_submission_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "requester_name": { + "name": "requester_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clawhub_urls": { + "name": "clawhub_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "clawhub_content_rights_cases_form_submission_id_unique": { + "name": "clawhub_content_rights_cases_form_submission_id_unique", + "columns": [ + "form_submission_id" + ], + "isUnique": true + }, + "idx_clawhub_content_rights_cases_status": { + "name": "idx_clawhub_content_rights_cases_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_clawhub_content_rights_cases_email": { + "name": "idx_clawhub_content_rights_cases_email", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_events": { + "name": "clawhub_content_rights_events", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor": { + "name": "actor", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_clawhub_content_rights_events_case_id": { + "name": "idx_clawhub_content_rights_events_case_id", + "columns": [ + "case_id" + ], + "isUnique": false + }, + "idx_clawhub_content_rights_events_event_type": { + "name": "idx_clawhub_content_rights_events_event_type", + "columns": [ + "event_type" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_files": { + "name": "clawhub_content_rights_files", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "original_name": { + "name": "original_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "clawhub_content_rights_files_object_key_unique": { + "name": "clawhub_content_rights_files_object_key_unique", + "columns": [ + "object_key" + ], + "isUnique": true + }, + "idx_clawhub_content_rights_files_case_id": { + "name": "idx_clawhub_content_rights_files_case_id", + "columns": [ + "case_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "form_submissions": { + "name": "form_submissions", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "form_id": { + "name": "form_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "auth_provider": { + "name": "auth_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "applicant_id": { + "name": "applicant_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "applicant_username": { + "name": "applicant_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "review_channel_id": { + "name": "review_channel_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "review_message_id": { + "name": "review_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_thread_id": { + "name": "review_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_at": { + "name": "decided_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_by_id": { + "name": "decided_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action_result": { + "name": "action_result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_form_submissions_form_id": { + "name": "idx_form_submissions_form_id", + "columns": [ + "form_id" + ], + "isUnique": false + }, + "idx_form_submissions_status": { + "name": "idx_form_submissions_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_form_submissions_applicant_id": { + "name": "idx_form_submissions_applicant_id", + "columns": [ + "applicant_id" + ], + "isUnique": false + }, + "idx_form_submissions_review_message_id": { + "name": "idx_form_submissions_review_message_id", + "columns": [ + "review_message_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "helper_events": { + "name": "helper_events", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'helper_command'" + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "event_time": { + "name": "event_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "invoked_by_id": { + "name": "invoked_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoked_by_username": { + "name": "invoked_by_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoked_by_global_name": { + "name": "invoked_by_global_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_helper_events_event_time": { + "name": "idx_helper_events_event_time", + "columns": [ + "event_time" + ], + "isUnique": false + }, + "idx_helper_events_command": { + "name": "idx_helper_events_command", + "columns": [ + "command" + ], + "isUnique": false + }, + "idx_helper_events_thread_id": { + "name": "idx_helper_events_thread_id", + "columns": [ + "thread_id" + ], + "isUnique": false + }, + "idx_helper_events_invoked_by_id": { + "name": "idx_helper_events_invoked_by_id", + "columns": [ + "invoked_by_id" + ], + "isUnique": false + }, + "idx_helper_events_event_type": { + "name": "idx_helper_events_event_type", + "columns": [ + "event_type" + ], + "isUnique": false + }, + "idx_helper_events_thread_time": { + "name": "idx_helper_events_thread_time", + "columns": [ + "thread_id", + "event_time" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "keyValue": { + "name": "keyValue", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "nomination_approvals": { + "name": "nomination_approvals", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "nomination_id": { + "name": "nomination_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "approver_id": { + "name": "approver_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_nomination_approvals_nomination_approver": { + "name": "idx_nomination_approvals_nomination_approver", + "columns": [ + "nomination_id", + "approver_id" + ], + "isUnique": true + }, + "idx_nomination_approvals_nomination_id": { + "name": "idx_nomination_approvals_nomination_id", + "columns": [ + "nomination_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "nominations": { + "name": "nominations", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nominee_id": { + "name": "nominee_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nominator_id": { + "name": "nominator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'No reason provided.'", + "autoincrement": false + }, + "target_role_id": { + "name": "target_role_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "required_approvals": { + "name": "required_approvals", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "completed_at": { + "name": "completed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_nominations_guild_nominee_status": { + "name": "idx_nominations_guild_nominee_status", + "columns": [ + "guild_id", + "nominee_id", + "status" + ], + "isUnique": false + }, + "idx_nominations_status": { + "name": "idx_nominations_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "reddit_moderation_contexts": { + "name": "reddit_moderation_contexts", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "subreddit": { + "name": "subreddit", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'moderated'" + }, + "unaction": { + "name": "unaction", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'reviewed'" + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "moderator": { + "name": "moderator", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "banned_at": { + "name": "banned_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_reddit_moderation_contexts_subreddit_username": { + "name": "idx_reddit_moderation_contexts_subreddit_username", + "columns": [ + "subreddit", + "username" + ], + "isUnique": true + }, + "idx_reddit_moderation_contexts_username": { + "name": "idx_reddit_moderation_contexts_username", + "columns": [ + "username" + ], + "isUnique": false + }, + "idx_reddit_moderation_contexts_action": { + "name": "idx_reddit_moderation_contexts_action", + "columns": [ + "action" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tracked_threads": { + "name": "tracked_threads", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_checked": { + "name": "last_checked", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "solved": { + "name": "solved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "warning_level": { + "name": "warning_level", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "closed": { + "name": "closed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_message_count": { + "name": "last_message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "tracked_threads_thread_id_unique": { + "name": "tracked_threads_thread_id_unique", + "columns": [ + "thread_id" + ], + "isUnique": true + }, + "idx_tracked_threads_solved": { + "name": "idx_tracked_threads_solved", + "columns": [ + "solved" + ], + "isUnique": false + }, + "idx_tracked_threads_last_checked": { + "name": "idx_tracked_threads_last_checked", + "columns": [ + "last_checked" + ], + "isUnique": false + }, + "idx_tracked_threads_received_at": { + "name": "idx_tracked_threads_received_at", + "columns": [ + "received_at" + ], + "isUnique": false + }, + "idx_tracked_threads_closed": { + "name": "idx_tracked_threads_closed", + "columns": [ + "closed" + ], + "isUnique": false + }, + "idx_tracked_threads_warning_level": { + "name": "idx_tracked_threads_warning_level", + "columns": [ + "warning_level" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} diff --git a/drizzle/meta/0006_snapshot.json b/drizzle/meta/0006_snapshot.json new file mode 100644 index 0000000..aeaf33a --- /dev/null +++ b/drizzle/meta/0006_snapshot.json @@ -0,0 +1,1151 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "8881e918-51a0-4d28-bb4c-0e9573a64c91", + "prevId": "b40ffb14-0380-48b0-90ab-1a97e3fbb7c0", + "tables": { + "claim_requests": { + "name": "claim_requests", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "github_username": { + "name": "github_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "merged_pr_count": { + "name": "merged_pr_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_message_id": { + "name": "review_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_thread_id": { + "name": "review_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_at": { + "name": "decided_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_by_id": { + "name": "decided_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_claim_requests_guild_user": { + "name": "idx_claim_requests_guild_user", + "columns": [ + "guild_id", + "user_id" + ], + "isUnique": true + }, + "idx_claim_requests_user_id": { + "name": "idx_claim_requests_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_claim_requests_status": { + "name": "idx_claim_requests_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_cases": { + "name": "clawhub_content_rights_cases", + "columns": { + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "form_submission_id": { + "name": "form_submission_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "requester_name": { + "name": "requester_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clawhub_urls": { + "name": "clawhub_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "clawhub_content_rights_cases_form_submission_id_unique": { + "name": "clawhub_content_rights_cases_form_submission_id_unique", + "columns": [ + "form_submission_id" + ], + "isUnique": true + }, + "idx_clawhub_content_rights_cases_status": { + "name": "idx_clawhub_content_rights_cases_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_clawhub_content_rights_cases_email": { + "name": "idx_clawhub_content_rights_cases_email", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_events": { + "name": "clawhub_content_rights_events", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor": { + "name": "actor", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_clawhub_content_rights_events_case_id": { + "name": "idx_clawhub_content_rights_events_case_id", + "columns": [ + "case_id" + ], + "isUnique": false + }, + "idx_clawhub_content_rights_events_event_type": { + "name": "idx_clawhub_content_rights_events_event_type", + "columns": [ + "event_type" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_files": { + "name": "clawhub_content_rights_files", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "original_name": { + "name": "original_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "clawhub_content_rights_files_object_key_unique": { + "name": "clawhub_content_rights_files_object_key_unique", + "columns": [ + "object_key" + ], + "isUnique": true + }, + "idx_clawhub_content_rights_files_case_id": { + "name": "idx_clawhub_content_rights_files_case_id", + "columns": [ + "case_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "form_submissions": { + "name": "form_submissions", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "form_id": { + "name": "form_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "auth_provider": { + "name": "auth_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "applicant_id": { + "name": "applicant_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "applicant_username": { + "name": "applicant_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "review_channel_id": { + "name": "review_channel_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "review_message_id": { + "name": "review_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_thread_id": { + "name": "review_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_at": { + "name": "decided_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_by_id": { + "name": "decided_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action_result": { + "name": "action_result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_form_submissions_form_id": { + "name": "idx_form_submissions_form_id", + "columns": [ + "form_id" + ], + "isUnique": false + }, + "idx_form_submissions_status": { + "name": "idx_form_submissions_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_form_submissions_applicant_id": { + "name": "idx_form_submissions_applicant_id", + "columns": [ + "applicant_id" + ], + "isUnique": false + }, + "idx_form_submissions_review_message_id": { + "name": "idx_form_submissions_review_message_id", + "columns": [ + "review_message_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "helper_events": { + "name": "helper_events", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'helper_command'" + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "event_time": { + "name": "event_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "invoked_by_id": { + "name": "invoked_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoked_by_username": { + "name": "invoked_by_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoked_by_global_name": { + "name": "invoked_by_global_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_helper_events_event_time": { + "name": "idx_helper_events_event_time", + "columns": [ + "event_time" + ], + "isUnique": false + }, + "idx_helper_events_command": { + "name": "idx_helper_events_command", + "columns": [ + "command" + ], + "isUnique": false + }, + "idx_helper_events_thread_id": { + "name": "idx_helper_events_thread_id", + "columns": [ + "thread_id" + ], + "isUnique": false + }, + "idx_helper_events_invoked_by_id": { + "name": "idx_helper_events_invoked_by_id", + "columns": [ + "invoked_by_id" + ], + "isUnique": false + }, + "idx_helper_events_event_type": { + "name": "idx_helper_events_event_type", + "columns": [ + "event_type" + ], + "isUnique": false + }, + "idx_helper_events_thread_time": { + "name": "idx_helper_events_thread_time", + "columns": [ + "thread_id", + "event_time" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "keyValue": { + "name": "keyValue", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "nomination_approvals": { + "name": "nomination_approvals", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "nomination_id": { + "name": "nomination_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "approver_id": { + "name": "approver_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_nomination_approvals_nomination_approver": { + "name": "idx_nomination_approvals_nomination_approver", + "columns": [ + "nomination_id", + "approver_id" + ], + "isUnique": true + }, + "idx_nomination_approvals_nomination_id": { + "name": "idx_nomination_approvals_nomination_id", + "columns": [ + "nomination_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "nominations": { + "name": "nominations", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nominee_id": { + "name": "nominee_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nominator_id": { + "name": "nominator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'No reason provided.'" + }, + "target_role_id": { + "name": "target_role_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "required_approvals": { + "name": "required_approvals", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "completed_at": { + "name": "completed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_nominations_submitted_unique": { + "name": "idx_nominations_submitted_unique", + "columns": [ + "guild_id", + "nominee_id", + "target_role_id" + ], + "isUnique": true, + "where": "\"nominations\".\"status\" = 'submitted'" + }, + "idx_nominations_status": { + "name": "idx_nominations_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "reddit_moderation_contexts": { + "name": "reddit_moderation_contexts", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "subreddit": { + "name": "subreddit", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'moderated'" + }, + "unaction": { + "name": "unaction", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'reviewed'" + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "moderator": { + "name": "moderator", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "banned_at": { + "name": "banned_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_reddit_moderation_contexts_subreddit_username": { + "name": "idx_reddit_moderation_contexts_subreddit_username", + "columns": [ + "subreddit", + "username" + ], + "isUnique": true + }, + "idx_reddit_moderation_contexts_username": { + "name": "idx_reddit_moderation_contexts_username", + "columns": [ + "username" + ], + "isUnique": false + }, + "idx_reddit_moderation_contexts_action": { + "name": "idx_reddit_moderation_contexts_action", + "columns": [ + "action" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tracked_threads": { + "name": "tracked_threads", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_checked": { + "name": "last_checked", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "solved": { + "name": "solved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "warning_level": { + "name": "warning_level", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "closed": { + "name": "closed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_message_count": { + "name": "last_message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "tracked_threads_thread_id_unique": { + "name": "tracked_threads_thread_id_unique", + "columns": [ + "thread_id" + ], + "isUnique": true + }, + "idx_tracked_threads_solved": { + "name": "idx_tracked_threads_solved", + "columns": [ + "solved" + ], + "isUnique": false + }, + "idx_tracked_threads_last_checked": { + "name": "idx_tracked_threads_last_checked", + "columns": [ + "last_checked" + ], + "isUnique": false + }, + "idx_tracked_threads_received_at": { + "name": "idx_tracked_threads_received_at", + "columns": [ + "received_at" + ], + "isUnique": false + }, + "idx_tracked_threads_closed": { + "name": "idx_tracked_threads_closed", + "columns": [ + "closed" + ], + "isUnique": false + }, + "idx_tracked_threads_warning_level": { + "name": "idx_tracked_threads_warning_level", + "columns": [ + "warning_level" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0007_snapshot.json b/drizzle/meta/0007_snapshot.json new file mode 100644 index 0000000..a966acb --- /dev/null +++ b/drizzle/meta/0007_snapshot.json @@ -0,0 +1,1165 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "2a3dc470-7bab-4d8f-ab95-5ba6b8a4e11c", + "prevId": "8881e918-51a0-4d28-bb4c-0e9573a64c91", + "tables": { + "claim_requests": { + "name": "claim_requests", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "github_username": { + "name": "github_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "merged_pr_count": { + "name": "merged_pr_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_message_id": { + "name": "review_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_thread_id": { + "name": "review_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_at": { + "name": "decided_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_by_id": { + "name": "decided_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_claim_requests_guild_user": { + "name": "idx_claim_requests_guild_user", + "columns": [ + "guild_id", + "user_id" + ], + "isUnique": true + }, + "idx_claim_requests_user_id": { + "name": "idx_claim_requests_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_claim_requests_status": { + "name": "idx_claim_requests_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_cases": { + "name": "clawhub_content_rights_cases", + "columns": { + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "form_submission_id": { + "name": "form_submission_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "requester_name": { + "name": "requester_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clawhub_urls": { + "name": "clawhub_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "clawhub_content_rights_cases_form_submission_id_unique": { + "name": "clawhub_content_rights_cases_form_submission_id_unique", + "columns": [ + "form_submission_id" + ], + "isUnique": true + }, + "idx_clawhub_content_rights_cases_status": { + "name": "idx_clawhub_content_rights_cases_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_clawhub_content_rights_cases_email": { + "name": "idx_clawhub_content_rights_cases_email", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_events": { + "name": "clawhub_content_rights_events", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor": { + "name": "actor", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_clawhub_content_rights_events_case_id": { + "name": "idx_clawhub_content_rights_events_case_id", + "columns": [ + "case_id" + ], + "isUnique": false + }, + "idx_clawhub_content_rights_events_event_type": { + "name": "idx_clawhub_content_rights_events_event_type", + "columns": [ + "event_type" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "clawhub_content_rights_files": { + "name": "clawhub_content_rights_files", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "case_id": { + "name": "case_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "original_name": { + "name": "original_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "clawhub_content_rights_files_object_key_unique": { + "name": "clawhub_content_rights_files_object_key_unique", + "columns": [ + "object_key" + ], + "isUnique": true + }, + "idx_clawhub_content_rights_files_case_id": { + "name": "idx_clawhub_content_rights_files_case_id", + "columns": [ + "case_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "form_submissions": { + "name": "form_submissions", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "form_id": { + "name": "form_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "auth_provider": { + "name": "auth_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "applicant_id": { + "name": "applicant_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "applicant_username": { + "name": "applicant_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "review_channel_id": { + "name": "review_channel_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "review_message_id": { + "name": "review_message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_thread_id": { + "name": "review_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_at": { + "name": "decided_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decided_by_id": { + "name": "decided_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action_result": { + "name": "action_result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_form_submissions_form_id": { + "name": "idx_form_submissions_form_id", + "columns": [ + "form_id" + ], + "isUnique": false + }, + "idx_form_submissions_status": { + "name": "idx_form_submissions_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_form_submissions_applicant_id": { + "name": "idx_form_submissions_applicant_id", + "columns": [ + "applicant_id" + ], + "isUnique": false + }, + "idx_form_submissions_review_message_id": { + "name": "idx_form_submissions_review_message_id", + "columns": [ + "review_message_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "helper_events": { + "name": "helper_events", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'helper_command'" + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "event_time": { + "name": "event_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "invoked_by_id": { + "name": "invoked_by_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoked_by_username": { + "name": "invoked_by_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "invoked_by_global_name": { + "name": "invoked_by_global_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_helper_events_event_time": { + "name": "idx_helper_events_event_time", + "columns": [ + "event_time" + ], + "isUnique": false + }, + "idx_helper_events_command": { + "name": "idx_helper_events_command", + "columns": [ + "command" + ], + "isUnique": false + }, + "idx_helper_events_thread_id": { + "name": "idx_helper_events_thread_id", + "columns": [ + "thread_id" + ], + "isUnique": false + }, + "idx_helper_events_invoked_by_id": { + "name": "idx_helper_events_invoked_by_id", + "columns": [ + "invoked_by_id" + ], + "isUnique": false + }, + "idx_helper_events_event_type": { + "name": "idx_helper_events_event_type", + "columns": [ + "event_type" + ], + "isUnique": false + }, + "idx_helper_events_thread_time": { + "name": "idx_helper_events_thread_time", + "columns": [ + "thread_id", + "event_time" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "keyValue": { + "name": "keyValue", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "nomination_approvals": { + "name": "nomination_approvals", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "nomination_id": { + "name": "nomination_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "approver_id": { + "name": "approver_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_nomination_approvals_nomination_approver": { + "name": "idx_nomination_approvals_nomination_approver", + "columns": [ + "nomination_id", + "approver_id" + ], + "isUnique": true + }, + "idx_nomination_approvals_nomination_id": { + "name": "idx_nomination_approvals_nomination_id", + "columns": [ + "nomination_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "nominations": { + "name": "nominations", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nominee_id": { + "name": "nominee_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nominator_id": { + "name": "nominator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'No reason provided.'" + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_role_id": { + "name": "target_role_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "required_approvals": { + "name": "required_approvals", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'submitted'" + }, + "expires_at": { + "name": "expires_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_nominations_submitted_unique": { + "name": "idx_nominations_submitted_unique", + "columns": [ + "guild_id", + "nominee_id", + "target_role_id" + ], + "isUnique": true, + "where": "\"nominations\".\"status\" = 'submitted'" + }, + "idx_nominations_status": { + "name": "idx_nominations_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "reddit_moderation_contexts": { + "name": "reddit_moderation_contexts", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "subreddit": { + "name": "subreddit", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'moderated'" + }, + "unaction": { + "name": "unaction", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'reviewed'" + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "moderator": { + "name": "moderator", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "banned_at": { + "name": "banned_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + } + }, + "indexes": { + "idx_reddit_moderation_contexts_subreddit_username": { + "name": "idx_reddit_moderation_contexts_subreddit_username", + "columns": [ + "subreddit", + "username" + ], + "isUnique": true + }, + "idx_reddit_moderation_contexts_username": { + "name": "idx_reddit_moderation_contexts_username", + "columns": [ + "username" + ], + "isUnique": false + }, + "idx_reddit_moderation_contexts_action": { + "name": "idx_reddit_moderation_contexts_action", + "columns": [ + "action" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tracked_threads": { + "name": "tracked_threads", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_checked": { + "name": "last_checked", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "solved": { + "name": "solved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "warning_level": { + "name": "warning_level", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "closed": { + "name": "closed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_message_count": { + "name": "last_message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "tracked_threads_thread_id_unique": { + "name": "tracked_threads_thread_id_unique", + "columns": [ + "thread_id" + ], + "isUnique": true + }, + "idx_tracked_threads_solved": { + "name": "idx_tracked_threads_solved", + "columns": [ + "solved" + ], + "isUnique": false + }, + "idx_tracked_threads_last_checked": { + "name": "idx_tracked_threads_last_checked", + "columns": [ + "last_checked" + ], + "isUnique": false + }, + "idx_tracked_threads_received_at": { + "name": "idx_tracked_threads_received_at", + "columns": [ + "received_at" + ], + "isUnique": false + }, + "idx_tracked_threads_closed": { + "name": "idx_tracked_threads_closed", + "columns": [ + "closed" + ], + "isUnique": false + }, + "idx_tracked_threads_warning_level": { + "name": "idx_tracked_threads_warning_level", + "columns": [ + "warning_level" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index d311bd6..33d670d 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -29,6 +29,34 @@ "when": 1781564580172, "tag": "0003_clawhub_content_rights", "breakpoints": true + }, + { + "idx": 4, + "version": "6", + "when": 1782182989552, + "tag": "0004_puzzling_omega_flight", + "breakpoints": true + }, + { + "idx": 5, + "version": "6", + "when": 1782184143387, + "tag": "0005_lethal_senator_kelly", + "breakpoints": true + }, + { + "idx": 6, + "version": "6", + "when": 1782242214106, + "tag": "0006_clean_prism", + "breakpoints": true + }, + { + "idx": 7, + "version": "6", + "when": 1782243715774, + "tag": "0007_overrated_mauler", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/commands/nominate.ts b/src/commands/nominate.ts new file mode 100644 index 0000000..5446140 --- /dev/null +++ b/src/commands/nominate.ts @@ -0,0 +1,193 @@ +import { + ApplicationCommandOptionType, + ApplicationIntegrationType, + type APIMessage, + type CommandInteraction, + InteractionContextType, + Routes, + serializePayload +} from "@buape/carbon" +import { + buildNominationContainer, + buildNominationNoticeContainer +} from "../components/nominationButtons.js" +import { nominationConfig } from "../config/nominations.js" +import { + createNomination, + deleteNomination, + getActiveNominationForNominee, + getNominationApproverIds, + markExpiredSubmittedNominationForNominee, + setNominationMessageId +} from "../data/nominations.js" +import { editNominationMessageExpired } from "../services/nominationExpiry.js" +import BaseCommand from "./base.js" + +const getNominationExpiresAt = () => + new Date( + Date.now() + nominationConfig.expirationHours * 60 * 60 * 1000 + ).toISOString() + +export default class NominateCommand extends BaseCommand { + name = nominationConfig.commandName + description = "Nominate a user for Shell Society" + defer = false + contexts = [InteractionContextType.Guild] + integrationTypes = [ApplicationIntegrationType.GuildInstall] + options = [ + { + type: ApplicationCommandOptionType.User as const, + name: "user", + description: "The user to nominate", + required: true + }, + { + type: ApplicationCommandOptionType.String as const, + name: "reason", + description: "Why this user should join Shell Society", + required: true, + max_length: nominationConfig.maxReasonLength + } + ] + + private async replyWithNotice( + interaction: CommandInteraction, + body: string, + accentColor = "#f1c40f" + ) { + await interaction.reply({ + components: [buildNominationNoticeContainer(body, accentColor)], + ephemeral: true, + allowedMentions: { parse: [] } + }) + } + + async run(interaction: CommandInteraction) { + const channelId = interaction.rawData.channel_id ?? interaction.channel?.id + if (!channelId || !nominationConfig.nominationChannelIds.includes(channelId)) { + await this.replyWithNotice(interaction, nominationConfig.copy.wrongChannel) + return + } + + if (!interaction.guild || !interaction.user?.id) { + return + } + + const target = interaction.options.getUser("user", true) + let reasonOption: string | undefined + try { + reasonOption = interaction.options.getString("reason") + } catch { + await this.replyWithNotice(interaction, nominationConfig.copy.reasonTooLong) + return + } + const reason = reasonOption?.trim() ?? "" + if (reason.length === 0) { + await this.replyWithNotice(interaction, nominationConfig.copy.reasonRequired) + return + } + + if (reason.length > nominationConfig.maxReasonLength) { + await this.replyWithNotice(interaction, nominationConfig.copy.reasonTooLong) + return + } + + if (target.id === interaction.user.id) { + await this.replyWithNotice(interaction, nominationConfig.copy.selfNomination) + return + } + + if (target.bot) { + await this.replyWithNotice(interaction, nominationConfig.copy.botNomination) + return + } + + await interaction.defer({ ephemeral: true }) + + const expiredNomination = await markExpiredSubmittedNominationForNominee( + nominationConfig.guildId, + target.id, + nominationConfig.targetRoleId + ) + if (expiredNomination) { + await editNominationMessageExpired( + interaction.client, + expiredNomination + ).catch(() => null) + } + + const targetMember = await interaction.guild.fetchMember(target.id).catch(() => null) + if (!targetMember) { + await this.replyWithNotice( + interaction, + nominationConfig.copy.userNotFound, + "#f85149" + ) + return + } + + if (targetMember.roles.some((role) => role.id === nominationConfig.targetRoleId)) { + await this.replyWithNotice(interaction, nominationConfig.copy.alreadyHasRole) + return + } + + const existingNomination = await getActiveNominationForNominee( + nominationConfig.guildId, + target.id, + nominationConfig.targetRoleId + ) + if (existingNomination) { + await this.replyWithNotice(interaction, nominationConfig.copy.alreadyPending) + return + } + + const nomination = await createNomination({ + guildId: nominationConfig.guildId, + channelId, + nomineeId: target.id, + nominatorId: interaction.user.id, + reason, + expiresAt: getNominationExpiresAt(), + targetRoleId: nominationConfig.targetRoleId, + requiredApprovals: nominationConfig.requiredApprovals + }) + if (!nomination) { + await this.replyWithNotice(interaction, nominationConfig.copy.alreadyPending) + return + } + const approverIds = await getNominationApproverIds(nomination.id) + + let postedMessage: APIMessage | null = null + try { + postedMessage = await interaction.client.rest.post( + Routes.channelMessages(channelId), + { + body: serializePayload({ + components: [buildNominationContainer(nomination, approverIds)], + allowedMentions: { parse: [] } + }) + } + ) as APIMessage + await setNominationMessageId(nomination.id, postedMessage.id) + } catch { + if (postedMessage) { + await interaction.client.rest.delete( + Routes.channelMessage(channelId, postedMessage.id) + ).catch(() => null) + } + await deleteNomination(nomination.id).catch(() => null) + await this.replyWithNotice( + interaction, + nominationConfig.copy.nominationPostFailed, + "#f85149" + ).catch(() => null) + return + } + + await this.replyWithNotice( + interaction, + nominationConfig.copy.nominationPosted, + "#3fb950" + ) + } +} diff --git a/src/components/nominationButtons.ts b/src/components/nominationButtons.ts new file mode 100644 index 0000000..3916413 --- /dev/null +++ b/src/components/nominationButtons.ts @@ -0,0 +1,313 @@ +import { + Button, + type ButtonInteraction, + ButtonStyle, + type ComponentData, + Container, + Row, + Separator, + TextDisplay +} from "@buape/carbon" +import { nominationConfig } from "../config/nominations.js" +import { + getNomination, + getNominationApproverIds, + isNominationExpired, + markNominationApproved, + markNominationExpired, + recordNominationApproval, + restoreApprovedNominationToSubmitted +} from "../data/nominations.js" +import type { Nomination } from "../db/schema.js" +import { getRuntimeEnv } from "../runtime/env.js" + +const discordApiBase = "https://discord.com/api/v10" + +const parseNominationId = (id: unknown) => { + if (typeof id === "number" && Number.isInteger(id)) { + return id + } + if (typeof id === "string" && /^\d+$/.test(id)) { + return Number(id) + } + return null +} + +const hasApproverRole = (interaction: ButtonInteraction) => + interaction.member?.roles.some((role) => + nominationConfig.approverRoleIds.includes(role.id) + ) ?? false + +export const buildNominationNoticeContainer = ( + body: string, + accentColor = "#f1c40f" +) => new Container([new TextDisplay(body)], { accentColor }) + +const addTargetRole = async (nomination: Nomination) => { + const roleResponse = await fetch( + `${discordApiBase}/guilds/${nomination.guildId}/members/${nomination.nomineeId}/roles/${nomination.targetRoleId}`, + { + method: "PUT", + headers: { + Authorization: `Bot ${getRuntimeEnv().DISCORD_BOT_TOKEN}` + } + } + ) + + return roleResponse.ok || roleResponse.status === 204 +} + +export const buildNominationContainer = ( + nomination: Nomination, + approverIds: string[] +) => { + const approved = nomination.status === "approved" + const expired = nomination.status === "expired" + const body = expired + ? nominationConfig.copy.nominationExpired + : approved + ? `<@${nomination.nomineeId}> welcome to the Shell Society! +This is a private section of the server that is high signal, low noise, for the valued members of the server to gather together without the chaotic madness that is <#1456350065223270435>. + +Just remember, this is not a channel to share your PRs, etc; it’s only a social channel so please treat it as such and above all else, enjoy! 🐚🦞` + : `<@${nomination.nomineeId}> has been nominated by <@${nomination.nominatorId}> for ${nomination.reason}.` + const components: Container["components"] = [ + new TextDisplay(`### ${nominationConfig.copy.title}`), + new TextDisplay(body) + ] + if (!expired) { + components.push( + new TextDisplay(`Approvals: ${Math.min(approverIds.length, nomination.requiredApprovals)}/${nomination.requiredApprovals}`), + new Separator({ divider: true, spacing: "small" }), + new Row([new NominationApproveButton(nomination.id, approved)]) + ) + } + + return new Container( + components, + { accentColor: expired ? "#8b949e" : approved ? "#3fb950" : "#f1c40f" } + ) +} + +export class NominationApproveButton extends Button { + customId = "nomination-approve" + label = nominationConfig.copy.buttonLabel + style = ButtonStyle.Success + ephemeral = true + defer = true + disabled = false + + constructor(id?: number, disabled = false) { + super() + if (typeof id === "number") { + this.customId = `nomination-approve:id=${id}` + } + this.disabled = disabled + } + + async run(interaction: ButtonInteraction, data: ComponentData) { + const replyWithExpired = async (nomination: Nomination) => { + const expiredNomination = + nomination.status === "expired" + ? nomination + : await markNominationExpired(nomination.id) ?? { + ...nomination, + status: "expired" + } + await interaction.message?.edit({ + components: [buildNominationContainer(expiredNomination, [])], + allowedMentions: { parse: [] } + }).catch(() => null) + await interaction.reply({ + components: [ + buildNominationNoticeContainer( + nominationConfig.copy.nominationExpired, + "#8b949e" + ) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + } + + const id = parseNominationId(data.id) + if (!id) { + await interaction.reply({ + components: [ + buildNominationNoticeContainer( + nominationConfig.copy.invalidNomination, + "#f85149" + ) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + const nomination = await getNomination(id) + if (!nomination) { + await interaction.reply({ + components: [ + buildNominationNoticeContainer( + nominationConfig.copy.invalidNomination, + "#f85149" + ) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + if (isNominationExpired(nomination)) { + await replyWithExpired(nomination) + return + } + + if (nomination.status === "approved") { + await interaction.reply({ + components: [ + buildNominationNoticeContainer(nominationConfig.copy.alreadyComplete) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + if (!hasApproverRole(interaction)) { + await interaction.reply({ + components: [ + buildNominationNoticeContainer( + nominationConfig.copy.noPermission, + "#f85149" + ) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + const approverId = interaction.user?.id ?? interaction.userId + if (!approverId) { + await interaction.reply({ + components: [ + buildNominationNoticeContainer( + nominationConfig.copy.invalidNomination, + "#f85149" + ) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + const recorded = await recordNominationApproval(nomination.id, approverId) + const approverIds = await getNominationApproverIds(nomination.id) + if (!recorded && approverIds.length < nomination.requiredApprovals) { + await interaction.reply({ + components: [ + buildNominationNoticeContainer(nominationConfig.copy.alreadyApproved) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + if (approverIds.length < nomination.requiredApprovals) { + await interaction.message?.edit({ + components: [buildNominationContainer(nomination, approverIds)], + allowedMentions: { parse: [] } + }).catch(() => null) + await interaction.reply({ + components: [ + buildNominationNoticeContainer( + `${nominationConfig.copy.approvalRecorded} ${approverIds.length}/${nomination.requiredApprovals}.`, + "#3fb950" + ) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + const approvedNomination = await markNominationApproved(nomination.id) + if (!approvedNomination) { + const latestNomination = await getNomination(nomination.id) + if (latestNomination && isNominationExpired(latestNomination)) { + await replyWithExpired(latestNomination) + return + } + + await interaction.reply({ + components: [ + buildNominationNoticeContainer(nominationConfig.copy.alreadyComplete) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + let roleAdded = false + try { + roleAdded = await addTargetRole(approvedNomination) + } catch (error) { + console.error( + `Failed to add role for nomination ${approvedNomination.id}:`, + error + ) + } + + if (!roleAdded) { + const restoredNomination = + await restoreApprovedNominationToSubmitted(approvedNomination.id) + if (restoredNomination && isNominationExpired(restoredNomination)) { + await replyWithExpired(restoredNomination) + return + } + + await interaction.message?.edit({ + components: [ + buildNominationContainer(restoredNomination ?? nomination, approverIds) + ], + allowedMentions: { parse: [] } + }).catch(() => null) + await interaction.reply({ + components: [ + buildNominationNoticeContainer( + nominationConfig.copy.roleAddFailed, + "#f85149" + ) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + return + } + + await interaction.message?.edit({ + components: [buildNominationContainer(approvedNomination, approverIds)], + allowedMentions: { parse: [] } + }).catch(() => null) + await interaction.reply({ + components: [ + buildNominationNoticeContainer( + nominationConfig.copy.approvalRecorded, + "#3fb950" + ) + ], + ephemeral: true, + allowedMentions: { parse: [] } + }) + } +} + +export const nominationComponents = [ + new NominationApproveButton() +] diff --git a/src/config/nominations.ts b/src/config/nominations.ts new file mode 100644 index 0000000..1aa358c --- /dev/null +++ b/src/config/nominations.ts @@ -0,0 +1,31 @@ +export const nominationConfig = { + guildId: "1456350064065904867", + nominationChannelIds: ["1471742293055635536"], + approverRoleIds: ["1477360613125787678"], + targetRoleId: "1470356404706607227", + requiredApprovals: 3, + expirationHours: 48, + maxReasonLength: 500, + commandName: "nominate", + copy: { + title: "🐚🐚 Society nomination! 🐚🐚", + buttonLabel: "Approve", + wrongChannel: "This command can only be used in a secret channel... 🐚", + selfNomination: "You cannot nominate yourself.", + reasonRequired: "Reason required. The shell demands context.", + reasonTooLong: "Please keep the reason to 500 characters or fewer.", + botNomination: "Bots cannot receive the shell. They know what they did.", + userNotFound: "User not found in the server.", + alreadyHasRole: "That user already has Shell Society.", + alreadyPending: "That user already has an open Shell Society nomination.", + noPermission: "Community Team only. Nice try though.", + alreadyApproved: "You already approved this one. The shell remembers.", + approvalRecorded: "Approval recorded.", + nominationPosted: "Nomination posted.", + nominationPostFailed: "Could not post the nomination. Please try again.", + nominationExpired: "This nomination has expired.", + alreadyComplete: "This nomination is already complete.", + roleAddFailed: "Could not add Shell Society. Check bot permissions and role order.", + invalidNomination: "Could not load this nomination." + } +} diff --git a/src/data/nominations.ts b/src/data/nominations.ts new file mode 100644 index 0000000..8c091a1 --- /dev/null +++ b/src/data/nominations.ts @@ -0,0 +1,249 @@ +import { and, asc, eq, gt, lte, sql } from "drizzle-orm" +import { nominationConfig } from "../config/nominations.js" +import { getDb } from "../db.js" +import { + nominationApprovals, + nominations, + type Nomination +} from "../db/schema.js" + +export type NominationStatus = "submitted" | "approved" | "expired" + +type CreateNominationInput = { + guildId: string + channelId: string + nomineeId: string + nominatorId: string + reason: string + expiresAt: string + targetRoleId: string + requiredApprovals: number +} + +const now = sql`strftime('%Y-%m-%dT%H:%M:%fZ', 'now')` +const fallbackExpirationModifier = `+${nominationConfig.expirationHours} hours` +const expiryDeadline = sql`coalesce( + ${nominations.expiresAt}, + strftime('%Y-%m-%dT%H:%M:%fZ', ${nominations.createdAt}, ${fallbackExpirationModifier}) +)` + +const getNominationExpiryTime = (nomination: Nomination) => { + if (nomination.expiresAt) { + return Date.parse(nomination.expiresAt) + } + + const createdAtTime = Date.parse(nomination.createdAt) + if (Number.isNaN(createdAtTime)) { + return Number.POSITIVE_INFINITY + } + + return createdAtTime + nominationConfig.expirationHours * 60 * 60 * 1000 +} + +export const createNomination = async ( + input: CreateNominationInput +): Promise => { + const [nomination] = await getDb() + .insert(nominations) + .values({ + ...input, + status: "submitted" + }) + .onConflictDoNothing() + .returning() + + return nomination ?? null +} + +export const getNomination = async (id: number): Promise => { + const [nomination] = await getDb() + .select() + .from(nominations) + .where(eq(nominations.id, id)) + .limit(1) + + return nomination ?? null +} + +export const deleteNomination = async (nominationId: number): Promise => { + await getDb().delete(nominations).where(eq(nominations.id, nominationId)) +} + +export const setNominationMessageId = async ( + nominationId: number, + messageId: string +): Promise => { + await getDb() + .update(nominations) + .set({ + messageId, + updatedAt: now + }) + .where(eq(nominations.id, nominationId)) +} + +export const isNominationExpired = ( + nomination: Nomination, + referenceDate = new Date() +) => + nomination.status === "expired" || + (nomination.status === "submitted" && + getNominationExpiryTime(nomination) <= referenceDate.getTime()) + +export const getActiveNominationForNominee = async ( + guildId: string, + nomineeId: string, + targetRoleId: string +): Promise => { + const [nomination] = await getDb() + .select() + .from(nominations) + .where( + and( + eq(nominations.guildId, guildId), + eq(nominations.nomineeId, nomineeId), + eq(nominations.targetRoleId, targetRoleId), + eq(nominations.status, "submitted"), + gt(expiryDeadline, now) + ) + ) + .limit(1) + + return nomination ?? null +} + +export const recordNominationApproval = async ( + nominationId: number, + approverId: string +): Promise => { + const [approval] = await getDb() + .insert(nominationApprovals) + .values({ + nominationId, + approverId + }) + .onConflictDoNothing({ + target: [nominationApprovals.nominationId, nominationApprovals.approverId] + }) + .returning() + + return Boolean(approval) +} + +export const getNominationApproverIds = async ( + nominationId: number +): Promise => { + const approvals = await getDb() + .select({ approverId: nominationApprovals.approverId }) + .from(nominationApprovals) + .where(eq(nominationApprovals.nominationId, nominationId)) + .orderBy(asc(nominationApprovals.createdAt), asc(nominationApprovals.id)) + + return approvals.map((approval) => approval.approverId) +} + +export const markNominationApproved = async ( + nominationId: number +): Promise => { + const [nomination] = await getDb() + .update(nominations) + .set({ + status: "approved", + completedAt: now, + updatedAt: now + }) + .where( + and( + eq(nominations.id, nominationId), + eq(nominations.status, "submitted"), + gt(expiryDeadline, now) + ) + ) + .returning() + + return nomination ?? null +} + +export const restoreApprovedNominationToSubmitted = async ( + nominationId: number +): Promise => { + const [nomination] = await getDb() + .update(nominations) + .set({ + status: "submitted", + completedAt: null, + updatedAt: now + }) + .where( + and( + eq(nominations.id, nominationId), + eq(nominations.status, "approved") + ) + ) + .returning() + + return nomination ?? null +} + +export const listExpiredSubmittedNominations = async ( + limit = 25 +): Promise => + getDb() + .select() + .from(nominations) + .where( + and( + eq(nominations.status, "submitted"), + lte(expiryDeadline, now) + ) + ) + .orderBy(asc(expiryDeadline), asc(nominations.id)) + .limit(limit) + +export const markNominationExpired = async ( + nominationId: number +): Promise => { + const [nomination] = await getDb() + .update(nominations) + .set({ + status: "expired", + completedAt: now, + updatedAt: now + }) + .where( + and( + eq(nominations.id, nominationId), + eq(nominations.status, "submitted"), + lte(expiryDeadline, now) + ) + ) + .returning() + + return nomination ?? null +} + +export const markExpiredSubmittedNominationForNominee = async ( + guildId: string, + nomineeId: string, + targetRoleId: string +): Promise => { + const [nomination] = await getDb() + .update(nominations) + .set({ + status: "expired", + completedAt: now, + updatedAt: now + }) + .where( + and( + eq(nominations.guildId, guildId), + eq(nominations.nomineeId, nomineeId), + eq(nominations.targetRoleId, targetRoleId), + eq(nominations.status, "submitted"), + lte(expiryDeadline, now) + ) + ) + .returning() + + return nomination ?? null +} diff --git a/src/db/schema.ts b/src/db/schema.ts index 4976642..b9cf27d 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -220,6 +220,52 @@ export const claimRequests = sqliteTable( ] ) +export const nominations = sqliteTable( + "nominations", + { + id: integer().primaryKey({ autoIncrement: true }), + guildId: text("guild_id").notNull(), + channelId: text("channel_id").notNull(), + nomineeId: text("nominee_id").notNull(), + nominatorId: text("nominator_id").notNull(), + reason: text().notNull().default("No reason provided."), + messageId: text("message_id"), + targetRoleId: text("target_role_id").notNull(), + requiredApprovals: integer("required_approvals").notNull(), + status: text().notNull().default("submitted"), + expiresAt: text("expires_at"), + completedAt: text("completed_at"), + createdAt: text("created_at") + .notNull() + .default(sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`), + updatedAt: text("updated_at") + .notNull() + .default(sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`) + }, + (table) => [ + uniqueIndex("idx_nominations_submitted_unique") + .on(table.guildId, table.nomineeId, table.targetRoleId) + .where(sql`${table.status} = 'submitted'`), + index("idx_nominations_status").on(table.status) + ] +) + +export const nominationApprovals = sqliteTable( + "nomination_approvals", + { + id: integer().primaryKey({ autoIncrement: true }), + nominationId: integer("nomination_id").notNull(), + approverId: text("approver_id").notNull(), + createdAt: text("created_at") + .notNull() + .default(sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`) + }, + (table) => [ + uniqueIndex("idx_nomination_approvals_nomination_approver").on(table.nominationId, table.approverId), + index("idx_nomination_approvals_nomination_id").on(table.nominationId) + ] +) + export type KeyValue = typeof keyValue.$inferSelect export type NewKeyValue = typeof keyValue.$inferInsert export type HelperEvent = typeof helperEvents.$inferSelect @@ -238,3 +284,7 @@ export type ClawhubContentRightsEvent = typeof clawhubContentRightsEvents.$infer export type NewClawhubContentRightsEvent = typeof clawhubContentRightsEvents.$inferInsert export type ClaimRequest = typeof claimRequests.$inferSelect export type NewClaimRequest = typeof claimRequests.$inferInsert +export type Nomination = typeof nominations.$inferSelect +export type NewNomination = typeof nominations.$inferInsert +export type NominationApproval = typeof nominationApprovals.$inferSelect +export type NewNominationApproval = typeof nominationApprovals.$inferInsert diff --git a/src/index.ts b/src/index.ts index 3431bf9..8e0f4d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import ClaimCommand from "./commands/claim.js" import GithubCommand from "./commands/github.js" import MaintainerCommand from "./commands/maintainer.js" import HelperRootCommand from "./commands/helper.js" +import NominateCommand from "./commands/nominate.js" import RoleCommand from "./commands/role.js" import SayRootCommand from "./commands/say.js" import SolvedModCommand from "./commands/solvedMod.js" @@ -20,6 +21,7 @@ import { formReviewModals } from "./forms/reviewButtons.js" import { fscRequestComponents } from "./components/fscRequestButtons.js" +import { nominationComponents } from "./components/nominationButtons.js" import { whoisDeleteComponents } from "./components/whoisDeleteButton.js" import { hydrateRuntimeEnv, type HermitEnv } from "./runtime/env.js" import { @@ -29,6 +31,7 @@ import { } from "./server/claimServer.js" import { handleFormsRequest } from "./forms/server.js" import { registerHelperLogsRoutes } from "./server/helperLogsServer.js" +import { runNominationExpiry } from "./services/nominationExpiry.js" import { runThreadLengthMonitor } from "./services/threadLengthMonitor.js" import { handleContentRightsApiRequest } from "./clawhubContentRights/api.js" @@ -53,6 +56,7 @@ export const client = new Client( new RoleCommand(), new HelperRootCommand(), new ClaimCommand(), + new NominateCommand(), new MaintainerCommand(), new AdminCommand() ], @@ -69,6 +73,7 @@ export const client = new Client( ...claimReviewComponents, ...formReviewComponents, ...fscRequestComponents, + ...nominationComponents, ...whoisDeleteComponents ], modals: [...claimReviewModals, ...formReviewModals] @@ -123,9 +128,12 @@ export default { waitUntil: ctx.waitUntil.bind(ctx) }) }, - scheduled(_controller: ScheduledController, env: HermitEnv, ctx: ExecutionContext) { + scheduled(controller: ScheduledController, env: HermitEnv, ctx: ExecutionContext) { hydrateRuntimeEnv(env) - ctx.waitUntil(runThreadLengthMonitor(client)) + ctx.waitUntil(runNominationExpiry(client)) + if (!controller.cron || controller.cron === "0 */2 * * *") { + ctx.waitUntil(runThreadLengthMonitor(client)) + } } } satisfies ExportedHandler diff --git a/src/services/nominationExpiry.ts b/src/services/nominationExpiry.ts new file mode 100644 index 0000000..f82a8a2 --- /dev/null +++ b/src/services/nominationExpiry.ts @@ -0,0 +1,52 @@ +import { + type Client, + Routes, + serializePayload +} from "@buape/carbon" +import { buildNominationContainer } from "../components/nominationButtons.js" +import { + getNominationApproverIds, + listExpiredSubmittedNominations, + markNominationExpired +} from "../data/nominations.js" +import type { Nomination } from "../db/schema.js" + +export const editNominationMessageExpired = async ( + client: Client, + nomination: Nomination +) => { + if (!nomination.messageId) { + return + } + + const approverIds = await getNominationApproverIds(nomination.id) + await client.rest.patch( + Routes.channelMessage(nomination.channelId, nomination.messageId), + { + body: serializePayload({ + components: [buildNominationContainer(nomination, approverIds)], + allowedMentions: { parse: [] } + }) + } + ) +} + +export const runNominationExpiry = async (client: Client) => { + const expiredNominations = await listExpiredSubmittedNominations() + + for (const nomination of expiredNominations) { + const expiredNomination = await markNominationExpired(nomination.id) + if (!expiredNomination) { + continue + } + + try { + await editNominationMessageExpired(client, expiredNomination) + } catch (error) { + console.error( + `Failed to edit expired nomination message ${expiredNomination.id}:`, + error + ) + } + } +} diff --git a/tests/nominations.test.ts b/tests/nominations.test.ts new file mode 100644 index 0000000..2ea1bcc --- /dev/null +++ b/tests/nominations.test.ts @@ -0,0 +1,245 @@ +import { Database } from "bun:sqlite" +import { describe, expect, it } from "bun:test" +import { readdirSync, readFileSync } from "node:fs" +import { nominationConfig } from "../src/config/nominations.js" + +const nominationMigrationPaths = readdirSync("drizzle") + .filter((file) => /000[4-7]_.*\.sql/.test(file)) + .sort() + +if (nominationMigrationPaths.length !== 4) { + throw new Error("Could not find nomination migrations") +} + +const applyMigration = (database: Database, path: string) => { + const migration = readFileSync(path, "utf8") + for (const statement of migration.split("--> statement-breakpoint")) { + const trimmed = statement.trim() + if (trimmed.length > 0) { + database.run(trimmed) + } + } +} + +const createNomination = (database: Database) => { + database.run( + `insert into nominations ( + guild_id, + channel_id, + nominee_id, + nominator_id, + reason, + expires_at, + target_role_id, + required_approvals, + status + ) values (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [ + "guild-1", + "channel-1", + "nominee-1", + "nominator-1", + "excellent shell judgment", + "2099-01-01T00:00:00.000Z", + "role-1", + 3, + "submitted" + ] + ) + + return Number(database.query("select last_insert_rowid() as id").get()?.id) +} + +describe("nomination migration", () => { + it("requires three configured approvals", () => { + expect(nominationConfig.requiredApprovals).toBe(3) + expect(nominationConfig.maxReasonLength).toBeLessThanOrEqual(500) + }) + + it("allows three distinct approvers for one nomination", () => { + const database = new Database(":memory:") + for (const migrationPath of nominationMigrationPaths) { + applyMigration(database, `drizzle/${migrationPath}`) + } + const nominationId = createNomination(database) + + database.run( + "insert into nomination_approvals (nomination_id, approver_id) values (?, ?)", + [nominationId, "approver-1"] + ) + database.run( + "insert into nomination_approvals (nomination_id, approver_id) values (?, ?)", + [nominationId, "approver-2"] + ) + database.run( + "insert into nomination_approvals (nomination_id, approver_id) values (?, ?)", + [nominationId, "approver-3"] + ) + + const row = database + .query("select count(*) as count from nomination_approvals") + .get() as { count: number } + const nomination = database + .query("select reason, required_approvals as requiredApprovals from nominations where id = ?") + .get(nominationId) as { reason: string; requiredApprovals: number } + + expect(row.count).toBe(3) + expect(nomination.reason).toBe("excellent shell judgment") + expect(nomination.requiredApprovals).toBe(3) + }) + + it("rejects duplicate approval from the same approver", () => { + const database = new Database(":memory:") + for (const migrationPath of nominationMigrationPaths) { + applyMigration(database, `drizzle/${migrationPath}`) + } + const nominationId = createNomination(database) + + database.run( + "insert into nomination_approvals (nomination_id, approver_id) values (?, ?)", + [nominationId, "approver-1"] + ) + + expect(() => + database.run( + "insert into nomination_approvals (nomination_id, approver_id) values (?, ?)", + [nominationId, "approver-1"] + ) + ).toThrow() + }) + + it("allows only one submitted nomination per nominee and target role", () => { + const database = new Database(":memory:") + for (const migrationPath of nominationMigrationPaths) { + applyMigration(database, `drizzle/${migrationPath}`) + } + createNomination(database) + + expect(() => createNomination(database)).toThrow() + }) + + it("allows a new nomination after the previous nomination is approved", () => { + const database = new Database(":memory:") + for (const migrationPath of nominationMigrationPaths) { + applyMigration(database, `drizzle/${migrationPath}`) + } + const nominationId = createNomination(database) + database.run("update nominations set status = ? where id = ?", [ + "approved", + nominationId + ]) + + createNomination(database) + + const row = database + .query("select count(*) as count from nominations") + .get() as { count: number } + expect(row.count).toBe(2) + }) + + it("allows a new nomination after the previous nomination is expired", () => { + const database = new Database(":memory:") + for (const migrationPath of nominationMigrationPaths) { + applyMigration(database, `drizzle/${migrationPath}`) + } + const nominationId = createNomination(database) + database.run("update nominations set status = ? where id = ?", [ + "expired", + nominationId + ]) + + createNomination(database) + + const row = database + .query("select count(*) as count from nominations") + .get() as { count: number } + expect(row.count).toBe(2) + }) + + it("backfills expiry for nominations created before expiry columns existed", () => { + const database = new Database(":memory:") + for (const migrationPath of nominationMigrationPaths.slice(0, -1)) { + applyMigration(database, `drizzle/${migrationPath}`) + } + database.run( + `insert into nominations ( + guild_id, + channel_id, + nominee_id, + nominator_id, + reason, + target_role_id, + required_approvals, + status + ) values (?, ?, ?, ?, ?, ?, ?, ?)`, + [ + "guild-1", + "channel-1", + "nominee-1", + "nominator-1", + "excellent shell judgment", + "role-1", + 3, + "submitted" + ] + ) + + applyMigration(database, `drizzle/${nominationMigrationPaths.at(-1)}`) + + const row = database + .query("select created_at as createdAt, expires_at as expiresAt from nominations") + .get() as { createdAt: string; expiresAt: string } + expect(row.expiresAt).toBeString() + expect(row.expiresAt > row.createdAt).toBe(true) + }) + + it("expires submitted nominations left with a null expiry timestamp", () => { + const database = new Database(":memory:") + for (const migrationPath of nominationMigrationPaths) { + applyMigration(database, `drizzle/${migrationPath}`) + } + database.run( + `insert into nominations ( + guild_id, + channel_id, + nominee_id, + nominator_id, + reason, + expires_at, + target_role_id, + required_approvals, + status, + created_at + ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-49 hours'))`, + [ + "guild-1", + "channel-1", + "nominee-1", + "nominator-1", + "excellent shell judgment", + null, + "role-1", + 3, + "submitted" + ] + ) + const deadline = `coalesce(expires_at, strftime('%Y-%m-%dT%H:%M:%fZ', created_at, '+${nominationConfig.expirationHours} hours'))` + + database.run( + `update nominations + set status = 'expired' + where guild_id = ? + and nominee_id = ? + and target_role_id = ? + and status = 'submitted' + and ${deadline} <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`, + ["guild-1", "nominee-1", "role-1"] + ) + + const expired = database + .query("select status from nominations where expires_at is null") + .get() as { status: string } + expect(expired.status).toBe("expired") + createNomination(database) + }) +}) diff --git a/wrangler.jsonc b/wrangler.jsonc index dfb35a2..17151c1 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -50,6 +50,6 @@ } ], "triggers": { - "crons": ["0 */2 * * *"] + "crons": ["*/15 * * * *", "0 */2 * * *"] } }