This commit is contained in:
Lea 2023-04-16 01:19:42 +02:00
parent 83dea73e77
commit c2b8b386ce
Signed by: Lea
GPG key ID: 1BAFFE8347019C42
4 changed files with 246 additions and 3 deletions

View file

@ -14,7 +14,9 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"axios": "^1.3.5",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"form-data": "^4.0.0",
"lowdb": "^3.0.0", "lowdb": "^3.0.0",
"revolt-api": "^0.5.16", "revolt-api": "^0.5.16",
"revolt.js": "^6.0.20", "revolt.js": "^6.0.20",

View file

@ -3,6 +3,7 @@ import { config } from 'dotenv';
import { SendableEmbed } from 'revolt-api'; import { SendableEmbed } from 'revolt-api';
import { Low, JSONFile } from 'lowdb'; import { Low, JSONFile } from 'lowdb';
import { decodeTime } from 'ulid'; import { decodeTime } from 'ulid';
import sherlock from "./sherlock";
config(); config();
@ -27,7 +28,11 @@ const CommandFlags: CommandFlag[] = [
{ {
name: 'kick', name: 'kick',
type: 'boolean', type: 'boolean',
} },
{
name: 'nsfw',
type: 'boolean',
},
]; ];
const PREFIX_WORD = '/kibby'; const PREFIX_WORD = '/kibby';
@ -38,7 +43,7 @@ const RE_FLAG = /^--\S+(=.*)?$/g;
const RE_FLAG_NAME = /^--[^=\s]+(=*?|$)/g; const RE_FLAG_NAME = /^--[^=\s]+(=*?|$)/g;
const DB_FILE = process.env.DB_FILE || './db.json'; const DB_FILE = process.env.DB_FILE || './db.json';
const RE_USER_MENTION = /^<@[0-9A-HJ-KM-NP-TV-Z]{26}>$/i; const RE_USER_MENTION = /^<@[0-9A-HJ-KM-NP-TV-Z]{26}>$/i;
const PUBLIC_COMMANDS = ['suicide', 'status', 'help', 'type']; const PUBLIC_COMMANDS = ['suicide', 'status', 'help', 'type', 'sherlock'];
const COMMANDS = { const COMMANDS = {
'help': 'List available commands', 'help': 'List available commands',
'status': 'Edit the bot\'s status', 'status': 'Edit the bot\'s status',
@ -49,6 +54,7 @@ const COMMANDS = {
'unapprove': 'Send users to probation', 'unapprove': 'Send users to probation',
'block': 'Troll a user', 'block': 'Troll a user',
'unblock': 'Untroll a user', 'unblock': 'Untroll a user',
'sherlock': 'Run a sherlock scan on a username',
} }
if (!process.env.TOKEN) throw "$TOKEN not set"; if (!process.env.TOKEN) throw "$TOKEN not set";
@ -489,6 +495,11 @@ client.on('message', async (message) => {
break; break;
} }
case 'sherlock': {
await sherlock(message, args, !!getFlag('nsfw'));
break;
}
case 'help': { case 'help': {
const commands = Object.entries(COMMANDS).filter(c => privileged || PUBLIC_COMMANDS.includes(c[0])); const commands = Object.entries(COMMANDS).filter(c => privileged || PUBLIC_COMMANDS.includes(c[0]));
@ -590,3 +601,5 @@ client.on('packet', async (packet) => {
console.error(e); console.error(e);
} }
}); });
export { embed }

176
src/sherlock.ts Normal file
View file

