From bde198fb4030d42d05e8de011174d241ecd67bee Mon Sep 17 00:00:00 2001 From: Andrzej Kupczyk Date: Wed, 1 Oct 2025 17:08:43 +0200 Subject: [PATCH] Introduce extend command to allow sandbox reservation extension --- messages/extend.js | 89 ++++++++++++++++++++++++++++++++++++++++++++++ messages/index.js | 2 ++ package.json | 2 +- 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 messages/extend.js diff --git a/messages/extend.js b/messages/extend.js new file mode 100644 index 0000000..2d16c1b --- /dev/null +++ b/messages/extend.js @@ -0,0 +1,89 @@ +const db = require('../db/connection'); +const { getSandboxNameFromMessage } = require('../common'); + +function getSandboxDetails(channel, sandboxName) { + return new Promise((resolve, reject) => { + db.get( + 'SELECT owner, assigned_at FROM sandboxes WHERE team = $teamChannel AND sandbox = $sandboxName', + { $teamChannel: channel, $sandboxName: sandboxName }, + (err, row) => { + if (err || !row) { + return reject(err || 'sandbox does not exist'); + } + return resolve(row); + }, + ); + }); +} + +function updateAssignedAt(channel, sandboxName, newAssignedAt) { + return new Promise((resolve, reject) => { + db.run( + 'UPDATE sandboxes SET assigned_at = $newAssignedAt WHERE team = $teamChannel AND sandbox = $sandboxName', + { + $newAssignedAt: newAssignedAt.toUTCString(), + $teamChannel: channel, + $sandboxName: sandboxName, + }, + (err) => { + if (err) return reject(err); + return resolve(); + }, + ); + }); +} + +function addWorkingDays(startDate, workingDays) { + const date = new Date(startDate.getTime()); + let remaining = workingDays; + while (remaining > 0) { + date.setDate(date.getDate() + 1); + const day = date.getDay(); + if (day !== 0 && day !== 6) { + remaining -= 1; + } + } + return date; +} + +function formatDate(date) { + return date.toISOString().slice(0, 10); +} + +module.exports = { + pattern: /(przedluzam|extending) (sandbox|adeng|neutron-api)-|^[pe] (sandbox|adeng|neutron-api)-/i, + action({ message, say }) { + const sandboxName = getSandboxNameFromMessage(message); + let msg = `<@${message.user}> `; + + if (!sandboxName) { + say(`${msg}:x: - can't find sandbox name in your message`); + return; + } + + getSandboxDetails(message.channel, sandboxName) + .then(async ({ owner }) => { + if (!owner) { + msg += ':-1: - sandbox is not currently booked'; + say(msg); + return; + } + if (owner !== message.user) { + msg += `:-1: - <@${owner}> is using it`; + say(msg); + return; + } + + // Reset assigned_at to now to grant a fresh 5 working day window + const now = new Date(); + await updateAssignedAt(message.channel, sandboxName, now); + const due = addWorkingDays(now, 5); + + msg += `:+1: (extended, new due date _${formatDate(due)}_)`; + say(msg); + }) + .catch((err) => { + say(`:x: sandbot error: \`${err}\`, try again`); + }); + }, +}; diff --git a/messages/index.js b/messages/index.js index 9428e5d..c8f4944 100644 --- a/messages/index.js +++ b/messages/index.js @@ -1,4 +1,5 @@ const book = require('./book'); +const extend = require('./extend'); const joke = require('./joke'); const ping = require('./ping'); const release = require('./release'); @@ -6,6 +7,7 @@ const status = require('./status'); module.exports = [ book, + extend, joke, ping, release, diff --git a/package.json b/package.json index 2eb7fb7..76cf4d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sandbot", - "version": "2.1.1", + "version": "2.2.0", "description": "Slack app for reserving sandboxes using Bolt Framework and SQLite", "main": "app.js", "scripts": {