This repository has been archived on 2022-08-19. You can view files and clone it, but cannot push or open issues/pull-requests.
WallopsServ/index.js

303 lines
12 KiB
JavaScript

let IRC = require("irc-framework")
const c = (s) => String.fromCodePoint(s);
const bold = c(0x02)
const color = c(0x03)
const reset = c(0x0f)
let fs = require("fs")
const nodemailer = require("nodemailer");
let conf = JSON.parse(fs.readFileSync("wallopsserv.json"))
let transporter = nodemailer.createTransport({
host: conf.mail.host,
port: conf.mail.port,
secure: conf.mail.secure,
auth: {
user: conf.mail.user,
pass: conf.mail.pass
}
});
const bot = new IRC.Client({
nick: conf.user.nick,
username: conf.user.ident,
gecos: conf.user.gecos,
version: conf.user.nick,
host: conf.server.host,
tls: conf.server.tls,
port: conf.server.port
});
let map = conf.relays
let exemptUsers = conf.cooldownExempt
let admin = conf.admin
let map2 = conf.shortMap
bot.connect();
let cd = {}
let globCd = 0
let wallops = []
let joins = []
if (!fs.existsSync("log.txt")) fs.writeFileSync("log.txt", "")
let log = fs.readFileSync("log.txt").toString().split("\n").filter(x => x)
let lockActive = false
function logS(str) {
let d = new Date()
log.push(`[${d.getUTCDate().toString().padStart(2, "0")}/${d.getUTCMonth().toString().padStart(2, "0")} ${d.getUTCHours().toString().padStart(2, "0")}:${d.getUTCMinutes().toString().padStart(2, "0")}:${d.getUTCSeconds().toString().padStart(2, "0")}] ${str}`)
if (log.length > conf.logLength) log.shift()
fs.writeFileSync("log.txt", log.join("\n"))
}
logS("Bot started")
bot.on("registered", () => {
bot.raw("oper " + conf.oper.name + " " + conf.oper.pass);
bot.join(conf.channel);
bot.join("#*Serv")
bot.mode(conf.user.nick, conf.user.mode)
});
function genIdentity(e) {
if (map2[genIdentity2(e)]) {
return `${bold}${color}14[${color}13${map2[genIdentity2(e)]}${color}14]`
}
let identity = `${bold}${color}14[`
let tn = e.nick
let bridge = ""
if (tn.includes("/") && tn.split("/").length === 2) {
let els = tn.split("/")
if (map[els[1]]) {
tn = els[0]
bridge = map[els[1]]
}
}
if (tn || e.ident) {
identity += `${color}07${tn}${color}03!${color}12${e.ident}${color}03@${color}10${e.hostname.replace(/~/g, "")}`
} else {
identity += `${color}03~${color}10${e.hostname.replace(/~/g, "")}`
}
if (bridge) {
identity += `${color}03~${color}12${bridge}`
}
identity += `${color}14]`
return identity
}
function genIdentity2(e) {
let identity = ``
let tn = e.nick
let bridge = ""
if (tn.includes("/") && tn.split("/").length === 2) {
let els = tn.split("/")
if (map[els[1]]) {
tn = els[0]
bridge = map[els[1]]
}
}
if (tn || e.ident) {
identity += `${tn}!${e.ident}@${e.hostname.replace(/~/g, "")}`
} else {
identity += `~${e.hostname.replace(/~/g, "")}`
}
if (bridge) {
identity += `~${bridge}`
}
return identity
}
bot.on("wallops", (wallop) => {
if (wallop.nick.toLowerCase() !== conf.user.nick.toLowerCase()) {
bot.notice(conf.channel, `${genIdentity(wallop)} ${reset}${wallop.message}`)
}
})
bot.on("privmsg", (e) => {
if (e.target === conf.user.nick) {
bot.notice(e.nick, bold + "+--------------- WallopsServ ---------------+")
bot.notice(e.nick, "" + "| WallopsServ provides a channel to send |")
bot.notice(e.nick, "" + "| and receive wallops. The prefix " + conf.prefix + " can be |")
bot.notice(e.nick, "" + "| used in the channel to send one. |")
bot.notice(e.nick, "" + "| Channel: " + conf.channel.padEnd(31, " ") + " |")
bot.notice(e.nick, bold + "+--------------- WallopsServ ---------------+")
return
}
if (e.target === conf.channel && admin.includes(genIdentity2(e)) && e.message.startsWith(conf.cmdPrefix)) {
let args = e.message.slice(conf.cmdPrefix.length).split(" ")
let cmd = args.shift()
switch (cmd) {
case "rehash":
logS(genIdentity2(e) + " rehashed the config")
conf = JSON.parse(fs.readFileSync("wallopsserv.json"))
map = conf.relays
exemptUsers = conf.cooldownExempt
admin = conf.admin
map2 = conf.shortMap
bot.notice(conf.channel, "Rehashed.")
break
case "sendlog":
logS(genIdentity2(e) + " requested the log")
transporter.sendMail({
from: conf.mail.sender,
to: conf.mail.receiver,
subject: "Log request",
text: "The log is attached to this message.",
attachments: [
{
filename: "log.txt",
contentDisposition: "inline",
content: log.join("\n"),
contentType: "text/plain"
}
]
})
bot.notice(conf.channel, "Log sent.")
break
case "forcelock":
if (args.length < 1) return bot.say(conf.channel, "No type specified")
if (args[0] !== "i" && args[0] !== "m") return bot.say(conf.channel, "Invalid lock type")
logS("Channel locked: spam")
lockActive = true
bot.say(conf.channel, bold + "Locking channel for 60 seconds")
bot.mode(conf.channel, "+" + args[0])
bot.raw("locops", "#wallops has been locked due to request")
transporter.sendMail({
from: conf.mail.sender,
to: conf.mail.receiver,
subject: "#wallops has been locked (on demand)",
text: "#wallops has been locked via the request of " + genIdentity2(e) + " with mode +" + args[0] + ".\nA log has been attached.",
attachments: [
{
filename: "log.txt",
contentDisposition: "inline",
content: log.join("\n"),
contentType: "text/plain"
}
]
})
setTimeout(() => {
lockActive = false
bot.say(conf.channel, bold + "Channel unlocked")
bot.mode(conf.channel, "-" + args[0])
logS("Channel unlocked")
}, 60000)
break
}
return
}
if (e.target === conf.channel && e.message === conf.cmdPrefix + "hostmask") {
bot.say(conf.channel, "Your hostmask is: " + genIdentity2(e))
return
}
if (!e.message.startsWith(conf.prefix) || e.message.length < 2) return
if (e.target === conf.channel) {
if (e.message.length > 1000) {
bot.say(conf.channel, `${e.nick}: Message too long`)
return
}
if (!exemptUsers.includes(genIdentity2(e))) {
if (cd[e.nick.toLowerCase()] > Date.now()) {
bot.say(conf.channel, `${e.nick}: Whow, slow down there! (${bold}${((cd[e.nick.toLowerCase()] - Date.now()) / 1000).toFixed(1)}s${reset})`)
return
}
if (globCd > Date.now()) {
bot.say(conf.channel, `${e.nick}: Whow, slow down there! (${bold}${((globCd - Date.now()) / 1000).toFixed(1)}s${reset} channel)`)
return
}
globCd = Date.now() + 5000
cd[e.nick.toLowerCase()] = Date.now() + 20000
}
bot.raw(`wallops :${genIdentity(e)} ${reset}${e.message.slice(1)}`)
logS(genIdentity2(e) + ` (${e.tags["unrealircd.org/userip"] || "???"})` + ": " + e.message.slice(1))
if (!exemptUsers.includes(genIdentity2(e))) {
wallops.push(Date.now() + 60000)
wallops = wallops.filter(l => l > Date.now())
if (wallops.length > 9 && !lockActive) {
logS("Channel locked: spam")
lockActive = true
bot.say(conf.channel, bold + "Locking channel for 30 seconds")
bot.mode(conf.channel, "+m")
bot.raw("locops", "#wallops has been locked temporarily due to spam")
transporter.sendMail({
from: conf.mail.sender,
to: conf.mail.receiver,
subject: "#wallops has been locked (probable spam attack)",
text: "#wallops has been locked due to a probable spam attack.\nA log has been attached.",
attachments: [
{
filename: "log.txt",
contentDisposition: "inline",
content: log.join("\n"),
contentType: "text/plain"
}
]
})
setTimeout(() => {
lockActive = false
bot.say(conf.channel, bold + "Channel unlocked")
bot.mode(conf.channel, "-m")
logS("Channel unlocked")
}, 30000)
}
}
}
});
bot.on("join", (evt) => {
if (evt.channel !== conf.channel || evt.nick === conf.user.nick) return
logS(`${evt.nick}!${evt.ident}@${evt.hostname}${evt.gecos ? `#${evt.gecos}` : ""} (${evt.tags["unrealircd.org/userip"] || "???"}) joins`)
if (evt.nick.includes("/")) return
joins = joins.filter(l => l[0] !== evt.nick.toLowerCase())
joins.push([evt.nick.toLowerCase(), Date.now() + 60000])
joins = joins.filter(l => l[1] > Date.now())
if (joins.length > 5 && !lockActive) {
logS("Channel locked: too many joins")
lockActive = true
bot.say(conf.channel, bold + "Locking channel for 60 seconds")
bot.mode(conf.channel, "+i")
bot.raw("locops", "#wallops has been locked temporarily due to too many joins")
transporter.sendMail({
from: conf.mail.sender,
to: conf.mail.receiver,
subject: "#wallops has been locked (too many joins)",
text: "#wallops has been locked due to too many joins.\nA log has been attached.",
attachments: [
{
filename: "log.txt",
contentDisposition: "inline",
content: log.join("\n"),
contentType: "text/plain"
}
]
})
setTimeout(() => {
lockActive = false
bot.say(conf.channel, bold + "Channel unlocked")
bot.mode(conf.channel, "-i")
logS("Channel unlocked")
}, 60000)
}
})
bot.on("part", (evt) => {
if (evt.nick === conf.user.nick) return
logS(`${evt.nick}!${evt.ident}@${evt.hostname}${evt.gecos ? `#${evt.gecos}` : ""} (${evt.tags["unrealircd.org/userip"] || "???"}) parts`)
})
bot.on("quit", (evt) => {
if (evt.nick === conf.user.nick) return
logS(`${evt.nick}!${evt.ident}@${evt.hostname}${evt.gecos ? `#${evt.gecos}` : ""} (${evt.tags["unrealircd.org/userip"] || "???"}) quits`)
})
bot.on("kick", (e) => {
if (e.kicked === conf.user.nick && e.channel === conf.channel) {
bot.join(conf.channel);
bot.raw(`kill ${e.nick} :Go away and don't disturb me (kicked WallopsServ from ${conf.channel})`);
}
});
bot.on("raw", (s) => {
if (!s.from_server) return
if (!s.line.endsWith("\r\n")) return
let r = s.line.slice(0, -2).split(" ")
if (r[0].startsWith(":") && r[1] === "KILL" && r[2].toLowerCase() === conf.user.nick) {
logS("Killed by " + r[0].slice(1))
transporter.sendMail({
from: conf.mail.sender,
to: conf.mail.receiver,
subject: "Bot killed",
text: "The bot has been killed by " + r[0].slice(1) + "\nA log has been attached.",
attachments: [
{
filename: "log.txt",
contentDisposition: "inline",
content: log.join("\n"),
contentType: "text/plain"
}
]
})
}
})