From e6179e20e5c52303fe1374cb2a5d8f8389a77275 Mon Sep 17 00:00:00 2001 From: CJ_Clippy Date: Wed, 7 Aug 2024 11:30:29 -0800 Subject: [PATCH] add ggshield --- .cache_ggshield | 1 + .gitguardian.yaml | 5 + .gitignore | 2 + .pre-commit-config.yaml | 7 + devbox.json | 11 +- devbox.lock | 142 ++++++++++++++++++ requirements.txt | 3 + services/bot/pnpm-lock.yaml | 36 ----- services/bot/src/commands/record.ts | 64 +------- .../bot/src/tasks/update_discord_message.ts | 132 +++++++++------- .../00004_create-trigger-function.sql | 22 +-- .../00005_add-trigger-for-record-update.sql | 21 +++ 12 files changed, 285 insertions(+), 161 deletions(-) create mode 100644 .cache_ggshield create mode 100644 .gitguardian.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 requirements.txt create mode 100644 services/migrations/migrations/00005_add-trigger-for-record-update.sql diff --git a/.cache_ggshield b/.cache_ggshield new file mode 100644 index 0000000..168e0a9 --- /dev/null +++ b/.cache_ggshield @@ -0,0 +1 @@ +{"last_found_secrets": [{"match": "7630852e9a6a0aecb849c91d14d426ca88187886fdf466189d67145856bdac3e", "name": "Generic Password - charts/postgresql/postgresql/templates/secrets.yaml"}]} \ No newline at end of file diff --git a/.gitguardian.yaml b/.gitguardian.yaml new file mode 100644 index 0000000..03b034b --- /dev/null +++ b/.gitguardian.yaml @@ -0,0 +1,5 @@ +secret: + ignored_matches: + - match: 7630852e9a6a0aecb849c91d14d426ca88187886fdf466189d67145856bdac3e + name: Generic Password - charts/postgresql/postgresql/templates/secrets.yaml +version: 2 diff --git a/.gitignore b/.gitignore index 08f5f28..baea4d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.venv/ + **/.env* *~ packages/**/isolate/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..858a45c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/gitguardian/ggshield + rev: v1.30.2 + hooks: + - id: ggshield + language_version: python3 + stages: [commit] \ No newline at end of file diff --git a/devbox.json b/devbox.json index b1b9800..cbe55f4 100644 --- a/devbox.json +++ b/devbox.json @@ -9,16 +9,21 @@ "kubernetes-helm@latest", "k9s@latest", "ffmpeg@latest", - "yt-dlp@latest" + "yt-dlp@latest", + "python312@latest", + "python312Packages.pip@latest" ], "env": { "DEVBOX_COREPACK_ENABLED": "true", "ENV": "development", - "KUBECONFIG": "$HOME/.kube/futureporn.yaml" + "KUBECONFIG": "$HOME/.kube/futureporn.yaml", + "VENV_DIR": ".venv" }, "shell": { "init_hook": [ - "echo Welcome to Futureporn devbox" + "echo Welcome to Futureporn devbox", + ". $VENV_DIR/bin/activate", + "pip install -r requirements.txt" ], "scripts": { "test": [ diff --git a/devbox.lock b/devbox.lock index b771845..1a6a3dd 100644 --- a/devbox.lock +++ b/devbox.lock @@ -490,6 +490,148 @@ } } }, + "python312@latest": { + "last_modified": "2024-06-12T20:55:33Z", + "plugin_version": "0.0.3", + "resolved": "github:NixOS/nixpkgs/a9858885e197f984d92d7fe64e9fff6b2e488d40#python312", + "source": "devbox-search", + "version": "3.12.3", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/65ackbgqn02p6fy75rksjbp17zj6440j-python3-3.12.3", + "default": true + } + ], + "store_path": "/nix/store/65ackbgqn02p6fy75rksjbp17zj6440j-python3-3.12.3" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/59siylp1k9i3ch4d9m3vs5jp5z56b5sg-python3-3.12.3", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/ji094818a78hrkag26202b21nr5v1pgk-python3-3.12.3-debug" + } + ], + "store_path": "/nix/store/59siylp1k9i3ch4d9m3vs5jp5z56b5sg-python3-3.12.3" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/nrskm5p85y6751k02ih51bpzfag8g5r8-python3-3.12.3", + "default": true + } + ], + "store_path": "/nix/store/nrskm5p85y6751k02ih51bpzfag8g5r8-python3-3.12.3" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/zwvdwiw0zg5liz6lkkkqyab035parhld-python3-3.12.3", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/wj6mffmpvly3md2hqnq2jj8zrrw5qck9-python3-3.12.3-debug" + } + ], + "store_path": "/nix/store/zwvdwiw0zg5liz6lkkkqyab035parhld-python3-3.12.3" + } + } + }, + "python312Packages.pip@latest": { + "last_modified": "2024-07-07T07:43:47Z", + "plugin_version": "0.0.2", + "resolved": "github:NixOS/nixpkgs/b60793b86201040d9dee019a05089a9150d08b5b#python312Packages.pip", + "source": "devbox-search", + "version": "24.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2p2wyydd5m787ms85fcic1ap7ql2j2gs-python3.12-pip-24.0", + "default": true + }, + { + "name": "man", + "path": "/nix/store/pad6sdd500ikddhym5p2h3krblbazs6k-python3.12-pip-24.0-man", + "default": true + }, + { + "name": "dist", + "path": "/nix/store/6wpcdjrk6wj95wb2v7q5zm06cmj9w1c7-python3.12-pip-24.0-dist" + } + ], + "store_path": "/nix/store/2p2wyydd5m787ms85fcic1ap7ql2j2gs-python3.12-pip-24.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/3m2j8ydrz8rj42aramlvh7hkc15izm52-python3.12-pip-24.0", + "default": true + }, + { + "name": "man", + "path": "/nix/store/z3lq18q7slp92q7wgqm34s7v4vg222hw-python3.12-pip-24.0-man", + "default": true + }, + { + "name": "dist", + "path": "/nix/store/y49wc2lcia8ji5sa9szc7b6gk7mr2mhi-python3.12-pip-24.0-dist" + } + ], + "store_path": "/nix/store/3m2j8ydrz8rj42aramlvh7hkc15izm52-python3.12-pip-24.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/rh3z88c38c9xn52rdkp1swxy0rml3bjv-python3.12-pip-24.0", + "default": true + }, + { + "name": "man", + "path": "/nix/store/j9csjm7dq29qpyvwnv48fslqm0b6dwzi-python3.12-pip-24.0-man", + "default": true + }, + { + "name": "dist", + "path": "/nix/store/rp5mhjjjll87pa7lmdad5b01wgz8qa91-python3.12-pip-24.0-dist" + } + ], + "store_path": "/nix/store/rh3z88c38c9xn52rdkp1swxy0rml3bjv-python3.12-pip-24.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/9gd8kj409msfv599456ywbyi97izlbr4-python3.12-pip-24.0", + "default": true + }, + { + "name": "man", + "path": "/nix/store/57hvcf28az2fx50z6kx3w6ijl0yazy4z-python3.12-pip-24.0-man", + "default": true + }, + { + "name": "dist", + "path": "/nix/store/0x4s6vyfivk6fck7lp1dwrg79gp0nvmg-python3.12-pip-24.0-dist" + } + ], + "store_path": "/nix/store/9gd8kj409msfv599456ywbyi97izlbr4-python3.12-pip-24.0" + } + } + }, "tilt@latest": { "last_modified": "2024-07-15T21:47:20Z", "resolved": "github:NixOS/nixpkgs/b2c1f10bfbb3f617ea8e8669ac13f3f56ceb2ea2#tilt", diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a8fc4c5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pre-commit +ggshield +click \ No newline at end of file diff --git a/services/bot/pnpm-lock.yaml b/services/bot/pnpm-lock.yaml index 21bb3d4..90b8295 100644 --- a/services/bot/pnpm-lock.yaml +++ b/services/bot/pnpm-lock.yaml @@ -46,42 +46,6 @@ importers: specifier: ^5.5.3 version: 5.5.4 - ../..: {} - - ../../packages/image: {} - - ../../packages/infra: {} - - ../../packages/meal: {} - - ../../packages/old: {} - - ../../packages/scout: {} - - ../../packages/storage: {} - - ../../packages/taco: {} - - ../../packages/types: {} - - ../../packages/utils: {} - - ../../packages/video: {} - - ../../packages/worker: {} - - ../capture: {} - - ../mailbox: {} - - ../migrations: {} - - ../next: {} - - ../strapi: {} - - ../uppy: {} - packages: '@babel/code-frame@7.24.7': diff --git a/services/bot/src/commands/record.ts b/services/bot/src/commands/record.ts index 82d2dc0..e6d9264 100644 --- a/services/bot/src/commands/record.ts +++ b/services/bot/src/commands/record.ts @@ -51,15 +51,14 @@ createCommand({ async execute(interaction: Interaction) { await interaction.defer() - console.log('interation.data as follows') - console.log(interaction.data) + // console.log('interation.data as follows') + // console.log(interaction.data) const options = interaction.data?.options if (!options) throw new Error(`interaction options was undefined. it's expected to be an array of options.`); const urlOption = options.find((o) => o.name === 'url') if (!urlOption) throw new Error(`url option was missing from interaction data`); const url = ''+urlOption.value if (!url) throw new Error(`url was missing from interaction data options`); - // const url = (interaction.data?.options?.find(o => o.name === 'url')?.value) ?? undefined; // respond to the interaction and get a message ID which we will then add to the database Record const embeds = new EmbedsBuilder() @@ -75,8 +74,8 @@ createCommand({ const response: InteractionCallbackData = { embeds } const message = await interaction.edit(response) - console.log('defferred, interaction message is as follows') - console.log(message) + // console.log('deferred, interaction message is as follows') + // console.log(message) if (!message?.id) { const msg = `message.id was empty, ruh roh raggy` console.error(msg) @@ -85,57 +84,8 @@ createCommand({ // @todo create record in db const record = await createRecordInDatabase(url, message.id.toString()) - console.log(record) + // console.log(record) - - // if (!interaction.data?.values) throw new Error('interaction data was missing values'); - // console.log(`while executing record command, the following values were seen.`) - // console.log(interaction.data.values) - - // const embeds = new EmbedsBuilder() - // .setTitle('My Embed') - // .setDescription('This is my new embed') - // .newEmbed() - // .setTitle('My Second Embed') - // await interaction.respond({ - // embeds: - // }) - }, -}) - - -// const statusEmbed = new EmbedBuilder() -// .setTitle('Pending') -// .setDescription('Waiting for a worker to accept the job.') -// .setColor(2326507) - -// const buttonRow = new ActionRowBuilder() -// .addComponents([ -// new ButtonBuilder() -// .setCustomId('stop') -// .setLabel('Stop Recording') -// .setEmoji('🛑') -// .setStyle(ButtonStyle.Danger), -// ]); - -// const command: CreateSlashApplicationCommand = { -// name: 'record', -// description: 'Record a livestream.', -// options: [ -// { -// name: 'url', -// description: 'URL of the livestream', -// type: ApplicationCommandOptionTypes.String -// }, -// ], -// async execute(interaction: Interaction) { -// const ping = Date.now() - snowflakeToTimestamp(interaction.id) - -// const embeds = createEmbeds().setTitle(`The bot ping is ${ping}ms`) - -// await interaction.respond({ embeds }) -// }, -// } - -// export default command \ No newline at end of file + } +}) \ No newline at end of file diff --git a/services/bot/src/tasks/update_discord_message.ts b/services/bot/src/tasks/update_discord_message.ts index 5433ec2..0e711d7 100644 --- a/services/bot/src/tasks/update_discord_message.ts +++ b/services/bot/src/tasks/update_discord_message.ts @@ -3,29 +3,19 @@ import type { RecordingState } from '@futureporn/types' import { type Task, type Helpers } from 'graphile-worker' import { add } from 'date-fns' import prettyBytes from 'pretty-bytes' -import { - type APIEmbedField, - Client, - GatewayIntentBits, - TextChannel, - ActionRowBuilder, - ButtonBuilder, - type MessageActionRowComponentBuilder, - ButtonStyle, - EmbedBuilder, - Guild -} from 'discord.js'; +import { EmbedsBuilder, type Component } from '@discordeno/bot' + interface Payload { - recordId: number; + record_id: number; } function assertPayload(payload: any): asserts payload is Payload { if (typeof payload !== "object" || !payload) throw new Error("invalid payload"); - if (!payload.recordId) throw new Error(`recordId was absent in the payload`); + if (!payload.record_id) throw new Error(`record_id was absent in the payload`); } @@ -111,7 +101,8 @@ async function getRecordFromDatabase(recordId: number) { export const updateDiscordMessage: Task = async function (payload, helpers: Helpers) { try { assertPayload(payload) - const { recordId } = payload + const { record_id } = payload + const recordId = record_id helpers.logger.info(`updateDiscordMessage() with recordId=${recordId}`) const record = await getRecordFromDatabase(recordId) const { discord_message_id, recording_state, file_size, url } = record @@ -133,64 +124,97 @@ export const updateDiscordMessage: Task = async function (payload, helpers: Help function getStatusEmbed({ recordingState, recordId, fileSize, url }: { fileSize: number, recordingState: RecordingState, recordId: number, url: string }) { - let title, description, color, fields; - title = `Record ${recordId}` - fields = [ - { name: 'Status', value: 'Pending', inline: true }, - { name: 'Filesize', value: `${fileSize} bytes (${prettyBytes(fileSize)})`, inline: true }, - { name: 'URL', value: url, inline: false }, - ] as APIEmbedField[] + const embeds = new EmbedsBuilder() + .setTitle(`Record ${recordId}`) + .setFields([ + { name: 'Status', value: 'Pending', inline: true }, + { name: 'Filesize', value: `${fileSize} bytes (${prettyBytes(fileSize)})`, inline: true }, + { name: 'URL', value: url, inline: false }, + ]) if (recordingState === 'pending') { - description = "Waiting for a worker to accept the job." - color = 2326507 + embeds + .setDescription("Waiting for a worker to accept the job.") + .setColor(2326507) } else if (recordingState === 'recording') { - description = 'The stream is being recorded.' - color = 392960 + embeds + .setDescription('The stream is being recorded.') + .setColor(392960) } else if (recordingState === 'aborted') { - description = "The recording was stopped by the user." - color = 8289651 + embeds + .setDescription("The recording was stopped by the user.") + .setColor(8289651) } else if (recordingState === 'ended') { - description = "The recording has stopped." - color = 10855845 + embeds + .setDescription("The recording has stopped.") + .setColor(10855845) } else { - description = 'The recording is in an unknown state? (this is a bug.)' - color = 10855845 + embeds + .setDescription('The recording is in an unknown state? (this is a bug.)') + .setColor(10855845) } - return new EmbedBuilder().setTitle(title).setDescription(description).setColor(color).setFields(fields) + return embeds } function getButtonRow(state: RecordingState) { - let id, label, emoji, style; + + const button = new Component() + .setType("BUTTON") + + // // Button with raw types + // const button2 = new Component() + // .setType(2) + // .setStyle(4) + // .setLabel("DO NOT CLICK") + // .setCustomId("12345") + // .toJSON(); + + // const actionRow = new Component() + // .setType("ACTION_ROW") + // .setComponents(button, button2) + // .toJSON(); + + // return actionRow + + // Message to send + // const messageOptions = { content: "hello", components: [actionRow] }; + + // await client.helpers.sendMessage(channelId, messageOptions); // You can also use the Message Structure if (state === 'pending') { - id = 'stop'; - label = 'Cancel'; - emoji = '❌'; - style = ButtonStyle.Danger; + button + .setCustomId('stop') + .setLabel('Cancel') + .setEmoji('❌') + .setStyle('DANGER') } else if (state === 'recording') { - id = 'stop'; - label = 'Stop Recording'; - emoji = '🛑'; - style = ButtonStyle.Danger; + button + .setCustomId('stop') + .setLabel('Stop Recording') + .setEmoji('🛑') + .setStyle('DANGER') } else if (state === 'aborted') { - id = 'retry'; - label = 'Retry Recording'; - emoji = '🔄'; - style = ButtonStyle.Success; + button + .setCustomId('retry') + .setLabel('Retry Recording') + .setEmoji('🔄') + .setStyle('SUCCESS') } else if (state === 'ended') { - id = 'download'; - label = 'Download Recording'; - emoji = '📥'; - style = ButtonStyle.Primary; + button + .setCustomId('download') + .setLabel('Download Recording') + .setEmoji('📥') + .setStyle('PRIMARY') } else { - id = 'unknown'; - label = 'Unknown State'; - emoji = '🤔'; - style = ButtonStyle.Secondary; + button + .setCustomId('unknown') + .setLabel('Unknown State') + .setEmoji('🤔') + .setStyle('SECONDARY') } + const actionRow = new Component return new ActionRowBuilder() .addComponents([ new ButtonBuilder() diff --git a/services/migrations/migrations/00004_create-trigger-function.sql b/services/migrations/migrations/00004_create-trigger-function.sql index 4dd0bff..f3e42f8 100644 --- a/services/migrations/migrations/00004_create-trigger-function.sql +++ b/services/migrations/migrations/00004_create-trigger-function.sql @@ -9,17 +9,17 @@ CREATE FUNCTION public.tg__add_job() RETURNS trigger - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'pg_catalog', 'public', 'pg_temp' - AS $$ - begin - PERFORM graphile_worker.add_job(tg_argv[0], json_build_object( - 'url', NEW.url, - 'record_id', NEW.id - ), max_attempts := 12); - return NEW; - end; - $$; + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'pg_catalog', 'public', 'pg_temp' + AS $$ + begin + PERFORM graphile_worker.add_job(tg_argv[0], json_build_object( + 'url', NEW.url, + 'record_id', NEW.id + ), max_attempts := 12); + return NEW; + end; + $$; CREATE TRIGGER record diff --git a/services/migrations/migrations/00005_add-trigger-for-record-update.sql b/services/migrations/migrations/00005_add-trigger-for-record-update.sql new file mode 100644 index 0000000..bce7252 --- /dev/null +++ b/services/migrations/migrations/00005_add-trigger-for-record-update.sql @@ -0,0 +1,21 @@ + +CREATE FUNCTION public.tg__update_discord_message() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'pg_catalog', 'public', 'pg_temp' + AS $$ + begin + PERFORM graphile_worker.add_job('update_discord_message', json_build_object( + 'record_id', NEW.record_id + ), max_attempts := 3); + return NEW; + end; + $$; + + + +-- when a record is updated, we add a job in graphile to update_discord_message +CREATE TRIGGER record_update + AFTER UPDATE ON api.records + FOR EACH ROW + EXECUTE PROCEDURE public.tg__update_discord_message('update_discord_message'); +