straight to prod you go
This commit is contained in:
parent
9a69d15a1e
commit
8785588f44
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@ dist/
|
|||
node_modules
|
||||
.env
|
||||
db.json
|
||||
reactionroles.json
|
||||
|
|
217
src/index.ts
217
src/index.ts
|
@ -1,17 +1,30 @@
|
|||
import { Channel, Client, Member, Message, User } from "revolt.js";
|
||||
import { Channel, Client, ClientboundNotification, Member, Message, User } from "revolt.js";
|
||||
import { config } from 'dotenv';
|
||||
import { SendableEmbed } from 'revolt-api';
|
||||
import { Low, JSONFile } from 'lowdb';
|
||||
import { decodeTime } from 'ulid';
|
||||
import sherlock from "./sherlock.js";
|
||||
import axios from "axios";
|
||||
import { readFileSync } from "fs";
|
||||
import path from "path";
|
||||
import { AnyMxRecord } from "dns";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
config();
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
type ReactionId = { message: string, channel: string, emoji: string };
|
||||
type Db = {
|
||||
probation: string[];
|
||||
blocked: { [key: string]: { blocked: boolean, kick: boolean } };
|
||||
children: string[];
|
||||
reaction_ids: {
|
||||
nsfw_role: ReactionId,
|
||||
cosmetic_roles: {
|
||||
[key: string]: ReactionId,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CommandFlag = {
|
||||
|
@ -58,6 +71,7 @@ const COMMANDS = {
|
|||
'unblock': 'Untroll a user',
|
||||
'child': 'Mark a user as child',
|
||||
'notchild': 'Remove user\'s child status',
|
||||
'setup_reactions': 'Set up reaction role messages',
|
||||
'sherlock': 'Run a sherlock scan on a username',
|
||||
}
|
||||
|
||||
|
@ -71,10 +85,11 @@ const client = new Client({ });
|
|||
client.loginBot(process.env.TOKEN);
|
||||
|
||||
db.read().then(() => {
|
||||
db.data ||= { probation: [], blocked: {}, children: [] };
|
||||
db.data ||= { probation: [], blocked: {}, children: [], reaction_ids: { cosmetic_roles: {}, nsfw_role: { emoji: 'a', channel: '', message: '1' } } };
|
||||
db.data.probation ||= [];
|
||||
db.data.blocked ||= {};
|
||||
db.data.children ||= [];
|
||||
db.data.reaction_ids ||= { cosmetic_roles: {}, nsfw_role: { emoji: 'a', channel: '', message: '1' } };
|
||||
|
||||
if (Array.isArray(db.data.blocked)) {
|
||||
console.log('Running migration: db.blocked');
|
||||
|
@ -96,7 +111,7 @@ client.once('ready', async () => {
|
|||
console.log('Got server members');
|
||||
});
|
||||
|
||||
function embed(content: string, title?: string, type?: 'INFO'|'SUCCESS'|'WARN'|'ERROR'): SendableEmbed {
|
||||
function embed(content: string, title?: string, type?: 'INFO'|'SUCCESS'|'WARN'|'ERROR'|`#${string}`): SendableEmbed {
|
||||
const colors = {
|
||||
'SUCCESS': 'var(--success)',
|
||||
'INFO': 'var(--status-streaming)',
|
||||
|
@ -105,7 +120,7 @@ function embed(content: string, title?: string, type?: 'INFO'|'SUCCESS'|'WARN'|'
|
|||
}
|
||||
|
||||
return {
|
||||
colour: colors[type || 'INFO'],
|
||||
colour: type ? (colors as any)[type] || type : undefined,
|
||||
description: content,
|
||||
title: title,
|
||||
}
|
||||
|
@ -561,6 +576,61 @@ client.on('message', async (message) => {
|
|||
break;
|
||||
}
|
||||
|
||||
case 'setup_reactions': {
|
||||
db.data!.reaction_ids.cosmetic_roles = {};
|
||||
const file = Object.entries(JSON.parse(readFileSync(path.join(__dirname, "..", "reactionroles.json"), "utf-8")) as { [key: string]: string });
|
||||
|
||||
const nsfwReactionId = '01GE2KP575W886T71MZK9Z17D6';
|
||||
const nsfwMsg = await message.channel!.sendMessage({
|
||||
embeds: [embed(
|
||||
"React to this message to get access to the NSFW channel.\n> This channel is marked 18+, and as such we ask minors not to interact with it :01H9HFRZTGPK9PA3WBZV07BQ9Q:\n> Please also read the channel description before participating!",
|
||||
"Reaction role",
|
||||
"#aa557f",
|
||||
)],
|
||||
interactions: {
|
||||
reactions: ['01GE2KP575W886T71MZK9Z17D6'],
|
||||
restrict_reactions: true,
|
||||
},
|
||||
masquerade: {
|
||||
name: message.channel?.server?.name,
|
||||
avatar: message.channel?.server?.generateIconURL({ size: 256 }),
|
||||
},
|
||||
});
|
||||
|
||||
db.data!.reaction_ids.nsfw_role.channel = nsfwMsg.channel_id;
|
||||
db.data!.reaction_ids.nsfw_role.message = nsfwMsg._id;
|
||||
db.data!.reaction_ids.nsfw_role.emoji = nsfwReactionId;
|
||||
|
||||
while (file.length > 0) {
|
||||
const items = file.splice(0, 15);
|
||||
const msg = await message.channel!.sendMessage({
|
||||
embeds: [embed(
|
||||
`React to receive one of the following roles:\n\n`
|
||||
+ items.map((item) => `${item[1]} ${message.channel?.server?.roles?.[item[0]].name}`).join("\n"),
|
||||
"Reaction role",
|
||||
"#aa557f",
|
||||
)],
|
||||
interactions: {
|
||||
reactions: items.map((item) => encodeURIComponent(item[1].replaceAll(":", ""))),
|
||||
restrict_reactions: true,
|
||||
},
|
||||
masquerade: {
|
||||
name: message.channel?.server?.name,
|
||||
avatar: message.channel?.server?.generateIconURL({ size: 256 }),
|
||||
},
|
||||
});
|
||||
for (const item of items) {
|
||||
db.data!.reaction_ids.cosmetic_roles[item[0]] = {
|
||||
channel: msg.channel_id,
|
||||
message: msg._id,
|
||||
emoji: item[1].replaceAll(":", ""),
|
||||
};
|
||||
}
|
||||
}
|
||||
await db.write();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'type': {
|
||||
if (message.channel?.typing_ids.has(client.user!._id)) {
|
||||
message.channel.stopTyping();
|
||||
|
@ -657,52 +727,115 @@ client.on('member/join', async (member) => {
|
|||
}
|
||||
});
|
||||
|
||||
client.on('packet', async (packet) => {
|
||||
if (packet.type != 'MessageReact') return;
|
||||
if (packet.emoji_id != '↩️') return;
|
||||
try {
|
||||
const channel = client.channels.get(packet.channel_id);
|
||||
const message = client.messages.get(packet.id) || await channel!.fetchMessage(packet.id);
|
||||
if (!message || message.author_id != client.user?._id) return console.log('Ignoring react: Author mismatch');
|
||||
async function processActionButtonReactions(packet: ClientboundNotification & { type: "MessageReact" }) {
|
||||
const channel = client.channels.get(packet.channel_id);
|
||||
const message = client.messages.get(packet.id) || await channel!.fetchMessage(packet.id);
|
||||
if (!message || message.author_id != client.user?._id) return console.log('Ignoring react: Author mismatch');
|
||||
|
||||
const members = getMembers(channel!.server_id!);
|
||||
const member = members.find(m => m._id.user == packet.user_id);
|
||||
if (!member) return console.log('Ignoring react: Could not find reacting user');
|
||||
const privileged = member.hasPermission(channel!.server!, 'ManageMessages');
|
||||
if (!privileged) return console.log('Ignoring react: User is unprivileged');
|
||||
const members = getMembers(channel!.server_id!);
|
||||
const member = members.find(m => m._id.user == packet.user_id);
|
||||
if (!member) return console.log('Ignoring react: Could not find reacting user');
|
||||
const privileged = member.hasPermission(channel!.server!, 'ManageMessages');
|
||||
if (!privileged) return console.log('Ignoring react: User is unprivileged');
|
||||
|
||||
let info = message.content?.match(/^\$%log action=\S+ user=[A-Z0-9]+%\$$/)?.[0];
|
||||
if (!info) return console.log('Ignoring react: Could not extract message metadata');
|
||||
let info = message.content?.match(/^\$%log action=\S+ user=[A-Z0-9]+%\$$/)?.[0];
|
||||
if (!info) return console.log('Ignoring react: Could not extract message metadata');
|
||||
|
||||
let action = info.match(/action=\S+/)?.[0].substring(7),
|
||||
user = info.match(/user=[A-Z0-9]+/)?.[0].substring(5);
|
||||
let action = info.match(/action=\S+/)?.[0].substring(7),
|
||||
user = info.match(/user=[A-Z0-9]+/)?.[0].substring(5);
|
||||
|
||||
const target = members.find(m => m._id.user == user);
|
||||
if (!target) return await channel?.sendMessage({ embeds: [
|
||||
embed(`Failed to find user \`${user}\`.`, 'Message interaction failed', 'ERROR'),
|
||||
] });
|
||||
const target = members.find(m => m._id.user == user);
|
||||
if (!target) return await channel?.sendMessage({ embeds: [
|
||||
embed(`Failed to find user \`${user}\`.`, 'Message interaction failed', 'ERROR'),
|
||||
] });
|
||||
|
||||
switch(action) {
|
||||
case 'probation_add': {// Log message was for added probation, clicking button will remove it
|
||||
await setProbation(target, false);
|
||||
const embed = message.embeds?.[0] as SendableEmbed;
|
||||
embed.description = embed.description?.replace(' Press ↩️ to undo.', '');
|
||||
embed.colour = 'var(--status-invisible)';
|
||||
await message.edit({ content: '#', embeds: [ embed ] });
|
||||
break;
|
||||
switch(action) {
|
||||
case 'probation_add': {// Log message was for added probation, clicking button will remove it
|
||||
await setProbation(target, false);
|
||||
const embed = message.embeds?.[0] as SendableEmbed;
|
||||
embed.description = embed.description?.replace(' Press ↩️ to undo.', '');
|
||||
embed.colour = 'var(--status-invisible)';
|
||||
await message.edit({ content: '#', embeds: [ embed ] });
|
||||
break;
|
||||
}
|
||||
case 'probation_remove': {
|
||||
await setProbation(target, true);
|
||||
const embed = message.embeds?.[0] as SendableEmbed;
|
||||
embed.description = embed.description?.replace(' Press ↩️ to undo.', '');
|
||||
embed.colour = 'var(--status-invisible)';
|
||||
await message.edit({ content: '#', embeds: [ embed ] });
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!target) return;
|
||||
}
|
||||
|
||||
async function processReactionRoles(messageId: string, emoji: string, userId: string, added: boolean) {
|
||||
|
||||
async function assignRole(member: Member, role: string, assign: boolean) {
|
||||
if (assign) {
|
||||
if (!member.roles?.includes(role)) {
|
||||
await member.edit({ roles: [...(member.roles ?? []), role] });
|
||||
}
|
||||
case 'probation_remove': {
|
||||
await setProbation(target, true);
|
||||
const embed = message.embeds?.[0] as SendableEmbed;
|
||||
embed.description = embed.description?.replace(' Press ↩️ to undo.', '');
|
||||
embed.colour = 'var(--status-invisible)';
|
||||
await message.edit({ content: '#', embeds: [ embed ] });
|
||||
break;
|
||||
} else {
|
||||
if (member.roles?.includes(role)) {
|
||||
await member.edit({ roles: member.roles.filter((r) => r != role) });
|
||||
}
|
||||
}
|
||||
if (!target) return;
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
// nsfw role needs extra processing
|
||||
if (messageId == db.data?.reaction_ids.nsfw_role.message && emoji == db.data.reaction_ids.nsfw_role.emoji) {
|
||||
const channel = await client.channels.fetch(db.data.reaction_ids.nsfw_role.channel);
|
||||
const members = getMembers(channel.server_id!);
|
||||
const member = members.find((member) => member._id.user == userId);
|
||||
if (!member) return;
|
||||
|
||||
if (added && db.data.children.includes(userId)) {
|
||||
try {
|
||||
await client.api.delete(
|
||||
`/channels/${channel._id}/messages/${messageId}/reactions/${emoji}` as '-/channels/{target}/messages/{msg}/reactions/{emoji}',
|
||||
{ user_id: userId },
|
||||
);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
await assignRole(member, process.env.NSFW_ROLE!, false).catch(); // Remove the role if it's still there for some reason
|
||||
return;
|
||||
}
|
||||
|
||||
await assignRole(member, process.env.NSFW_ROLE!, added);
|
||||
return;
|
||||
}
|
||||
|
||||
const roleConfig = Object.entries(db.data?.reaction_ids.cosmetic_roles ?? []).find(
|
||||
(r) => r[1].emoji == emoji && r[1].message == messageId
|
||||
);
|
||||
|
||||
if (!roleConfig) return;
|
||||
|
||||
const channel = await client.channels.fetch(roleConfig[1].channel);
|
||||
const members = getMembers(channel.server_id!);
|
||||
const member = members.find((member) => member._id.user == userId);
|
||||
if (!member) return;
|
||||
|
||||
await assignRole(member, roleConfig[0], added);
|
||||
}
|
||||
|
||||
client.on('packet', async (packet) => {
|
||||
if (packet.type == 'MessageReact') {
|
||||
try {
|
||||
if (packet.emoji_id == '↩️') await processActionButtonReactions(packet);
|
||||
await processReactionRoles(packet.id, packet.emoji_id, packet.user_id, true);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
} else if (packet.type == 'MessageUnreact') {
|
||||
try {
|
||||
await processReactionRoles(packet.id, packet.emoji_id, packet.user_id, false);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue