303 lines
12 KiB
JavaScript
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"
|
|
}
|
|
]
|
|
})
|
|
}
|
|
}) |