Last active
September 11, 2019 05:36
-
-
Save SunRed/5017b5353714b44b804e6179c311fae2 to your computer and use it in GitHub Desktop.
Poorly written lurk bot for Twitch in node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var channels = {}; | |
channels.required = [ | |
// Channels you will always lurk in (as long as the list is smaller than config.maxChannels) | |
]; | |
channels.pool = [ | |
// Channels of which you randomly lurk in whenever the channels get rerolled (config.rerollCronjob) | |
// These are used to fill the pool of channels up to the config.maxChannels value in case channels.required doesn't reach that value in size | |
]; | |
module.exports = channels; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var clients = [ | |
// [ "username", "oauthtoken" ], | |
[ "example", "h4kx832brkd82kf73k5gsd5k238dfh" ], | |
[ "", "" ], | |
]; | |
module.exports = clients; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var config = { | |
rerollCronjob: '0 0 */3 * * *', // Time when reroll/reconnect of channels should happen | |
maxChannels: 1000, // Maximum channels to join | |
timezone: 'Europe/Berlin', | |
rawUserString: 'USER IRC-CLIENT v1.0.4', | |
debugMode: false, | |
// Whisper yourself whenever you get mentioned somewhere in a chat you're lurking in using a second account | |
whisperBot: false, | |
// whisperBot: { | |
// name: "", | |
// oauth: "" | |
// } | |
}; | |
module.exports = config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const tmi = require('tmi.js'); | |
const cron = require('cron'); | |
const clients = require('./clients'); | |
const channels = require('./channels'); | |
const config = require('./config'); | |
const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs)); | |
const sessions = []; | |
var whisperBot = false; | |
if (config.whisperBot) { | |
whisperBot = new tmi.client({ | |
options: { | |
debug: false | |
}, | |
connection: { | |
cluster: "aws", | |
reconnect: true, | |
secure: true | |
}, | |
identity: { | |
username: config.whisperBot.name.toLowerCase(), | |
password: config.whisperBot.oauth | |
} | |
}); | |
whisperBot.connect(); | |
} | |
const clientsJoinChannels = async () => { | |
// If this is executed by a cronjob we want to disconnect first | |
if (sessions.length > 0) { | |
while(sessions.length > 0) { | |
sessions.shift().disconnect(); | |
} | |
console.log("Waiting 3 seconds before restarting..."); | |
await sleep(3000); | |
} | |
clients.forEach(async credentials => { | |
let mergedChannelList = []; | |
let requiredChannelList = channels.required; | |
let poolChannelList = channels.pool; | |
// Remove required channels to be included from the pool list to avoid duplicates | |
// (May not be necessary) | |
requiredChannelList.forEach(channel => { | |
for (let i = 0; i < poolChannelList.length; i++) { | |
if (poolChannelList[i] === channel) { | |
poolChannelList.splice(i, 1); | |
} | |
} | |
}); | |
mergedChannelList = requiredChannelList.concat(shuffle(poolChannelList)); | |
const options = { | |
options: { | |
debug: false | |
}, | |
connection: { | |
cluster: "aws", | |
reconnect: true, | |
secure: true | |
}, | |
identity: { | |
username: credentials[0].toLowerCase(), | |
password: credentials[1] | |
}, | |
channels: mergedChannelList.slice(0, config.maxChannels-1) | |
}; | |
const client = new tmi.client(options); | |
client.on("message", (channel, userstate, message, self) => { | |
if (self) return; // Ignore messages from this client | |
if (!whisperBot) return; // Stop here if no whisper bot has been specified | |
if (whisperBot.getUsername() === client.getUsername()) return; // Ignore messages for our whisper account because we cannot send ourselves whispers anyway | |
if (userstate.username === client.getUsername()) return; // Ignore messages from same account as our client | |
if (userstate["message-type"] === "whisper") return; // Ignore whispers completely | |
// Send messages to us via whisper if we get mentioned | |
if (message.toLowerCase().indexOf(client.getUsername()) === -1) return; // We didn't get mentioned in chat | |
if (config.debugMode) console.log(userstate.username + " mentioned " + client.getUsername() + " in chat. Sending whisper..."); | |
whisperBot.whisper(client.getUsername(), (userstate.mod ? "[M] " : "") + userstate["display-name"] + " mentioned you in channel '" + channel + "' with the following message: " + message); | |
}); | |
client.on("join", (channel, username, self) => { | |
if (self && config.debugMode) console.log("Joined channel '" + channel + "' as " + username); | |
}); | |
client.on("part", (channel, username, self) => { | |
if (self && config.debugMode) console.log("Left channel '" + channel + "' as " + username); | |
}); | |
// client.on("connected", (address, port) => { | |
// | |
// }); | |
client.on("connecting", (address, port) => { | |
client.raw(config.rawUserString) | |
.then((data) => { | |
if (config.debugMode) console.log("Sent USER command as " + client.getUsername()); | |
}).catch((err) => { | |
console.log("An error occured while sending USER command: ", err); | |
}); | |
}); | |
client.on("logon", () => { | |
console.log("Connected as " + client.getUsername()); | |
if (config.debugMode) { | |
sleep(3000).then(() => { | |
client.say(config.whisperBot.name, "HONEYDETECTED Lurk bot reconnected.") | |
}); | |
} | |
}); | |
client.on("reconnect", () => { | |
console.log("Reconnected as " + client.getUsername()); | |
}); | |
client.on("disconnected", (reason) => { | |
console.log("Disconnected as " + client.getUsername()); | |
}); | |
client.connect(); | |
sessions.push(client); | |
}); | |
}; | |
const clientsJoinChannelsCronJob = new cron.CronJob(config.rerollCronjob, clientsJoinChannels, null, true, config.timezone, false, true); | |
const sigs = ['SIGINT', 'SIGTERM', 'SIGQUIT']; | |
sigs.forEach(sig => { | |
process.on(sig, () => { | |
console.log(sig + " received"); | |
clientsJoinChannelsCronJob.stop(); | |
sessions.forEach(client => { | |
client.disconnect(); | |
}); | |
if (whisperBot) whisperBot.disconnect(); | |
console.log("Waiting 3 seconds before exiting..."); | |
sleep(3000).then(() => { | |
process.exit(0); | |
}); | |
}); | |
}); | |
/** | |
* Fisher-Yates shuffle - Shuffles array in place | |
* @param {Array} a items An array containing the items. | |
*/ | |
function shuffle(a) { | |
for (let i = a.length - 1; i > 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[a[i], a[j]] = [a[j], a[i]]; | |
} | |
return a; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "lurkbot-node", | |
"version": "0.0.3", | |
"description": "Twitch lurk bot", | |
"main": "lurkbot.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "SunRed", | |
"license": "MIT", | |
"dependencies": { | |
"cron": "^1.7.2", | |
"tmi.js": "^1.4.5" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment