Skip to content

Commit

Permalink
button interactions
Browse files Browse the repository at this point in the history
  • Loading branch information
mbund committed Nov 21, 2023
1 parent c6dbb46 commit 2c6a181
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 28 deletions.
46 changes: 46 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: build-workspace-bot
on:
push:

jobs:
push-ghcr:
name: Build and push image
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Generate variables
id: vars
run: |
echo "date=$(date +%Y-%m-%d)" >> $GITHUB_OUTPUT
- name: Build image
id: build
uses: redhat-actions/buildah-build@v2
with:
containerfiles: |
./Dockerfile
image: ${{ github.event.repository.name }}
tags: |
latest
stable
${{ github.sha }}
${{ steps.vars.outputs.date }}
oci: true

- name: Push to ghcr
uses: redhat-actions/push-to-registry@v2
id: push
with:
image: ${{ steps.build.outputs.image }}
tags: ${{ steps.build.outputs.tags }}
registry: ghcr.io/${{ github.repository_owner }}
username: ${{ github.actor }}
password: ${{ github.token }}
extra-args: |
--disable-content-trust
149 changes: 121 additions & 28 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
ApplicationCommandOptionType,
AutocompleteInteraction,
ButtonBuilder,
ButtonStyle,
ChatInputApplicationCommandData,
Client,
Expand All @@ -13,8 +12,10 @@ import {
import k8s from "@kubernetes/client-node";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import relativeTime from "dayjs/plugin/relativeTime";

dayjs.extend(duration);
dayjs.extend(relativeTime);

const kc = new k8s.KubeConfig();
kc.loadFromDefault();
Expand All @@ -32,7 +33,20 @@ interface Command extends ChatInputApplicationCommandData {

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

const timeouts = new Map<{ discordId: string }, NodeJS.Timeout>();
const timeouts = new Map<
string,
{
timeout: NodeJS.Timeout;
end: {
id: "end-session";
fn: () => Promise<void>;
};
extend: {
id: "extend-session";
fn: () => Promise<void>;
};
}
>();

const createWorkspace: Command = {
name: "workspace",
Expand Down Expand Up @@ -100,26 +114,50 @@ const createWorkspace: Command = {
sleep(1000);
} while (pod.status?.phase !== "Running");

const extendSessionButton = new ButtonBuilder()
.setCustomId("extend-session")
.setLabel("Extend Session")
.setStyle(ButtonStyle.Primary);
const workspaceDuration = 1000 * 60 * 60;

const deleteTimestamp = Math.floor(Date.now() / 1000) + 9;
const warnFn = (deleteTimestamp: Date) => {
const warningOffset = 1000 * 60 * 10;

setTimeout(async () => {
const message = await interaction.user.send({
content: `Your workspace will be deleted <t:${deleteTimestamp}:R>`,
components: [
{
type: ComponentType.ActionRow,
components: [extendSessionButton],
},
],
});
setTimeout(async () => {
const message = await interaction.user.send({
content: `Your workspace will be deleted <t:${Math.floor(
deleteTimestamp.getTime() / 1000
)}:R>. If you're still using it, you can add more time by clicking the button below.`,
components: [
{
type: ComponentType.ActionRow,
components: [
{
type: ComponentType.Button,
style: ButtonStyle.Primary,
customId: "extend-session",
label: "Add another hour",
},
{
type: ComponentType.Button,
style: ButtonStyle.Danger,
customId: "end-session",
label: "End session",
},
{
type: ComponentType.Button,
style: ButtonStyle.Link,
url: "http://localhost:9001",
label: "Open workspace",
},
],
},
],
});

const deleteTimeoutFn = () =>
setTimeout(async () => endFn(), warningOffset);

const endFn = async () => {
const t = timeouts.get(interaction.user.id);
clearTimeout(t?.timeout);

const deleteTimeoutFn = () =>
setTimeout(async () => {
const pod = await k8sApi.readNamespacedPod({
name: podName,
namespace: "workspaces",
Expand All @@ -128,26 +166,74 @@ const createWorkspace: Command = {
const age = dayjs.duration(dayjs().diff(pod.status!.startTime!));

await message.edit({
content: `Your workspace lasted \`${age.format("HH:mm")}\``,
content: `Your workspace lasted ${age.humanize()}`,
components: [],
});

await k8sApi.deleteNamespacedPod({
name: podName,
namespace: "workspaces",
});
}, 1000 * 5);
};

const extendFn = async () => {
await message.edit({
content: `Extending the workspace until`,
const extendFn = async () => {
const newDeleteTimestamp = new Date(
deleteTimestamp.getTime() + workspaceDuration
);

await message.edit({
content: `Extending the workspace until <t:${Math.floor(
newDeleteTimestamp.getTime() / 1000
)}:t>`,
components: [
{
type: ComponentType.ActionRow,
components: [
{
type: ComponentType.Button,
style: ButtonStyle.Link,
url: "http://localhost:9001",
label: "Open workspace",
},
],
},
],
});

const t = timeouts.get(interaction.user.id);
clearTimeout(t?.timeout);
warnFn(newDeleteTimestamp);
};

const deleteTimeout = deleteTimeoutFn();
timeouts.set(interaction.user.id, {
timeout: deleteTimeout,
end: { fn: endFn, id: "end-session" },
extend: { fn: extendFn, id: "extend-session" },
});
};
}, deleteTimestamp.getTime() - new Date().getTime() - warningOffset);
};

const deleteTimeout = deleteTimeoutFn();
}, 1000 * 5);
const endTime = new Date(new Date().getTime() + workspaceDuration);
warnFn(endTime);

await interaction.editReply({
content: `Workspace created! Visit it at `,
content: `Workspace created! It will expire at <t:${Math.floor(
endTime.getTime() / 1000
)}:t>`,
components: [
{
type: ComponentType.ActionRow,
components: [
{
type: ComponentType.Button,
style: ButtonStyle.Link,
url: "http://localhost:9001",
label: "Open workspace",
},
],
},
],
});
},
};
Expand All @@ -170,6 +256,13 @@ client.on(Events.InteractionCreate, async (interaction) => {
const command = commands.find((c) => c.name === interaction.commandName);
if (command) await command.run(interaction);
}
if (interaction.isButton()) {
const t = timeouts.get(interaction.user.id);

if (interaction.customId === t?.end.id) await t.end.fn();
if (interaction.customId === t?.extend.id) await t.extend.fn();
interaction.update({});
}
});

client.login(Bun.env.DISCORD_BOT_TOKEN);

0 comments on commit 2c6a181

Please sign in to comment.