@ -0,0 +1,176 @@
import { Message } from "revolt.js";
import { spawn } from 'child_process';
import { embed } from ".";
import { SendableEmbed } from "revolt-api";
import FormData from 'form-data';
import axios from "axios";
let RUNNING: string[] = [];
export default async function sherlock(message: Message, args: string[], nsfw: boolean) {
const SHERLOCK_COMMAND = (process.env.SHERLOCK_COMMAND || 'python3 sherlock').split(' ');
if (RUNNING.includes(message.channel_id)) {
return await message.reply({ embeds: [
embed('Please wait for the previous scan to finish before launching another one!', 'Sherlock', 'ERROR'),
] });
}
return new Promise<void>(async (resolve, reject) => {
try {
if (!args.length) {
await message.reply({ embeds: [
embed('No username(s) to search for provided.', 'Sherlock', 'ERROR'),
] });
return resolve();
}
if (args.length > 3) {
await message.reply({ embeds: [
embed('Please provide no more than 3 usernames.', 'Sherlock', 'ERROR'),
] });
return resolve();
}
RUNNING.push(message.channel_id);
const infoSearchingEmbed = embed(
`Looking up \`${args.join('`, `')}\`. This might take a while, please be patient!\n\nIncluding NSFW results: \`${nsfw}\``,
'Sherlock',
'INFO',
);
const msg = await message.reply({ embeds: [ infoSearchingEmbed ] });
const matches: { user: string, name: string, url: string }[] = [];
const searchedEmbeds: SendableEmbed[] = [];
const attachments: string[] = [];
let currentUser = '';
const addEmbed = async (name: string) => {
let text = `Found ${matches.filter(match => match.user == name).length} links!\n`;
for (const match of matches.filter(match => match.user == name)) {
const newLine = args.length > 1
? `\n- [${match.name}](${match.url.replaceAll(' ', '%20')})`
: `\n$\\textsf{\\color{#1abc9c}${match.name}}$: ${match.url.replaceAll(' ', '%20')}`;
if (text.length + newLine.length > 2000) break;
else if (text.length + newLine.length > 1800 / args.length) {
text = 'Message too long - Results will be sent as file';
attachments.push(await uploadFile(
matches.filter(match => match.user == name)
.map(m => `${m.name}: ${m.url}`)
.join('\n'),
name,
));
break;
}
else text += newLine;
}
searchedEmbeds.push(embed(
text,
`Sherlock - ${name}`,
'SUCCESS'
));
}
const previousLength = matches.length;
const interval = setInterval(async () => {
try {
if (matches.length > previousLength) {
await msg?.edit({ embeds: [
infoSearchingEmbed,
...searchedEmbeds,
embed(
`Searching: \`${currentUser}\` - ${matches.filter(m => m.user == currentUser).length} found so far`,
`Sherlock - ${currentUser}`,
'INFO',
),
] });
}
} catch(e) {
// Would be super funny if the errors were actually fucking catched, wouldn't it?
clearInterval(interval);
proc.kill();
RUNNING = RUNNING.filter(r => r != message.channel_id);
reject(e);
}
}, 3000);
const cmdArgs = nsfw
? [ ...SHERLOCK_COMMAND.slice(1), '--nsfw', '--output', '/dev/null', '--', ...args ]
: [ ...SHERLOCK_COMMAND.slice(1), '--output', '/dev/null', '--', ...args ];
console.log(`Invoking: ${SHERLOCK_COMMAND[0]} ${cmdArgs.join(' ')}`);
const proc = spawn(`${SHERLOCK_COMMAND[0]}`, cmdArgs, { shell: false });
proc.on('exit', async (res) => {
console.log(`[Sherlock] Exited with code ${res}`);
RUNNING = RUNNING.filter(r => r != message.channel_id);
clearInterval(interval);
if (res != 0) {
await msg?.edit({ embeds: [
embed(`Sherlock exited with code ${res}.\n\`\`\`\n${stderr}\n\`\`\``, 'Sherlock - Error', 'ERROR'),
] });
}
else {
await addEmbed(currentUser);
await msg?.edit({ embeds: searchedEmbeds });
if (attachments.length) {
await message.channel?.sendMessage({ attachments });
}
}
return resolve();
});
let stderr = '';
proc.stderr.on('data', (data) => {
console.log('[Sherlock] [stderr] ' + data.toString('utf8'));
stderr += data.toString('utf8');
});
let stdoutBuf = '';
proc.stdout.on('data', async (data) => {
stdoutBuf += data.toString('utf8');
if (stdoutBuf.includes('\n')) {
const buf = stdoutBuf.split('\n');
stdoutBuf = buf.pop() ?? '';
for (let line of buf) {
console.log('[Sherlock] [stdout] ' + line);
if (line.startsWith('[+]')) {
line = line.substring(3).trimStart();
const [name, url] = line.split(': ');
matches.push({ user: currentUser, name, url });
}
else if (line.startsWith('[*] Checking username')) {
line = line.substring('[*] Checking username'.length, line.length - 'on:'.length).trim();
if (currentUser) {
await addEmbed(currentUser);
}
currentUser = line;
console.log('[Sherlock] Now scanning user:', currentUser);
}
}
}
});
} catch(e) {
reject(e);
RUNNING = RUNNING.filter(r => r != message.channel_id);
}
});
}
async function uploadFile(file: any, filename: string): Promise<string> {
let data = new FormData();
data.append("file", file, { filename: filename });
let req = await axios.post("https://autumn.revolt.chat/attachments", data, {
headers: data.getHeaders(),
});
return (req.data as any)["id"] as string;
}

View file

@ -32,6 +32,11 @@ argparse@^2.0.1:
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^0.21.4: axios@^0.21.4:
version "0.21.4" version "0.21.4"
resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz"
@ -46,6 +51,15 @@ axios@^0.26.1:
dependencies: dependencies:
follow-redirects "^1.14.8" follow-redirects "^1.14.8"
axios@^1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.5.tgz#e07209b39a0d11848e3e341fa087acd71dadc542"
integrity sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
busboy@^1.6.0: busboy@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
@ -53,6 +67,18 @@ busboy@^1.6.0:
dependencies: dependencies:
streamsearch "^1.1.0" streamsearch "^1.1.0"
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
dotenv@^16.0.3: dotenv@^16.0.3:
version "16.0.3" version "16.0.3"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz" resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz"
@ -63,11 +89,20 @@ eventemitter3@^4.0.7:
resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
follow-redirects@^1.14.0, follow-redirects@^1.14.8: follow-redirects@^1.14.0, follow-redirects@^1.14.8, follow-redirects@^1.15.0:
version "1.15.2" version "1.15.2"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
globalyzer@0.1.0: globalyzer@0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz" resolved "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz"
@ -112,6 +147,18 @@ lowdb@^3.0.0:
dependencies: dependencies:
steno "^2.1.0" steno "^2.1.0"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@^3.0.0: mime@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz" resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz"
@ -139,6 +186,11 @@ prettier@^2.6.2:
resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz"
integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
revolt-api@0.5.7: revolt-api@0.5.7:
version "0.5.7" version "0.5.7"
resolved "https://registry.npmjs.org/revolt-api/-/revolt-api-0.5.7.tgz" resolved "https://registry.npmjs.org/revolt-api/-/revolt-api-0.5.7.tgz"