update_discord_message implemented

This commit is contained in:
CJ_Clippy 2024-08-07 15:43:17 -08:00
parent d08fcc0867
commit 54572dbebe
8 changed files with 175 additions and 161 deletions

View File

@ -6,7 +6,7 @@ import { configs } from './config.ts'
export const bot = createProxyCache(
createBot({
token: configs.token,
intents: Intents.Guilds
intents: Intents.Guilds | Intents.GuildMessages
}),
{
desiredProps: {

View File

@ -69,7 +69,7 @@ createCommand({
{ name: 'Filesize', value: '0 bytes', inline: true},
{ name: 'URL', value: url, inline: false }
])
.setColor('#33eb23')
.setColor('#808080')
const response: InteractionCallbackData = { embeds }
const message = await interaction.edit(response)

View File

@ -1,18 +1,35 @@
if (!process.env.WORKER_CONNECTION_STRING) throw new Error("WORKER_CONNECTION_STRING was missing from env");
if (!process.env.POSTGREST_URL) throw new Error('Missing POSTGREST_URL env var');
if (!process.env.DISCORD_TOKEN) throw new Error('Missing DISCORD_TOKEN env var');
if (!process.env.DISCORD_CHANNEL_ID) throw new Error("DISCORD_CHANNEL_ID was missing from env");
if (!process.env.DISCORD_GUILD_ID) throw new Error("DISCORD_GUILD_ID was missing from env");
if (!process.env.AUTOMATION_USER_JWT) throw new Error('Missing AUTOMATION_USER_JWT env var');
const token = process.env.DISCORD_TOKEN!
const postgrestUrl = process.env.POSTGREST_URL!
const discordChannelId = process.env.DISCORD_CHANNEL_ID!
const discordGuildId = process.env.DISCORD_GUILD_ID!
const automationUserJwt = process.env.AUTOMATION_USER_JWT!
const connectionString = process.env.WORKER_CONNECTION_STRING!
console.log(`hello i am configs and configs.connectionString=${connectionString}`)
export const configs: Config = {
token,
postgrestUrl,
automationUserJwt,
}
export interface Config {
token: string;
postgrestUrl: string;
automationUserJwt: string;
discordGuildId: string;
discordChannelId: string;
connectionString: string;
}
export const configs: Config = {
token,
postgrestUrl,
automationUserJwt,
discordGuildId,
discordChannelId,
connectionString,
}

View File

@ -1,14 +1,12 @@
import 'dotenv/config'
// import loadCommands from './loadCommands.js'
// import deployCommands from './deployCommands.js'
// import loadEvents from './loadEvents.js'
// import updateDiscordMessage from './tasks/update_discord_message.js'
import { type WorkerUtils } from 'graphile-worker'
import updateDiscordMessage from './tasks/update_discord_message.js'
import { type WorkerUtils, type RunnerOptions, run } from 'graphile-worker'
import { bot } from './bot.ts'
import type { Interaction } from '@discordeno/bot'
import { importDirectory } from './utils/loader.ts'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'url';
import { configs } from './config.ts'
const __dirname = dirname(fileURLToPath(import.meta.url));
@ -18,43 +16,36 @@ export interface ExecuteArguments {
workerUtils: WorkerUtils;
}
if (!process.env.AUTOMATION_USER_JWT) throw new Error(`AUTOMATION_USER_JWT was missing from env`);
if (!process.env.DISCORD_TOKEN) throw new Error("DISCORD_TOKEN was missing from env");
if (!process.env.DISCORD_CHANNEL_ID) throw new Error("DISCORD_CHANNEL_ID was missing from env");
if (!process.env.DISCORD_GUILD_ID) throw new Error("DISCORD_GUILD_ID was missing from env");
if (!process.env.WORKER_CONNECTION_STRING) throw new Error("WORKER_CONNECTION_STRING was missing from env");
const preset: GraphileConfig.Preset = {
async function setupGraphileWorker() {
const preset: GraphileConfig.Preset = {
worker: {
connectionString: process.env.WORKER_CONNECTION_STRING,
connectionString: configs.connectionString,
concurrentJobs: 3,
fileExtensions: [".js", ".ts"]
fileExtensions: [".js", ".ts"],
taskDirectory: join(__dirname, 'tasks')
},
};
};
console.log('worker preset as follows')
console.log(preset)
const runnerOptions: RunnerOptions = {
preset
// concurrency: 3,
// connectionString: configs.connectionString,
// taskDirectory: join(__dirname, 'tasks'),
// taskList: {
// 'update_discord_message': updateDiscordMessage
// }
}
// async function setupGraphileWorker() {
// const runnerOptions: RunnerOptions = {
// preset,
// taskList: {
// 'updateDiscordMessage': updateDiscordMessage
// }
// }
// const runner = await run(runnerOptions)
// if (!runner) throw new Error('failed to initialize graphile worker');
// await runner.promise
// }
const runner = await run(runnerOptions)
if (!runner) throw new Error('failed to initialize graphile worker');
await runner.promise
}
// async function setupWorkerUtils() {
// const workerUtils = await makeWorkerUtils({
// preset
// });
// await workerUtils.migrate()
// return workerUtils
// }
async function main() {
async function setupBot() {
bot.logger.info('Starting @futureporn/bot.')
@ -64,16 +55,15 @@ async function main() {
bot.logger.info('Loading events...')
await importDirectory(join(__dirname, './events'))
// const commands = await loadCommands()
// if (!commands) throw new Error('there were no commands available to be loaded.');
// await deployCommands(commands.map((c) => c.data.toJSON()))
// console.log(`${commands.length} commands deployed: ${commands.map((c) => c.data.name).join(', ')}`)
// const workerUtils = await setupWorkerUtils()
// setupGraphileWorker()
await bot.start()
}
async function main() {
await setupBot()
await setupGraphileWorker()
}
main().catch((e) => {
console.error("error during main() function")
console.error(e)

View File

@ -3,9 +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 { EmbedsBuilder, type Component } from '@discordeno/bot'
import {
EmbedsBuilder,
ButtonStyles,
type ActionRow,
MessageComponentTypes,
type ButtonComponent,
type InputTextComponent,
type EditMessage,
type Message,
type Embed
} from '@discordeno/bot'
import { bot } from '../bot.ts'
import { configs } from '../config.ts'
interface Payload {
record_id: number;
@ -19,11 +29,6 @@ function assertPayload(payload: any): asserts payload is Payload {
}
if (!process.env.AUTOMATION_USER_JWT) throw new Error(`AUTOMATION_USER_JWT was missing from env`);
if (!process.env.DISCORD_TOKEN) throw new Error("DISCORD_TOKEN was missing from env");
if (!process.env.DISCORD_CHANNEL_ID) throw new Error("DISCORD_CHANNEL_ID was missing from env");
if (!process.env.DISCORD_GUILD_ID) throw new Error("DISCORD_GUILD_ID was missing from env");
async function editDiscordMessage({ helpers, recordingState, discordMessageId, url, fileSize, recordId }: { recordId: number, fileSize: number, url: string, helpers: Helpers, recordingState: RecordingState, discordMessageId: string }) {
@ -35,45 +40,51 @@ async function editDiscordMessage({ helpers, recordingState, discordMessageId, u
helpers.logger.info(`editDiscordMessage has begun with discordMessageId=${discordMessageId}, state=${recordingState}`)
// create a discord.js client
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
});
// const guild = await bot.cache.guilds.get(BigInt(configs.discordGuildId))
// const channel = guild?.channels.get(BigInt(configs.discordChannelId))
// Log in to Discord with your client's token
client.login(process.env.DISCORD_TOKEN);
// // const channel = await bot.cache.channels.get()
// console.log('channel as follows')
// console.log(channel)
const channelId = BigInt(configs.discordChannelId)
const updatedMessage: EditMessage = {
embeds: getStatusEmbed({ recordingState, fileSize, recordId, url }),
}
bot.helpers.editMessage(channelId, discordMessageId, updatedMessage)
// channel.
// const guild = await client.guilds.fetch(process.env.DISCORD_GUILD_ID!) as Guild
// if (!guild) throw new Error('guild was undefined');
// helpers.logger.info('here is the guild as follows')
// helpers.logger.info(guild.toString())
// helpers.logger.info(`fetching discord channel id=${process.env.DISCORD_CHANNEL_ID} from discord guild`)
// const channel = await client.channels.fetch(process.env.DISCORD_CHANNEL_ID!) as TextChannel
// if (!channel) throw new Error(`discord channel was undefined`);
// const message = await channel.messages.fetch(discordMessageId)
// helpers.logger.info(`discordMessageId=${discordMessageId}`)
// helpers.logger.info(message as any)
// const statusEmbed = getStatusEmbed({ recordId, recordingState, fileSize, url })
// const buttonRow = getButtonRow(recordingState)
const guild = await client.guilds.fetch(process.env.DISCORD_GUILD_ID!) as Guild
if (!guild) throw new Error('guild was undefined');
helpers.logger.info('here is the guild as follows')
helpers.logger.info(guild.toString())
helpers.logger.info(`fetching discord channel id=${process.env.DISCORD_CHANNEL_ID} from discord guild`)
const channel = await client.channels.fetch(process.env.DISCORD_CHANNEL_ID!) as TextChannel
if (!channel) throw new Error(`discord channel was undefined`);
const message = await channel.messages.fetch(discordMessageId)
helpers.logger.info(`discordMessageId=${discordMessageId}`)
helpers.logger.info(message as any)
const statusEmbed = getStatusEmbed({ recordId, recordingState, fileSize, url })
const buttonRow = getButtonRow(recordingState)
// // const embed = new EmbedBuilder().setTitle('Attachments');
// const embed = new EmbedBuilder().setTitle('Attachments');
// const updatedMessage = {
// embeds: [
// statusEmbed
// ],
// components: [
// buttonRow
// ]
// };
const updatedMessage = {
embeds: [
statusEmbed
],
components: [
buttonRow
]
};
message.edit(updatedMessage)
// message.edit(updatedMessage)
}
@ -121,14 +132,15 @@ export const updateDiscordMessage: Task = async function (payload, helpers: Help
}
}
function getStatusEmbed({
recordingState, recordId, fileSize, url
}: { fileSize: number, recordingState: RecordingState, recordId: number, url: string }) {
const embeds = new EmbedsBuilder()
.setTitle(`Record ${recordId}`)
.setFields([
{ name: 'Status', value: 'Pending', inline: true },
{ name: 'Filesize', value: `${fileSize} bytes (${prettyBytes(fileSize)})`, inline: true },
{ name: 'Status', value: recordingState.charAt(0).toUpperCase()+recordingState.slice(1), inline: true },
{ name: 'Filesize', value: prettyBytes(fileSize), inline: true },
{ name: 'URL', value: url, inline: false },
])
if (recordingState === 'pending') {
@ -157,72 +169,59 @@ function getStatusEmbed({
function getButtonRow(state: RecordingState) {
function getButtonRow(state: RecordingState): ActionRow {
const components: ButtonComponent[] = []
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') {
button
.setCustomId('stop')
.setLabel('Cancel')
.setEmoji('❌')
.setStyle('DANGER')
} else if (state === 'recording') {
button
.setCustomId('stop')
.setLabel('Stop Recording')
.setEmoji('🛑')
.setStyle('DANGER')
if (state === 'pending' || state === 'recording') {
const stopButton: ButtonComponent = {
type: MessageComponentTypes.Button,
customId: 'stop',
label: 'Cancel',
style: ButtonStyles.Danger
}
components.push(stopButton)
} else if (state === 'aborted') {
button
.setCustomId('retry')
.setLabel('Retry Recording')
.setEmoji('🔄')
.setStyle('SUCCESS')
const retryButton: ButtonComponent = {
type: MessageComponentTypes.Button,
customId: 'retry',
label: 'Retry Recording',
emoji: {
name: 'retry'
},
style: ButtonStyles.Secondary
}
components.push(retryButton)
} else if (state === 'ended') {
button
.setCustomId('download')
.setLabel('Download Recording')
.setEmoji('📥')
.setStyle('PRIMARY')
const downloadButton: ButtonComponent = {
type: MessageComponentTypes.Button,
customId: 'download',
label: 'Download Recording',
emoji: {
id: BigInt('1253191939461873756')
},
style: ButtonStyles.Success
}
components.push(downloadButton)
} else {
button
.setCustomId('unknown')
.setLabel('Unknown State')
.setEmoji('🤔')
.setStyle('SECONDARY')
const unknownButton: ButtonComponent = {
type: MessageComponentTypes.Button,
customId: 'unknown',
label: 'Unknown State',
emoji: {
name: 'thinking'
},
style: ButtonStyles.Primary
}
components.push(unknownButton)
}
const actionRow = new Component
return new ActionRowBuilder<MessageActionRowComponentBuilder>()
.addComponents([
new ButtonBuilder()
.setCustomId(id)
.setLabel(label)
.setEmoji(emoji)
.setStyle(style),
]);
const actionRow: ActionRow = {
type: MessageComponentTypes.ActionRow,
components: components as [ButtonComponent]
}
return actionRow
}

View File

@ -131,6 +131,7 @@ export default class Record {
parallelUploads3.on("httpUploadProgress", (progress) => {
if (progress?.loaded) {
if (this.onProgress) this.onProgress(this.counter);
console.log(`uploaded ${progress.loaded} bytes (${prettyBytes(progress.loaded)})`);
} else {
console.log(`httpUploadProgress ${JSON.stringify(progress, null, 2)}`)
@ -158,12 +159,9 @@ export default class Record {
// streams setup
this.uploadStream.on('data', (data) => {
this.counter += data.length
if (this.counter % (1 * 1024 * 1024) <= 1024) {
console.log(`Received ${this.counter} bytes (${prettyBytes(this.counter)})`);
if (this.onProgress) this.onProgress(this.counter)
}
})
this.uploadStream.on('close', () => {
console.log('[!!!] upload stream has closed')

View File

@ -67,7 +67,16 @@ function checkIfAborted(record: RawRecordingRecord): boolean {
return (record.is_aborted)
}
async function updateDatabaseRecord({recordId, recordingState, fileSize}: { recordId: number, recordingState: RecordingState, fileSize: number }): Promise<RawRecordingRecord> {
async function updateDatabaseRecord({
recordId,
recordingState,
fileSize
}: {
recordId: number,
recordingState: RecordingState,
fileSize: number
}): Promise<RawRecordingRecord> {
console.log(`updating database record with recordId=${recordId}, recordingState=${recordingState}, fileSize=${fileSize}`)
const payload: any = {
file_size: fileSize
}
@ -83,7 +92,8 @@ async function updateDatabaseRecord({recordId, recordingState, fileSize}: { reco
body: JSON.stringify(payload)
})
if (!res.ok) {
throw new Error(`failed to updateDatabaseRecord. status=${res.status}, statusText=${res.statusText}`);
const body = await res.text()
throw new Error(`failed to updateDatabaseRecord. status=${res.status}, statusText=${res.statusText}, body=${body}`);
}
const body = await res.json() as RawRecordingRecord[];
if (!body[0]) throw new Error(`failed to get a record that matched recordId=${recordId}`)

View File

@ -5,7 +5,7 @@ CREATE FUNCTION public.tg__update_discord_message() RETURNS trigger
AS $$
begin
PERFORM graphile_worker.add_job('update_discord_message', json_build_object(
'record_id', NEW.record_id
'record_id', NEW.id
), max_attempts := 3);
return NEW;
end;