massbanner/index.js

285 lines
9.4 KiB
JavaScript
Raw Permalink Normal View History

2021-08-23 16:10:22 +02:00
const { extend: createGotClient } = require('got');
const { ConnectionError, SayError, TimeoutError, UserBanError, ChatClient } = require('dank-twitch-irc');
2020-12-15 14:25:01 +01:00
const chalk = require('chalk');
2020-12-22 07:25:43 +01:00
const WS = require('ws');
2020-12-15 14:25:01 +01:00
const Config = require('./config.json');
const clients = [];
let listenClient = null;
const pingInterval = 120e3;
2020-12-15 14:25:01 +01:00
let lastIndex = 0;
const uuidRegex = /[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/i;
2020-12-22 07:25:43 +01:00
const msgRegex = /[\u034f\u2800\u{E0000}\u180e\ufeff\u2000-\u200d\u206D]/gu;
2020-12-15 14:25:01 +01:00
const pnslClient = createGotClient({
prefixUrl: 'https://bot.tetyys.com/api/v1',
throwHttpErrors: false,
headers: {
2021-08-23 16:10:22 +02:00
Authorization: `Bearer ${Config.PNSLToken}`
}
2020-12-15 14:25:01 +01:00
});
2021-03-23 03:02:31 +01:00
const urlClient = createGotClient();
const fetchList = async (list) => {
const listID = uuidRegex.exec(list)?.[0];
if (!listID) {
2020-12-22 07:25:43 +01:00
throw new Error('Invalid List ID!');
}
2021-08-23 16:10:22 +02:00
const { body: listMetaBody, statusCode } = await pnslClient('BotLists/Properties', { searchParams: { Guid: listID } });
2020-12-22 07:25:43 +01:00
if (statusCode === 404) {
throw new Error('List was not found.');
}
if (statusCode === 401) {
throw new Error('Authorization token is invalid');
}
if (statusCode !== 200) {
throw new Error('Unexpected error occurred!');
}
2021-08-23 16:10:22 +02:00
const { body: listData } = await pnslClient(`BotLists/${listID}`);
2020-12-22 07:25:43 +01:00
const listMeta = JSON.parse(listMetaBody);
2021-08-23 16:10:22 +02:00
return { listMeta, listData };
2020-12-22 07:25:43 +01:00
};
2021-03-23 03:02:31 +01:00
const fetchUrlList = async (list) => {
2021-08-23 16:10:22 +02:00
const { body: listMetaBody, statusCode } = await urlClient(list);
2021-03-23 03:02:31 +01:00
if (statusCode === 404) {
throw new Error('List was not found.');
}
if (statusCode !== 200) {
throw new Error('Unexpected error occured!');
}
return listMetaBody;
};
const runList = async (listData, listID, channel, sourceChannel) => {
2020-12-22 07:25:43 +01:00
const banArr = listData.split('\n');
const targetChannel = Config.UseParallelUniverse === true ? channel : `#${channel}`;
2020-12-22 07:25:43 +01:00
if (banArr.length > Config.MaxChunkSize) {
const banChunks = [];
2020-12-22 07:25:43 +01:00
while (banArr.length > 0) {
banChunks.push(banArr.splice(0, Config.MaxChunkSize));
}
2021-03-23 11:15:06 +01:00
const chunkCount = banChunks.length;
2020-12-22 07:25:43 +01:00
for (const [i, chunk] of banChunks.entries()) {
2021-08-23 16:10:22 +02:00
const currentChunk = i + 1;
2020-12-22 07:25:43 +01:00
const promises = [];
for (const entry of chunk) {
2021-08-23 16:10:22 +02:00
let user = '';
let reason = '';
if (entry === '') {
continue;
}
2020-12-22 07:25:43 +01:00
[, user, ...reason] = entry.split(' ');
promises.push(getConnection().ban(targetChannel, user, reason.join(' ')));
2020-12-22 07:25:43 +01:00
}
await Promise.allSettled(promises);
say(sourceChannel, `${Config.UseParallelUniverse === true ? '[PU]' : ''} Chunk ${currentChunk}/${chunkCount} of ${listID} executed successfully on channel ${targetChannel}`);
if (Config.SendChunks) say(channel, `Chunk ${currentChunk}/${chunkCount} executed successfully`);
2021-03-30 12:21:21 +02:00
if (currentChunk === chunkCount) {
2020-12-22 07:25:43 +01:00
break;
}
await sleep(Config.DelayPerChunk);
}
} else {
const promises = [];
for (const entry of banArr) {
2021-08-23 16:10:22 +02:00
let user = '';
let reason = '';
if (entry === '') {
continue;
}
2020-12-22 07:25:43 +01:00
[, user, ...reason] = entry.split(' ');
promises.push(getConnection().ban(targetChannel, user, reason.join(' ')));
2020-12-22 07:25:43 +01:00
}
await Promise.allSettled(promises);
}
};
2021-08-23 16:10:22 +02:00
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2020-12-15 14:25:01 +01:00
2021-08-23 16:10:22 +02:00
const createTwitchClient = () => new ChatClient({
username: Config.Username,
password: Config.Password,
rateLimits: 'verifiedBot'
});
2020-12-15 14:25:01 +01:00
const getConnection = () => {
2020-12-22 07:25:43 +01:00
const readyClients = clients.filter((i) => i.ready);
return readyClients[++lastIndex % readyClients.length];
2020-12-15 14:25:01 +01:00
};
const say = (channel, message) => {
2021-08-23 22:36:59 +02:00
if (channel.startsWith('#')) {
listenClient.say(channel, message);
} else {
listenClient.say(`#${channel}`, message);
}
};
2021-08-23 16:10:22 +02:00
if (!Config.IgnorePNSL) {
const pnslWebSocket = new WS('wss://bot.tetyys.com/api/wss', [], { headers: { Authorization: `Bearer ${Config.PNSLToken}` } });
2020-12-22 07:25:43 +01:00
2021-03-23 03:02:31 +01:00
pnslWebSocket.on('open', () => {
console.log(`${chalk.green('[P&SL]')} || Connected to P&SL Websocket server`);
});
2020-12-22 07:25:43 +01:00
2021-03-23 03:02:31 +01:00
pnslWebSocket.on('message', async (data) => {
2021-08-23 16:10:22 +02:00
const { o: type, p: payload } = JSON.parse(data);
2021-03-23 03:02:31 +01:00
switch (type) {
case 0: {
2021-08-23 16:10:22 +02:00
const { listMeta } = await fetchList(payload.LatestBotList);
2021-03-23 03:02:31 +01:00
console.log(`${chalk.green('[P&SL]')} || Latest Botlist: ${payload.LatestBotList} [${listMeta.name}] (${listMeta.count})`);
break;
}
case 1: {
console.log(`${chalk.green('[P&SL]')} || New Botlist: ${payload.Guid} [${payload.Name}] (${payload.Count}) (${payload.Tags.join(' ')})`);
break;
}
case 2: {
console.log(`${chalk.green('[P&SL]')} || New False Positive: ${payload.UserTwitchId} [${payload.BotListGuid}]`);
break;
}
default:
break;
}
});
}
2020-12-22 07:25:43 +01:00
(async () => {
await Promise.all([...Array(Config.MaxConnections)].map(async (_, i) => {
2020-12-15 14:25:01 +01:00
clients[i] = createTwitchClient();
clients[i].connect();
2020-12-22 07:25:43 +01:00
clients[i].on('error', (error) => {
2021-08-23 16:10:22 +02:00
if (error instanceof SayError) {
return;
}
2020-12-22 10:23:52 +01:00
if (error instanceof UserBanError) {
2021-03-23 11:15:06 +01:00
return console.warn(`Failed to ban user ${error.username} from ${error.channelName}`);
2020-12-22 10:23:52 +01:00
}
2020-12-22 07:25:43 +01:00
if (error instanceof ConnectionError) {
if (clients[i].ready) {
return;
}
clients[i].connect();
return console.error(`Error in client ${i} -> ${error.name} || ${error.message}`);
2020-12-15 14:25:01 +01:00
}
if (error instanceof TimeoutError) {
2020-12-22 07:40:02 +01:00
clients[i].connect();
return console.error(`Timeout in client ${i} -> ${error.name} || ${error.message} | Trying to reconnect`);
2020-12-22 07:40:02 +01:00
}
2020-12-22 07:25:43 +01:00
console.error(error);
});
return await new Promise((resolve) => clients[i].on('ready', () => resolve()));
}));
2020-12-15 14:25:01 +01:00
listenClient = createTwitchClient();
listenClient.connect();
listenClient.on('ready', async () => {
2021-09-06 22:23:14 +02:00
if (Config.Channels.length !== 0) {
2021-09-06 22:19:16 +02:00
await listenClient.joinAll(Config.Channels);
}
2021-09-06 22:23:14 +02:00
console.log(`${chalk.green('[TWITCH]')} || Command listener connected`);
});
2020-12-22 07:25:43 +01:00
console.log(`${chalk.green('[TWITCH]')} || ${clients.filter((i) => i.ready).length} Clients connected`);
2020-12-15 14:25:01 +01:00
listenClient.on('PRIVMSG', async ({ messageText, senderUserID, channelName }) => {
2021-08-23 16:10:22 +02:00
if (!Config.Users.includes(senderUserID)) {
return;
}
2020-12-15 14:25:01 +01:00
2021-08-23 16:10:22 +02:00
const message = messageText.replace(msgRegex, '').trimEnd();
if (!message.startsWith(Config.Prefix)) {
return;
}
2020-12-22 07:25:43 +01:00
const content = message.split(/\s+/g);
const command = content[0].slice(Config.Prefix.length);
const args = content.slice(1);
2020-12-15 14:25:01 +01:00
2020-12-22 07:25:43 +01:00
if (command === 'ping') {
const channel = args[0];
return say(channel || channelName, `${clients.filter((i) => i.ready).length} Clients from total of ${clients.length} are operational`);
2020-12-22 07:25:43 +01:00
}
2020-12-22 10:13:04 +01:00
if (command === 'quit') {
process.exit(0);
}
2020-12-15 14:25:01 +01:00
2020-12-22 07:25:43 +01:00
if (command === 'runlist') {
const listID = args[0];
let channel = args[1];
2020-12-15 14:25:01 +01:00
2021-08-23 16:10:22 +02:00
if (!listID) {
return say(channelName, 'No list ID provided!');
2021-08-23 16:10:22 +02:00
}
if (!channel) {
channel = Config.DefaultChannel || channelName;
}
if (Config.Blacklisted.includes(channel)) {
return say(channelName, `I am not allowed to execute lists in channel ${channel}!`);
}
2020-12-22 07:25:43 +01:00
try {
if (channel != channelName) {
const mods = await listenClient.getMods(`#${channel}`);
2021-08-23 16:10:22 +02:00
if (!mods.includes(Config.Username)) {
return say(channelName, `I am not a moderator in channel ${channel}!`);
2021-08-23 16:10:22 +02:00
}
2020-12-15 14:25:01 +01:00
}
if (!Config.IgnorePNSL) {
2021-03-23 03:02:31 +01:00
const data = await fetchList(listID);
await runList(data.listData, listID, channel, channelName);
2021-08-23 16:10:22 +02:00
} else {
2021-03-23 03:02:31 +01:00
const data = await fetchUrlList(listID);
await runList(data, listID, channel, channelName);
2021-03-23 03:02:31 +01:00
}
say(channelName, `${Config.UseParallelUniverse === true ? '[PU]' : ''} List ${listID} executed successfully on channel ${channel || channelName}`);
if (Config.SendMeme) say(channel, 'FeelsGoodMan');
2021-08-23 16:10:22 +02:00
} catch (e) {
console.error(e);
return say(channelName, `Error Occurred! ${e.message}`);
2020-12-15 14:25:01 +01:00
}
}
});
2020-12-22 07:25:43 +01:00
setInterval(() => {
for (const client of clients) {
client.ping();
}
}, pingInterval);
2020-12-15 14:25:01 +01:00
})();
2020-12-22 10:23:52 +01:00
process
.on('unhandledRejection', (err) => {
2021-08-23 16:10:22 +02:00
if (err.name === 'SayError') {
return;
}
2020-12-22 10:23:52 +01:00
console.error(`${chalk.red('[UnhandledRejection]')} || [${err.name}] ${err} - ${err.stack}`);
})
.on('uncaughtException', (err) => {
console.error(`${chalk.red('[UncaughtException]')} || ${err.message}`);
process.exit(0);
2021-03-23 03:02:31 +01:00
});