Skip to content
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ It lets us:
- Mute people from the Slack
- Mute people from Channels only
- Make Channels read only & Whitelist people
- Start slow mode (WIP)
- Start slow mode in a channel

## Filesystem
- ```interactions``` is where the code for deleting messages, slow mode (WIP), muting people, and making channels read only; It also joins every new channel
Expand Down
8 changes: 4 additions & 4 deletions actions/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
const path = require("path");

async function handleAction({ event, client, body, say }) {
async function handleAction({ ack, event, client, body, say, action, respond, logger }) {
try {
const firstAction = body.actions[0];
const viewId = body.view.callback_id
const actionId = firstAction.action_id;
const blockId = firstAction.block_id;


console.log("it's working")
console.log("action triggered:", actionId)
const actionFile = path.resolve(__dirname, `${actionId}.js`);

// Dynamically require action handlers
const actionHandler = require(actionFile);
if (actionHandler) {
await actionHandler({ event, client, body, say });
await actionHandler({ ack, event, client, body, say, action, respond, logger });
} else {
console.warn(`No handler found for action: ${actionId}`);
}
} catch (error) {
console.error(`Error handling action ${body.actions[0].action_id}:`, error);
if (ack) await ack();
}
}

Expand Down
68 changes: 68 additions & 0 deletions actions/slowmode_disable_button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const { getPrisma } = require('../utils/prismaConnector');
require('dotenv').config();

async function slowmode_disable_button(args) {
const { ack, body, client } = args;
const prisma = getPrisma();

try {
await ack();

const data = JSON.parse(body.actions[0].value);
const { channel, threadTs } = data;
const admin_id = body.user.id;
const userInfo = await client.users.info({ user: admin_id });
if (!userInfo.user.is_admin) {
return await client.chat.postEphemeral({
channel: channel,
user: admin_id,
text: "You must be an admin"
});
}

const existingSlowmode = await prisma.Slowmode.findUnique({
where: {
channel_threadTs: {
channel: channel,
threadTs: threadTs || ""
}
}
});

if (!existingSlowmode || !existingSlowmode.locked) {
return await client.chat.postEphemeral({
channel: channel,
user: admin_id,
text: `No active slowmode in <#${channel}>`
});
} else {
await prisma.Slowmode.update({
where: {
channel_threadTs: {
channel: channel,
threadTs: threadTs || ""
}
},
data: {
locked: false,
updatedAt: new Date(),
admin: admin_id
}
});

await client.chat.postMessage({
channel: process.env.MIRRORCHANNEL,
text: `<@${admin_id}> turned off Slowmode in <#${channel}>`
});

await client.chat.postMessage({
channel: channel,
text: "Slowmode has been turned off in this channel."
})
}
} catch(e) {
console.error(e);
}
}

module.exports = slowmode_disable_button;
72 changes: 72 additions & 0 deletions actions/slowmode_thread_disable_button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// this is virtually the same as slowmode_disable_button.js
const { getPrisma } = require('../utils/prismaConnector');
require('dotenv').config();

async function slowmode_disable_button(args) {
const { ack, body, client } = args;
const prisma = getPrisma();

try {
await ack();

const data = JSON.parse(body.actions[0].value);
const { channel, threadTs } = data;
const actionThreadTs = body.actions[0].value && typeof body.actions[0].value === 'string' ? JSON.parse(body.actions[0].value).threadTs : threadTs;
const userInfo = await client.users.info({ user: body.user.id });
if (!userInfo.user.is_admin) {
return await client.chat.postEphemeral({
channel: channel,
thread_ts: threadTs,
user: body.user.id,
text: "You must be an admin"
});
}

const existingSlowmode = await prisma.Slowmode.findUnique({
where: {
channel_threadTs: {
channel: channel,
threadTs: actionThreadTs
}
}
});

if (!existingSlowmode || !existingSlowmode.locked) {
return await client.chat.postEphemeral({
channel: channel,
thread_ts: actionThreadTs,
user: body.user.id,
text: `No active slowmode in this thread.`
});
} else {
await prisma.Slowmode.update({
where: {
channel_threadTs: {
channel: channel,
threadTs: actionThreadTs
}
},
data: {
locked: false,
updatedAt: new Date(),
admin: body.user.id
}
});

await client.chat.postMessage({
channel: process.env.MIRRORCHANNEL,
text: `<@${body.user.id}> turned off Slowmode in https://hackclub.slack.com/archives/${channel}/p${actionThreadTs.toString().replace(".", "")}`
});

await client.chat.postMessage({
channel: channel,
thread_ts: actionThreadTs,
text: "Slowmode has been turned off in this thread."
})
}
} catch(e) {
console.error(e);
}
}

module.exports = slowmode_disable_button;
188 changes: 168 additions & 20 deletions commands/slowmode.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const chrono = require('chrono-node');
const { getPrisma } = require('../utils/prismaConnector');
const getChannelManagers = require("../utils/isChannelManger");


async function slowmode(args) {
Expand All @@ -9,27 +10,174 @@ async function slowmode(args) {
const commands = text.split(" ");
const userInfo = await client.users.info({ user: user_id });
const isAdmin = userInfo.user.is_admin;
const channel = commands[0].split('|')[0].replace("<#", "");
let count = Number(commands[1]);
let time = Number(commands[2]);

if (!isAdmin) return;


const createSlowMode = await prisma.Slowmode.create({
data: {
channel: channel,
locked: true,
time: time,
messageCount: count,
}
})
const channelManagers = await getChannelManagers(channel_id);

let channel = channel_id;
if (commands[0] && commands[0].includes('#')) {
channel = commands[0].split('|')[0].replace("<#", "").replace(">", "");
}

const errors = []
// editor's note: i don't think it would be appropriate allowing channel managers to enable slowmode (for now...) - up to discussion.
if (!isAdmin) errors.push("Only admins can run this command.");
if (!channel) errors.push("You need to give a channel to make it read only");

if (errors.length > 0)
return await client.chat.postEphemeral({
channel: `${channel_id}`,
user: `${user_id}`,
text: errors.join("\n")
});

// TODO: send message in firehouse logs
// TODO: cancel slowmode
const existingSlowmode = await prisma.Slowmode.findFirst({
where: { channel: channel }
});



}
// TODO: Slowmode for specific threads similar to threadlocker

const isUpdate = existingSlowmode && existingSlowmode.locked;
const defaultTime = (existingSlowmode?.time || 5).toString();
const defaultExpiry = existingSlowmode?.expiresAt
? Math.floor(existingSlowmode.expiresAt.getTime() / 1000)
: undefined;
const defaultWhitelist = existingSlowmode?.whitelistedUsers || [];

// using a modal-based approach similar to definite threadlocker
const slowmodeModal = {
type: "modal",
callback_id: "slowmode_modal",
private_metadata: JSON.stringify({
channel_id: channel,
admin_id: user_id,
command_channel: channel_id
}),
title: {
type: "plain_text",
text: isUpdate ? "Update Slowmode" : "Configure Slowmode"
},
submit: {
type: "plain_text",
text: "Enable"
},
close: {
type: "plain_text",
text: "Cancel"
},
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `Configure slowmode for <#${channel}>`
}
},
{
type: "input",
block_id: "slowmode_time_block",
element: {
type: "number_input",
is_decimal_allowed: false,
action_id: "slowmode_time_input",
initial_value: defaultTime,
min_value: "1"
},
label: {
type: "plain_text",
text: "Slowmode interval (seconds)"
},
hint: {
type: "plain_text",
text: "Users can send one message every X seconds"
}
},
{
type: "input",
block_id: "slowmode_duration_block",
optional: true,
element: {
type: "datetimepicker",
action_id: "slowmode_duration_input",
...(defaultExpiry && { initial_date_time: defaultExpiry })
},
label: {
type: "plain_text",
text: "Slowmode until"
},
hint: {
type: "plain_text",
text: "Leave blank for indefinite"
}
},
{
type: "input",
block_id: "slowmode_reason_block",
optional: true,
element: {
type: "plain_text_input",
action_id: "slowmode_reason_input",
multiline: false,
placeholder: {
type: "plain_text",
text: "Optional reason"
}
},
label: {
type: "plain_text",
text: "Reason"
}
},
{
type: "input",
block_id: "slowmode_whitelist_block",
optional: true,
element: defaultWhitelist.length > 0 ? {
type: "multi_users_select",
action_id: "slowmode_whitelist_input",
initial_users: defaultWhitelist,
placeholder: {
type: "plain_text",
text: "Select users (admins and channel managers are exempt by default)"
}
} : {
type: "multi_users_select",
action_id: "slowmode_whitelist_input",
placeholder: {
type: "plain_text",
text: "Select users (admins and channel managers are exempt by default)"
}
},
label: {
type: "plain_text",
text: "Whitelisted users"
},
hint: {
type: "plain_text",
text: "These users will be immune to slowmode"
}
},
{
type: "actions",
block_id: "slowmode_disable_block",
elements: [
{
type: "button",
text: {
type: "plain_text",
text: "Turn off Slowmode"
},
style: "danger",
action_id: "slowmode_disable_button",
value: JSON.stringify({ channel: channel, threadTs: "" })
}
]
}
]
};

await client.views.open({
trigger_id: payload.trigger_id,
view: slowmodeModal
})
}

module.exports = slowmode;
5 changes: 5 additions & 0 deletions events/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
async function handleMessage(args) {
return;
}

module.exports = handleMessage;
Loading