2022-07-21 12:40:09 +00:00
|
|
|
import sys
|
|
|
|
from copy import deepcopy
|
|
|
|
from datetime import datetime
|
|
|
|
from random import randint
|
|
|
|
|
|
|
|
from twisted.internet import reactor
|
2022-07-21 12:40:01 +00:00
|
|
|
from twisted.internet.defer import Deferred
|
2022-07-21 12:40:09 +00:00
|
|
|
from twisted.internet.protocol import ReconnectingClientFactory
|
2022-07-21 12:40:01 +00:00
|
|
|
from twisted.internet.task import LoopingCall
|
|
|
|
from twisted.words.protocols.irc import (
|
|
|
|
IRCBadMessage,
|
2022-07-21 12:40:09 +00:00
|
|
|
IRCClient,
|
|
|
|
lowDequote,
|
|
|
|
numeric_to_symbolic,
|
2022-07-21 12:40:01 +00:00
|
|
|
)
|
|
|
|
|
2022-07-21 12:40:09 +00:00
|
|
|
import main
|
2019-08-05 21:51:16 +00:00
|
|
|
from core.relay import sendRelayNotification
|
2022-08-09 06:20:30 +00:00
|
|
|
from modules import chankeep, counters, helpers, monitor, regproc, userinfo
|
2019-08-15 20:20:49 +00:00
|
|
|
from utils.dedup import dedup
|
2022-08-11 20:47:44 +00:00
|
|
|
from utils.logging.debug import debug, trace
|
2022-07-21 12:40:09 +00:00
|
|
|
from utils.logging.log import error, log, warn
|
2022-07-21 12:40:05 +00:00
|
|
|
from utils.logging.send import sendAll
|
2019-10-17 19:19:35 +00:00
|
|
|
from utils.parsing import parsen
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2019-01-26 01:57:24 +00:00
|
|
|
|
2019-12-07 16:35:29 +00:00
|
|
|
# Copied from the Twisted source so we can fix a bug
|
2019-10-31 15:44:59 +00:00
|
|
|
def parsemsg(s):
|
|
|
|
"""
|
|
|
|
Breaks a message from an IRC server into its prefix, command, and
|
|
|
|
arguments.
|
|
|
|
@param s: The message to break.
|
|
|
|
@type s: L{bytes}
|
|
|
|
@return: A tuple of (prefix, command, args).
|
|
|
|
@rtype: L{tuple}
|
|
|
|
"""
|
2022-07-21 12:39:41 +00:00
|
|
|
prefix = ""
|
2019-10-31 15:44:59 +00:00
|
|
|
trailing = []
|
|
|
|
if not s:
|
|
|
|
raise IRCBadMessage("Empty line.")
|
2022-07-21 12:39:41 +00:00
|
|
|
if s[0:1] == ":":
|
|
|
|
prefix, s = s[1:].split(" ", 1)
|
|
|
|
if s.find(" :") != -1:
|
|
|
|
s, trailing = s.split(" :", 1)
|
|
|
|
args = s.split(" ") # Twisted bug fixed by adding an argument to split()
|
2019-10-31 15:44:59 +00:00
|
|
|
args.append(trailing)
|
|
|
|
else:
|
2022-07-21 12:39:41 +00:00
|
|
|
args = s.split(" ") # And again
|
2019-10-31 15:44:59 +00:00
|
|
|
command = args.pop(0)
|
|
|
|
return prefix, command, args
|
|
|
|
|
2022-07-21 12:39:41 +00:00
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
class IRCBot(IRCClient):
|
2019-08-25 20:29:11 +00:00
|
|
|
def __init__(self, net, num):
|
2019-10-31 15:44:59 +00:00
|
|
|
self.isconnected = False
|
2018-02-23 22:05:40 +00:00
|
|
|
self.channels = []
|
2019-08-25 20:29:11 +00:00
|
|
|
self.net = net
|
2022-08-14 11:43:33 +00:00
|
|
|
self.authenticated = not regproc.needToAuth(self.net)
|
2019-08-25 20:29:11 +00:00
|
|
|
self.num = num
|
2018-03-14 20:13:40 +00:00
|
|
|
self.buffer = ""
|
2019-08-25 20:29:11 +00:00
|
|
|
self.name = net + str(num)
|
2019-09-29 21:45:16 +00:00
|
|
|
alias = main.alias[num]
|
2019-08-25 20:29:11 +00:00
|
|
|
relay = main.network[self.net].relays[num]
|
2022-08-13 19:37:21 +00:00
|
|
|
self.netinst = main.network[self.net]
|
2019-01-26 18:58:21 +00:00
|
|
|
self.nickname = alias["nick"]
|
|
|
|
self.realname = alias["realname"]
|
2022-07-21 12:39:41 +00:00
|
|
|
self.username = alias["nick"].lower() + "/" + relay["net"]
|
2019-08-25 20:29:11 +00:00
|
|
|
self.password = main.config["Relay"]["Password"]
|
2022-07-21 12:39:41 +00:00
|
|
|
self.userinfo = None #
|
|
|
|
self.fingerReply = None #
|
|
|
|
self.versionName = None # Don't give out information
|
|
|
|
self.versionNum = None #
|
|
|
|
self.versionEnv = None #
|
|
|
|
self.sourceURL = None #
|
|
|
|
|
|
|
|
self._getWho = {} # LoopingCall objects -- needed to be able to stop them
|
|
|
|
|
|
|
|
self._tempWho = {} # temporary storage for gathering WHO info
|
|
|
|
self._tempNames = {} # temporary storage for gathering NAMES info
|
|
|
|
self._tempList = ([], []) # temporary storage for gathering LIST info
|
|
|
|
self.listOngoing = False # we are currently receiving a LIST
|
|
|
|
self.listRetried = False # we asked and got nothing so asked again
|
|
|
|
self.listAttempted = False # we asked for a list
|
|
|
|
self.listSimple = False # after asking again we got the list, so use the simple
|
|
|
|
# syntax from now on
|
2022-07-21 12:40:01 +00:00
|
|
|
self.wantList = False # we want to send a LIST, but not all relays are active yet
|
2019-10-08 20:00:57 +00:00
|
|
|
self.chanlimit = 0
|
2020-07-09 18:43:47 +00:00
|
|
|
self.prefix = {}
|
2019-10-20 15:44:33 +00:00
|
|
|
self.servername = None
|
2019-10-08 17:15:23 +00:00
|
|
|
|
2020-05-31 20:52:56 +00:00
|
|
|
self._regAttempt = None
|
2020-10-31 16:49:37 +00:00
|
|
|
self._negativePass = None
|
2020-05-31 20:52:56 +00:00
|
|
|
|
2019-10-31 15:44:59 +00:00
|
|
|
def lineReceived(self, line):
|
|
|
|
if bytes != str and isinstance(line, bytes):
|
|
|
|
# decode bytes from transport to unicode
|
|
|
|
line = line.decode("utf-8", "replace")
|
|
|
|
|
|
|
|
line = lowDequote(line)
|
|
|
|
try:
|
|
|
|
prefix, command, params = parsemsg(line)
|
|
|
|
if command in numeric_to_symbolic:
|
|
|
|
command = numeric_to_symbolic[command]
|
2020-11-01 22:18:48 +00:00
|
|
|
try:
|
|
|
|
self.handleCommand(command, prefix, params)
|
|
|
|
except Exception as err:
|
|
|
|
error(err)
|
2019-10-31 15:44:59 +00:00
|
|
|
except IRCBadMessage:
|
|
|
|
self.badMessage(line, *sys.exc_info())
|
|
|
|
|
2019-10-11 12:07:57 +00:00
|
|
|
def joinChannels(self, channels):
|
|
|
|
sleeptime = 0.0
|
|
|
|
increment = 0.8
|
|
|
|
for i in channels:
|
2022-07-21 12:40:05 +00:00
|
|
|
if i not in self.channels:
|
2020-11-01 19:54:24 +00:00
|
|
|
if self.net in main.blacklist.keys():
|
|
|
|
if i in main.blacklist[self.net]:
|
2022-07-21 12:40:01 +00:00
|
|
|
debug("Not joining blacklisted channel %s on %s - %i" % (i, self.net, self.num))
|
2020-11-01 19:54:24 +00:00
|
|
|
continue
|
2022-08-11 20:47:44 +00:00
|
|
|
trace(self.net, "-", self.num, ": joining", i, "in", sleeptime, "seconds")
|
2019-10-11 12:07:57 +00:00
|
|
|
reactor.callLater(sleeptime, self.join, i)
|
|
|
|
sleeptime += increment
|
|
|
|
if sleeptime == 10:
|
|
|
|
sleeptime = 0.0
|
|
|
|
increment = 0.7
|
|
|
|
increment += 0.1
|
|
|
|
else:
|
2022-07-21 12:40:01 +00:00
|
|
|
error("%s - Cannot join channel we are already on: %s - %i" % (i, self.net, self.num))
|
2019-10-11 12:07:57 +00:00
|
|
|
|
|
|
|
def checkChannels(self):
|
2022-08-11 19:18:49 +00:00
|
|
|
if chankeep.allRelaysActive(self.net):
|
|
|
|
debug(f"checkChannels() all relays active for {self.net}")
|
|
|
|
else:
|
|
|
|
debug("checkChannels() skipping channel check as we have inactive relays: %s - %i" % (self.net, self.num))
|
2020-05-31 12:09:58 +00:00
|
|
|
return
|
2019-10-11 12:07:57 +00:00
|
|
|
if self.net in main.TempChan.keys():
|
|
|
|
if self.num in main.TempChan[self.net].keys():
|
|
|
|
self.joinChannels(main.TempChan[self.net][self.num])
|
2019-10-12 20:05:55 +00:00
|
|
|
del main.TempChan[self.net][self.num]
|
|
|
|
if not main.TempChan[self.net]:
|
|
|
|
del main.TempChan[self.net]
|
2019-10-11 12:07:57 +00:00
|
|
|
|
2019-08-11 19:52:10 +00:00
|
|
|
def event(self, **cast):
|
2022-07-21 12:40:05 +00:00
|
|
|
if "ts" not in cast.keys():
|
2022-07-21 12:39:54 +00:00
|
|
|
cast["ts"] = str(datetime.now().isoformat())
|
2020-06-02 20:34:15 +00:00
|
|
|
|
|
|
|
# remove odd stuff
|
2022-07-21 12:40:01 +00:00
|
|
|
for i in list(cast.keys()): # Make a copy of the .keys() as Python 3 cannot handle iterating over
|
2022-07-21 12:40:05 +00:00
|
|
|
if cast[i] == "": # a dictionary that changes length with each iteration
|
2019-08-11 19:52:10 +00:00
|
|
|
del cast[i]
|
2020-06-02 20:34:15 +00:00
|
|
|
# remove server stuff
|
2019-08-11 19:52:10 +00:00
|
|
|
if "muser" in cast.keys():
|
2019-10-20 15:44:33 +00:00
|
|
|
if cast["muser"] == self.servername:
|
2020-11-02 20:13:36 +00:00
|
|
|
cast["type"] = "conn"
|
2019-10-20 15:44:33 +00:00
|
|
|
if "channel" in cast.keys():
|
|
|
|
if cast["channel"] == "*":
|
2020-11-02 20:13:36 +00:00
|
|
|
cast["type"] = "conn"
|
2020-06-02 20:34:15 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
# expand out the hostmask
|
2019-10-20 15:44:33 +00:00
|
|
|
if not {"nick", "ident", "host"}.issubset(set(cast.keys())):
|
2020-11-01 19:03:56 +00:00
|
|
|
if "muser" in cast.keys():
|
|
|
|
cast["nick"], cast["ident"], cast["host"] = parsen(cast["muser"])
|
2020-06-02 20:34:15 +00:00
|
|
|
|
|
|
|
# handle ZNC stuff
|
2020-05-31 20:52:56 +00:00
|
|
|
if {"nick", "ident", "host", "msg"}.issubset(set(cast)):
|
2020-06-02 20:34:15 +00:00
|
|
|
if cast["ident"] == "znc" and cast["host"] == "znc.in":
|
|
|
|
cast["type"] = "znc"
|
|
|
|
cast["num"] = self.num
|
|
|
|
del cast["nick"]
|
|
|
|
del cast["ident"]
|
|
|
|
del cast["host"]
|
|
|
|
del cast["channel"]
|
2020-06-07 16:26:53 +00:00
|
|
|
del cast["muser"]
|
2020-06-02 20:34:15 +00:00
|
|
|
if "Disconnected from IRC" in cast["msg"]:
|
|
|
|
log("ZNC disconnected on %s - %i" % (self.net, self.num))
|
|
|
|
self.isconnected = False
|
2020-10-31 00:12:06 +00:00
|
|
|
self.authenticated = False
|
2020-06-02 20:34:15 +00:00
|
|
|
if "Connected!" in cast["msg"]:
|
|
|
|
log("ZNC connected on %s - %i" % (self.net, self.num))
|
|
|
|
self.isconnected = True
|
2020-10-31 23:58:51 +00:00
|
|
|
if "could not be joined, disabling it" in cast["msg"]:
|
|
|
|
error(cast["msg"])
|
2020-06-02 20:34:15 +00:00
|
|
|
#
|
|
|
|
|
|
|
|
# don't reprocess the same message twice
|
|
|
|
# if the type is in that list, it's already been here, don't run it again
|
2020-11-02 20:13:36 +00:00
|
|
|
if not cast["type"] in {"query", "self", "highlight", "znc", "who", "conn"}:
|
2020-06-02 20:34:15 +00:00
|
|
|
cast["num"] = self.num
|
|
|
|
if "channel" in cast.keys():
|
|
|
|
if cast["type"] == "mode":
|
2020-06-07 16:26:53 +00:00
|
|
|
if cast["channel"].lower() == self.nickname.lower():
|
2022-07-21 12:39:41 +00:00
|
|
|
# castDup = deepcopy(cast)
|
2020-06-07 16:26:53 +00:00
|
|
|
cast["mtype"] = cast["type"]
|
|
|
|
cast["type"] = "self"
|
2022-07-21 12:39:41 +00:00
|
|
|
# self.event(**castDup)
|
|
|
|
if cast["modearg"]: # check if modearg is non-NoneType
|
2020-06-02 20:34:15 +00:00
|
|
|
if self.nickname.lower() == cast["modearg"].lower():
|
|
|
|
castDup = deepcopy(cast)
|
|
|
|
castDup["mtype"] = cast["type"]
|
2020-06-07 16:26:53 +00:00
|
|
|
castDup["type"] = "highlight"
|
2020-06-02 20:34:15 +00:00
|
|
|
self.event(**castDup)
|
|
|
|
else:
|
2022-08-13 21:46:10 +00:00
|
|
|
if cast["channel"].lower() == self.nickname.lower() or not cast["channel"].startswith("#"):
|
2020-06-02 20:34:15 +00:00
|
|
|
cast["mtype"] = cast["type"]
|
|
|
|
cast["type"] = "query"
|
2022-07-21 12:39:41 +00:00
|
|
|
# self.event(**castDup)
|
2020-06-02 20:34:15 +00:00
|
|
|
# Don't call self.event for this one because queries are not events on a
|
|
|
|
# channel, but we still want to see them
|
2022-08-13 21:15:50 +00:00
|
|
|
if cast["channel"] == "AUTH":
|
|
|
|
cast["type"] = "conn"
|
|
|
|
cast["mtype"] = cast["type"]
|
2020-06-02 20:34:15 +00:00
|
|
|
|
2020-06-07 16:26:53 +00:00
|
|
|
# TODO: better way to do this
|
|
|
|
# as we changed the types above, check again
|
2020-11-02 20:13:36 +00:00
|
|
|
if not cast["type"] in {"query", "self", "highlight", "znc", "who", "conn"}:
|
2020-06-07 16:26:53 +00:00
|
|
|
# we have been kicked
|
|
|
|
if "user" in cast.keys():
|
|
|
|
if cast["user"].lower() == self.nickname.lower():
|
2019-08-15 21:14:45 +00:00
|
|
|
castDup = deepcopy(cast)
|
|
|
|
castDup["mtype"] = cast["type"]
|
2020-06-07 16:26:53 +00:00
|
|
|
castDup["type"] = "self"
|
2022-08-14 19:44:04 +00:00
|
|
|
if self.net in main.blacklist.keys():
|
|
|
|
if cast["channel"] not in main.blacklist[self.net]:
|
|
|
|
main.blacklist[self.net].append(cast["channel"])
|
|
|
|
else:
|
|
|
|
main.blacklist[self.net] = [cast["channel"]]
|
|
|
|
main.saveConf("blacklist")
|
2019-08-15 21:14:45 +00:00
|
|
|
self.event(**castDup)
|
2019-08-11 19:52:10 +00:00
|
|
|
|
2020-06-07 16:26:53 +00:00
|
|
|
# we sent a message/left/joined/kick someone/quit
|
|
|
|
if "nick" in cast.keys():
|
|
|
|
if cast["nick"].lower() == self.nickname.lower():
|
|
|
|
castDup = deepcopy(cast)
|
|
|
|
castDup["mtype"] = cast["type"]
|
|
|
|
castDup["type"] = "self"
|
|
|
|
|
|
|
|
# we have been mentioned in a msg/notice/action/part/quit/topic message
|
2022-07-21 12:39:41 +00:00
|
|
|
if "msg" in cast.keys(): # Don't highlight queries
|
2022-07-21 12:40:05 +00:00
|
|
|
if cast["msg"] is not None:
|
2020-06-07 16:26:53 +00:00
|
|
|
if self.nickname.lower() in cast["msg"].lower():
|
|
|
|
castDup = deepcopy(cast)
|
|
|
|
castDup["mtype"] = cast["type"]
|
|
|
|
castDup["type"] = "highlight"
|
|
|
|
self.event(**castDup)
|
|
|
|
|
2022-07-21 12:40:05 +00:00
|
|
|
if "net" not in cast.keys():
|
2019-10-05 17:13:04 +00:00
|
|
|
cast["net"] = self.net
|
2022-07-21 12:40:05 +00:00
|
|
|
if "num" not in cast.keys():
|
2020-05-31 20:52:56 +00:00
|
|
|
cast["num"] = self.num
|
2020-06-07 16:26:53 +00:00
|
|
|
if not self.authenticated:
|
|
|
|
regproc.registerTest(cast)
|
2019-08-15 20:20:49 +00:00
|
|
|
counters.event(self.net, cast["type"])
|
2019-10-05 17:13:04 +00:00
|
|
|
monitor.event(self.net, cast)
|
2019-08-11 19:52:10 +00:00
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
def privmsg(self, user, channel, msg):
|
2019-10-04 23:51:00 +00:00
|
|
|
self.event(type="msg", muser=user, channel=channel, msg=msg)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def noticed(self, user, channel, msg):
|
2019-10-04 23:51:00 +00:00
|
|
|
self.event(type="notice", muser=user, channel=channel, msg=msg)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def action(self, user, channel, msg):
|
2019-10-04 23:51:00 +00:00
|
|
|
self.event(type="action", muser=user, channel=channel, msg=msg)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2022-08-09 06:20:30 +00:00
|
|
|
def sendmsg(self, channel, msg, in_query=False):
|
2022-08-09 06:20:30 +00:00
|
|
|
query = f"{self.nickname}!*@*"
|
2022-08-14 15:45:40 +00:00
|
|
|
res = userinfo.getWhoSingle(self.net, query)
|
|
|
|
if not res:
|
|
|
|
res = []
|
|
|
|
us = list(res)
|
2022-08-09 06:20:30 +00:00
|
|
|
if len(us) > 0:
|
|
|
|
hostmask = us[0]
|
|
|
|
else:
|
|
|
|
# Close enough...
|
|
|
|
hostmask = f"{self.nickname}!*@{self.servername}"
|
|
|
|
warn(f"Could not get a hostname, using {hostmask}")
|
|
|
|
nick, ident, host = parsen(hostmask)
|
2022-08-09 06:20:30 +00:00
|
|
|
# We sent someone a query reply
|
|
|
|
if in_query:
|
|
|
|
self.event(type="self", mtype="msg", channel=self.nickname, nick=channel, ident=ident, host=host, msg=msg)
|
|
|
|
else:
|
|
|
|
self.event(type="self", mtype="msg", channel=channel, nick=self.nickname, ident=ident, host=host, msg=msg)
|
|
|
|
self.event(type="msg", channel=channel, nick=self.nickname, ident=ident, host=host, msg=msg)
|
2022-08-09 06:20:30 +00:00
|
|
|
self.msg(channel, msg)
|
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
def get(self, var):
|
|
|
|
try:
|
|
|
|
result = getattr(self, var)
|
|
|
|
except AttributeError:
|
|
|
|
result = None
|
|
|
|
return result
|
|
|
|
|
|
|
|
def setNick(self, nickname):
|
|
|
|
self._attemptedNick = nickname
|
|
|
|
self.sendLine("NICK %s" % nickname)
|
|
|
|
self.nickname = nickname
|
|
|
|
|
|
|
|
def alterCollidedNick(self, nickname):
|
|
|
|
newnick = nickname + "_"
|
|
|
|
return newnick
|
|
|
|
|
2018-08-27 19:42:49 +00:00
|
|
|
def nickChanged(self, olduser, newnick):
|
|
|
|
self.nickname = newnick
|
2019-08-12 20:03:47 +00:00
|
|
|
self.event(type="self", mtype="nick", muser=olduser, user=newnick)
|
2018-03-10 14:01:43 +00:00
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
def irc_ERR_NICKNAMEINUSE(self, prefix, params):
|
|
|
|
self._attemptedNick = self.alterCollidedNick(self._attemptedNick)
|
|
|
|
self.setNick(self._attemptedNick)
|
|
|
|
|
|
|
|
def irc_ERR_PASSWDMISMATCH(self, prefix, params):
|
2020-10-28 22:26:41 +00:00
|
|
|
log("%s - %i: password mismatch as %s" % (self.net, self.num, self.username))
|
2022-07-21 12:40:01 +00:00
|
|
|
sendAll("%s - %i: password mismatch as %s" % (self.net, self.num, self.username))
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2019-09-28 18:46:10 +00:00
|
|
|
def _who(self, channel):
|
2018-02-23 22:05:40 +00:00
|
|
|
d = Deferred()
|
2019-09-28 18:46:10 +00:00
|
|
|
if channel not in self._tempWho:
|
|
|
|
self._tempWho[channel] = ([], [])
|
|
|
|
self._tempWho[channel][0].append(d)
|
2018-02-23 22:05:40 +00:00
|
|
|
self.sendLine("WHO %s" % channel)
|
|
|
|
return d
|
|
|
|
|
2019-09-28 18:46:10 +00:00
|
|
|
def who(self, channel):
|
|
|
|
self._who(channel).addCallback(self.got_who)
|
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
def irc_RPL_WHOREPLY(self, prefix, params):
|
|
|
|
channel = params[1]
|
2019-08-11 19:52:10 +00:00
|
|
|
ident = params[2]
|
2018-02-23 22:05:40 +00:00
|
|
|
host = params[3]
|
|
|
|
server = params[4]
|
|
|
|
nick = params[5]
|
|
|
|
status = params[6]
|
|
|
|
realname = params[7]
|
2019-09-28 18:46:10 +00:00
|
|
|
if channel not in self._tempWho:
|
2018-02-23 22:05:40 +00:00
|
|
|
return
|
2019-09-28 18:46:10 +00:00
|
|
|
n = self._tempWho[channel][1]
|
2019-08-11 19:52:10 +00:00
|
|
|
n.append([nick, nick, host, server, status, realname])
|
2022-07-21 12:39:41 +00:00
|
|
|
self.event(
|
|
|
|
type="who",
|
|
|
|
nick=nick,
|
|
|
|
ident=ident,
|
|
|
|
host=host,
|
|
|
|
realname=realname,
|
|
|
|
channel=channel,
|
|
|
|
server=server,
|
|
|
|
status=status,
|
|
|
|
)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def irc_RPL_ENDOFWHO(self, prefix, params):
|
|
|
|
channel = params[1]
|
2019-09-28 18:46:10 +00:00
|
|
|
if channel not in self._tempWho:
|
2018-02-23 22:05:40 +00:00
|
|
|
return
|
2019-09-28 18:46:10 +00:00
|
|
|
callbacks, info = self._tempWho[channel]
|
2018-02-23 22:05:40 +00:00
|
|
|
for cb in callbacks:
|
|
|
|
cb.callback((channel, info))
|
2019-09-28 18:46:10 +00:00
|
|
|
del self._tempWho[channel]
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def got_who(self, whoinfo):
|
2018-08-27 19:42:49 +00:00
|
|
|
userinfo.initialUsers(self.net, whoinfo[0], whoinfo[1])
|
|
|
|
|
|
|
|
def sanit(self, data):
|
|
|
|
if len(data) >= 1:
|
2020-07-09 18:43:47 +00:00
|
|
|
if data[0] in self.prefix.keys():
|
2022-07-21 12:39:41 +00:00
|
|
|
return (
|
|
|
|
self.prefix[data[0]],
|
|
|
|
data[1:],
|
|
|
|
) # would use a set but it's possible these are the same
|
2020-07-09 18:43:47 +00:00
|
|
|
return (None, data)
|
2018-08-27 19:42:49 +00:00
|
|
|
else:
|
2020-07-09 18:43:47 +00:00
|
|
|
return (None, False)
|
2018-08-27 19:42:49 +00:00
|
|
|
|
|
|
|
def names(self, channel):
|
|
|
|
d = Deferred()
|
2019-10-06 20:10:44 +00:00
|
|
|
if channel not in self._tempNames:
|
|
|
|
self._tempNames[channel] = ([], [])
|
|
|
|
self._tempNames[channel][0].append(d)
|
2018-08-27 19:42:49 +00:00
|
|
|
self.sendLine("NAMES %s" % channel)
|
|
|
|
return d
|
|
|
|
|
|
|
|
def irc_RPL_NAMREPLY(self, prefix, params):
|
|
|
|
channel = params[2]
|
2022-07-21 12:39:41 +00:00
|
|
|
nicklist = params[3].split(" ")
|
2019-10-06 20:10:44 +00:00
|
|
|
if channel not in self._tempNames:
|
2018-08-27 19:42:49 +00:00
|
|
|
return
|
2019-10-06 20:10:44 +00:00
|
|
|
n = self._tempNames[channel][1]
|
2018-08-27 19:42:49 +00:00
|
|
|
n.append(nicklist)
|
|
|
|
|
|
|
|
def irc_RPL_ENDOFNAMES(self, prefix, params):
|
|
|
|
channel = params[1]
|
2019-10-06 20:10:44 +00:00
|
|
|
if channel not in self._tempNames:
|
2018-08-27 19:42:49 +00:00
|
|
|
return
|
2019-10-06 20:10:44 +00:00
|
|
|
callbacks, namelist = self._tempNames[channel]
|
2018-08-27 19:42:49 +00:00
|
|
|
for cb in callbacks:
|
|
|
|
cb.callback((channel, namelist))
|
2019-10-06 20:10:44 +00:00
|
|
|
del self._tempNames[channel]
|
2018-08-27 19:42:49 +00:00
|
|
|
|
|
|
|
def got_names(self, nicklist):
|
|
|
|
newNicklist = []
|
|
|
|
for i in nicklist[1]:
|
|
|
|
for x in i:
|
2020-07-09 18:43:47 +00:00
|
|
|
mode, nick = self.sanit(x)
|
|
|
|
if nick:
|
|
|
|
newNicklist.append((mode, nick))
|
2018-08-27 19:42:49 +00:00
|
|
|
userinfo.initialNames(self.net, nicklist[0], newNicklist)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2019-10-20 15:44:33 +00:00
|
|
|
def myInfo(self, servername, version, umodes, cmodes):
|
|
|
|
self.servername = servername
|
|
|
|
|
2019-10-08 20:00:57 +00:00
|
|
|
def _list(self, noargs):
|
2019-10-06 20:10:44 +00:00
|
|
|
d = Deferred()
|
|
|
|
self._tempList = ([], [])
|
|
|
|
self._tempList[0].append(d)
|
2019-10-20 15:44:33 +00:00
|
|
|
if self.listSimple:
|
|
|
|
self.sendLine("LIST")
|
2022-07-21 12:39:41 +00:00
|
|
|
return d # return early if we know what to do
|
2019-10-31 15:44:59 +00:00
|
|
|
|
2019-10-08 20:00:57 +00:00
|
|
|
if noargs:
|
|
|
|
self.sendLine("LIST")
|
|
|
|
else:
|
|
|
|
self.sendLine("LIST >0")
|
2019-10-06 20:10:44 +00:00
|
|
|
return d
|
|
|
|
|
2019-10-31 15:44:59 +00:00
|
|
|
def list(self, noargs=False, nocheck=False):
|
2020-06-07 16:26:53 +00:00
|
|
|
if not self.authenticated:
|
|
|
|
debug("Will not send LIST, unauthenticated: %s - %i" % (self.net, self.num))
|
2020-05-30 20:40:10 +00:00
|
|
|
return
|
2019-10-31 15:44:59 +00:00
|
|
|
if self.listAttempted:
|
2022-07-21 12:40:01 +00:00
|
|
|
debug("List request dropped, already asked for LIST - %s - %i" % (self.net, self.num))
|
2019-10-20 15:44:33 +00:00
|
|
|
return
|
|
|
|
else:
|
2019-10-31 15:44:59 +00:00
|
|
|
self.listAttempted = True
|
|
|
|
if self.listOngoing:
|
2022-07-21 12:40:01 +00:00
|
|
|
debug("LIST request dropped, already ongoing - %s - %i" % (self.net, self.num))
|
2019-10-20 15:44:33 +00:00
|
|
|
return
|
2019-10-31 15:44:59 +00:00
|
|
|
else:
|
|
|
|
if nocheck:
|
2022-07-21 12:39:41 +00:00
|
|
|
allRelays = True # override the system - if this is
|
2022-07-21 12:40:05 +00:00
|
|
|
else: # specified, we already did this
|
2019-10-31 15:44:59 +00:00
|
|
|
allRelays = chankeep.allRelaysActive(self.net)
|
|
|
|
if not allRelays:
|
|
|
|
self.wantList = True
|
|
|
|
debug("Not all relays were active for LIST request")
|
|
|
|
return
|
|
|
|
self._list(noargs).addCallback(self.got_list)
|
2019-10-08 17:15:23 +00:00
|
|
|
|
|
|
|
def irc_RPL_LISTSTART(self, prefix, params):
|
2019-10-20 15:44:33 +00:00
|
|
|
self.listAttempted = False
|
2019-10-08 17:15:23 +00:00
|
|
|
self.listOngoing = True
|
2019-10-31 15:44:59 +00:00
|
|
|
self.wantList = False
|
2019-10-06 20:10:44 +00:00
|
|
|
|
|
|
|
def irc_RPL_LIST(self, prefix, params):
|
|
|
|
channel = params[1]
|
|
|
|
users = params[2]
|
|
|
|
topic = params[3]
|
|
|
|
self._tempList[1].append([channel, users, topic])
|
|
|
|
|
|
|
|
def irc_RPL_LISTEND(self, prefix, params):
|
2022-07-21 12:40:01 +00:00
|
|
|
if not len(self._tempList[0]) > 0: # there are no callbacks, can't do anything there
|
2019-10-31 15:44:59 +00:00
|
|
|
debug("We didn't ask for this LIST, discarding")
|
|
|
|
self._tempList[0].clear()
|
|
|
|
self._tempList[1].clear()
|
|
|
|
return
|
2019-10-06 20:10:44 +00:00
|
|
|
callbacks, info = self._tempList
|
2019-10-08 20:00:57 +00:00
|
|
|
self.listOngoing = False
|
2019-10-06 20:10:44 +00:00
|
|
|
for cb in callbacks:
|
|
|
|
cb.callback((info))
|
2019-10-08 20:00:57 +00:00
|
|
|
noResults = False
|
|
|
|
if len(self._tempList[1]) == 0:
|
|
|
|
noResults = True
|
2019-10-06 20:10:44 +00:00
|
|
|
self._tempList[0].clear()
|
|
|
|
self._tempList[1].clear()
|
2019-10-08 20:00:57 +00:00
|
|
|
if noResults:
|
2019-10-31 15:44:59 +00:00
|
|
|
if self.listRetried:
|
2020-04-21 22:32:17 +00:00
|
|
|
warn("LIST still empty after retry: %s - %i" % (self.net, self.num))
|
2019-10-08 20:00:57 +00:00
|
|
|
self.listRetried = False
|
|
|
|
return
|
2019-10-31 15:44:59 +00:00
|
|
|
else:
|
|
|
|
self.list(True)
|
|
|
|
self.listRetried = True
|
2019-10-08 20:00:57 +00:00
|
|
|
else:
|
2019-10-20 15:44:33 +00:00
|
|
|
if self.listRetried:
|
|
|
|
self.listRetried = False
|
2022-07-21 12:40:01 +00:00
|
|
|
debug("List received after retry - defaulting to simple list syntax: %s - %i" % (self.net, self.num))
|
2019-10-20 15:44:33 +00:00
|
|
|
self.listSimple = True
|
2019-10-06 20:10:44 +00:00
|
|
|
|
|
|
|
def got_list(self, listinfo):
|
2022-07-21 12:39:41 +00:00
|
|
|
if len(listinfo) == 0: # probably ngircd not supporting LIST >0
|
2019-10-08 20:00:57 +00:00
|
|
|
return
|
2022-07-21 12:40:11 +00:00
|
|
|
if main.config["ChanKeep"]["Enabled"]:
|
2022-08-13 18:20:29 +00:00
|
|
|
chankeep.initialList(self.net, self.num, listinfo)
|
2019-10-08 20:00:57 +00:00
|
|
|
|
2020-06-07 16:26:53 +00:00
|
|
|
def recheckList(self):
|
2022-08-14 10:41:29 +00:00
|
|
|
if not main.config["ChanKeep"]["Enabled"]:
|
|
|
|
return
|
2020-06-07 16:26:53 +00:00
|
|
|
allRelays = chankeep.allRelaysActive(self.net)
|
2022-08-11 19:18:49 +00:00
|
|
|
debug(f"recheckList() all relays for {self.net} {allRelays}")
|
2020-06-07 16:26:53 +00:00
|
|
|
if allRelays:
|
2022-08-11 19:18:49 +00:00
|
|
|
debug(f"recheckList() all relays active for {self.net}")
|
2022-08-11 18:22:09 +00:00
|
|
|
first_relay = helpers.get_first_relay(self.net)
|
2022-08-11 19:18:49 +00:00
|
|
|
debug(f"recheckList() first relay for {self.net}: {first_relay.num}")
|
2022-08-11 18:22:09 +00:00
|
|
|
if first_relay:
|
|
|
|
if first_relay.wantList is True:
|
|
|
|
first_relay.list(nocheck=True)
|
2022-08-14 09:58:28 +00:00
|
|
|
debug(f"recheckList() asking for a list for {self.net} after final relay {self.num} connected")
|
2022-08-11 19:21:39 +00:00
|
|
|
else:
|
|
|
|
debug(f"recheckList() first relay wantList is False for {self.net} ({first_relay.num})")
|
2022-08-11 18:22:09 +00:00
|
|
|
# name = self.net + "1"
|
|
|
|
|
|
|
|
# if self.num == 1: # Only one instance should do a list
|
|
|
|
if helpers.is_first_relay(self.net, self.num):
|
2022-08-11 19:18:49 +00:00
|
|
|
debug(f"recheckList() we are the first relay for {self.net} ({self.num})")
|
2020-06-07 16:26:53 +00:00
|
|
|
if self.chanlimit:
|
|
|
|
if allRelays:
|
|
|
|
self.list()
|
2022-08-11 19:18:49 +00:00
|
|
|
debug(f"recheckList() requested a list for {self.net} from {self.num}")
|
2020-06-07 16:26:53 +00:00
|
|
|
else:
|
2022-08-11 19:21:39 +00:00
|
|
|
debug(f"recheckList() not all relays active for {self.net}")
|
2020-06-07 16:26:53 +00:00
|
|
|
self.wantList = True
|
|
|
|
else:
|
2022-08-11 19:18:49 +00:00
|
|
|
debug("recheckList() aborting LIST due to bad chanlimit")
|
2020-06-07 16:26:53 +00:00
|
|
|
self.checkChannels()
|
|
|
|
|
|
|
|
def seed_chanlimit(self, chanlimit):
|
2022-07-21 12:39:41 +00:00
|
|
|
if not main.network[self.net].relays[self.num][
|
|
|
|
"registered"
|
|
|
|
]: # TODO: add check for register request sent, only send it once
|
2020-05-30 20:40:10 +00:00
|
|
|
if main.config["AutoReg"]:
|
2022-07-21 12:39:57 +00:00
|
|
|
if not self.authenticated:
|
2022-07-21 12:40:01 +00:00
|
|
|
self._regAttempt = reactor.callLater(5, regproc.registerAccount, self.net, self.num)
|
2022-07-21 12:39:57 +00:00
|
|
|
# regproc.registerAccount(self.net, self.num)
|
2020-06-07 16:26:53 +00:00
|
|
|
try:
|
|
|
|
self.chanlimit = int(chanlimit)
|
|
|
|
except TypeError:
|
2022-07-21 12:40:05 +00:00
|
|
|
warn("Invalid chanlimit: %s" % chanlimit)
|
2020-06-07 16:26:53 +00:00
|
|
|
if self.chanlimit == 0:
|
2022-07-21 12:39:41 +00:00
|
|
|
self.chanlimit = 200 # don't take the piss if it's not limited
|
2022-08-13 19:37:21 +00:00
|
|
|
net_inst_chanlimit = self.netinst.chanlimit
|
|
|
|
if net_inst_chanlimit:
|
|
|
|
if self.chanlimit > net_inst_chanlimit:
|
|
|
|
self.chanlimit = net_inst_chanlimit
|
2022-08-14 10:41:29 +00:00
|
|
|
# warn(f"Chanlimit on {self.net} too high, setting to {self.chanlimit}")
|
2022-08-13 19:37:21 +00:00
|
|
|
|
2022-07-21 12:40:01 +00:00
|
|
|
if not regproc.needToRegister(self.net): # if we need to register, only recheck on auth confirmation
|
2022-08-14 10:41:29 +00:00
|
|
|
if main.config["ChanKeep"]["Enabled"]:
|
|
|
|
self.recheckList()
|
2020-06-07 16:26:53 +00:00
|
|
|
|
|
|
|
def seed_prefix(self, prefix):
|
2020-07-09 18:43:47 +00:00
|
|
|
prefix = prefix.replace(")", "")
|
|
|
|
prefix = prefix.replace("(", "")
|
|
|
|
length = len(prefix)
|
2022-07-21 12:39:41 +00:00
|
|
|
half = int(length / 2)
|
2020-07-09 18:43:47 +00:00
|
|
|
prefixToMode = dict(zip(prefix[half:], prefix[:half]))
|
|
|
|
self.prefix = prefixToMode
|
2020-06-07 16:26:53 +00:00
|
|
|
|
|
|
|
def isupport(self, options):
|
|
|
|
interested = {"CHANLIMIT", "MAXCHANNELS", "PREFIX"}
|
|
|
|
newOptions = {x for x in options if any(y in x for y in interested)}
|
|
|
|
if len(newOptions) == 0:
|
|
|
|
return
|
|
|
|
if not self.isconnected:
|
|
|
|
log("endpoint connected: %s - %i" % (self.net, self.num))
|
|
|
|
self.isconnected = True
|
|
|
|
for i in newOptions:
|
|
|
|
if i.startswith("PREFIX"):
|
|
|
|
if "=" in i:
|
|
|
|
split = i.split("=")
|
|
|
|
if len(split) == 2:
|
|
|
|
prefix = split[1]
|
|
|
|
self.seed_prefix(prefix)
|
|
|
|
elif i.startswith("CHANLIMIT"):
|
2019-10-08 20:00:57 +00:00
|
|
|
if ":" in i:
|
|
|
|
split = i.split(":")
|
2019-10-08 17:15:23 +00:00
|
|
|
if len(split) >= 2:
|
2019-10-08 20:00:57 +00:00
|
|
|
chanlimit = split[1]
|
2020-06-07 16:26:53 +00:00
|
|
|
self.seed_chanlimit(chanlimit)
|
2019-10-20 15:44:33 +00:00
|
|
|
elif i.startswith("MAXCHANNELS"):
|
|
|
|
if "=" in i:
|
|
|
|
split = i.split("=")
|
|
|
|
if len(split) == 2:
|
|
|
|
chanlimit = split[1]
|
2020-06-07 16:26:53 +00:00
|
|
|
self.seed_chanlimit(chanlimit)
|
2019-10-06 20:10:44 +00:00
|
|
|
|
2019-12-07 16:35:29 +00:00
|
|
|
# We need to override these functions as Twisted discards
|
|
|
|
# the hostname and other useful information in the functions
|
|
|
|
# that these call by default
|
2018-03-14 20:13:40 +00:00
|
|
|
def irc_JOIN(self, prefix, params):
|
2022-07-21 12:39:41 +00:00
|
|
|
nick = prefix.split("!")[0]
|
2018-03-14 20:13:40 +00:00
|
|
|
channel = params[-1]
|
|
|
|
if nick == self.nickname:
|
|
|
|
self.joined(channel)
|
|
|
|
else:
|
|
|
|
self.userJoined(prefix, channel)
|
|
|
|
|
|
|
|
def irc_PART(self, prefix, params):
|
2022-07-21 12:39:41 +00:00
|
|
|
nick = prefix.split("!")[0]
|
2018-03-14 20:13:40 +00:00
|
|
|
channel = params[0]
|
|
|
|
if len(params) >= 2:
|
|
|
|
message = params[1]
|
|
|
|
else:
|
|
|
|
message = None
|
|
|
|
if nick == self.nickname:
|
2019-08-05 21:51:16 +00:00
|
|
|
self.left(prefix, channel, message)
|
2018-03-14 20:13:40 +00:00
|
|
|
else:
|
|
|
|
self.userLeft(prefix, channel, message)
|
|
|
|
|
|
|
|
def irc_QUIT(self, prefix, params):
|
2022-07-21 12:40:05 +00:00
|
|
|
# nick = prefix.split("!")[0]
|
2019-08-11 20:52:46 +00:00
|
|
|
self.userQuit(prefix, params[0])
|
2018-03-14 20:13:40 +00:00
|
|
|
|
|
|
|
def irc_NICK(self, prefix, params):
|
2022-07-21 12:39:41 +00:00
|
|
|
nick = prefix.split("!", 1)[0]
|
2018-03-14 20:13:40 +00:00
|
|
|
if nick == self.nickname:
|
2018-08-27 19:42:49 +00:00
|
|
|
self.nickChanged(prefix, params[0])
|
2018-03-14 20:13:40 +00:00
|
|
|
else:
|
|
|
|
self.userRenamed(prefix, params[0])
|
|
|
|
|
|
|
|
def irc_KICK(self, prefix, params):
|
|
|
|
channel = params[0]
|
|
|
|
kicked = params[1]
|
|
|
|
message = params[-1]
|
2019-08-11 19:52:10 +00:00
|
|
|
# Checks on whether it was us that was kicked are done in userKicked
|
|
|
|
self.userKicked(kicked, channel, prefix, message)
|
2018-03-14 20:13:40 +00:00
|
|
|
|
|
|
|
def irc_TOPIC(self, prefix, params):
|
|
|
|
channel = params[0]
|
|
|
|
newtopic = params[1]
|
|
|
|
self.topicUpdated(prefix, channel, newtopic)
|
2022-07-21 12:39:41 +00:00
|
|
|
|
2019-12-07 16:35:29 +00:00
|
|
|
# End of Twisted hackery
|
2018-03-14 20:13:40 +00:00
|
|
|
|
2022-08-14 22:58:35 +00:00
|
|
|
def regPing(self, negativepass=None, reset=True):
|
2020-10-31 16:49:37 +00:00
|
|
|
if self.authenticated:
|
|
|
|
return
|
2022-08-14 11:43:33 +00:00
|
|
|
if not regproc.needToAuth(self.net):
|
2022-08-14 10:41:29 +00:00
|
|
|
self.authenticated = True
|
2022-08-14 11:43:33 +00:00
|
|
|
return
|
2020-10-31 16:49:37 +00:00
|
|
|
sinst = regproc.substitute(self.net, self.num)
|
2022-07-21 12:39:52 +00:00
|
|
|
if not sinst:
|
2022-08-14 08:25:54 +00:00
|
|
|
error(f"regPing() {self.net}: registration ping failed for {self.num}")
|
2022-07-21 12:39:52 +00:00
|
|
|
return
|
2022-08-14 22:58:35 +00:00
|
|
|
if reset:
|
|
|
|
self._negativePass = None
|
|
|
|
|
2022-08-18 06:20:30 +00:00
|
|
|
debug(f"regProc() {self.net} - {self.num}: _negativePass:{self._negativepass} negativepass:{negativepass}")
|
2022-08-18 06:20:30 +00:00
|
|
|
if self._negativePass is None:
|
|
|
|
# We have failed, the blacklisted message has been found
|
2022-07-21 12:40:05 +00:00
|
|
|
if negativepass is False:
|
2020-10-31 16:49:37 +00:00
|
|
|
self._negativePass = False
|
2022-08-18 06:20:30 +00:00
|
|
|
debug(
|
|
|
|
(
|
|
|
|
f"registerTest() {self.net} - {self.num} not passed negative:checknegativemsg "
|
|
|
|
f"check, {sinst['checknegativemsg']} present in message"
|
|
|
|
)
|
|
|
|
)
|
2020-10-31 16:49:37 +00:00
|
|
|
return
|
2022-08-18 06:20:30 +00:00
|
|
|
# End of negative output reached with no blacklisted message
|
2022-08-18 06:20:30 +00:00
|
|
|
elif negativepass is True:
|
2022-08-18 06:20:30 +00:00
|
|
|
if self._negativePass is None: # check if it's not failed yet
|
2020-10-31 16:49:37 +00:00
|
|
|
self._negativePass = True
|
2022-08-18 06:20:30 +00:00
|
|
|
debug(
|
|
|
|
(
|
|
|
|
f"registerTest() {self.net} - {self.num} passed negative:checkendnegative "
|
|
|
|
f"check, {sinst['checkendnegative']} present in message"
|
|
|
|
)
|
|
|
|
)
|
2020-10-31 16:49:37 +00:00
|
|
|
else:
|
2022-08-14 22:58:35 +00:00
|
|
|
debug(f"regPing() {self.net}: negative registration check - {self.num}")
|
2020-10-31 16:49:37 +00:00
|
|
|
return
|
2022-08-18 06:20:30 +00:00
|
|
|
else:
|
2020-10-31 16:49:37 +00:00
|
|
|
if sinst["negative"]:
|
|
|
|
self.msg(sinst["entity"], sinst["negativemsg"])
|
2022-08-14 11:43:33 +00:00
|
|
|
debug(
|
|
|
|
(
|
|
|
|
f"regPing() {self.net}: sent negativemsg "
|
|
|
|
f"'{sinst['negativemsg']}' to {sinst['entity']} - {self.num}"
|
|
|
|
)
|
|
|
|
)
|
2020-10-31 16:49:37 +00:00
|
|
|
return
|
|
|
|
else:
|
|
|
|
self._negativePass = True
|
2022-08-18 06:20:30 +00:00
|
|
|
|
|
|
|
if sinst["check"]:
|
2020-10-31 16:49:37 +00:00
|
|
|
if sinst["ping"]:
|
|
|
|
self.msg(sinst["entity"], sinst["pingmsg"])
|
2022-08-14 10:41:29 +00:00
|
|
|
debug(f"regPing() {self.net}: sent ping '{sinst['pingmsg']}' to {sinst['entity']} - {self.num}")
|
2020-10-31 16:49:37 +00:00
|
|
|
return
|
|
|
|
else:
|
|
|
|
self.authenticated = True
|
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
def signedOn(self):
|
2019-10-05 17:13:04 +00:00
|
|
|
log("signed on: %s - %i" % (self.net, self.num))
|
2020-10-28 22:26:41 +00:00
|
|
|
ctime = str(datetime.now().isoformat())
|
2022-07-21 12:39:41 +00:00
|
|
|
sendRelayNotification(
|
|
|
|
{
|
|
|
|
"type": "conn",
|
|
|
|
"net": self.net,
|
|
|
|
"num": self.num,
|
|
|
|
"status": "signedon",
|
2022-07-21 12:39:54 +00:00
|
|
|
"ts": ctime,
|
2022-07-21 12:39:41 +00:00
|
|
|
}
|
|
|
|
)
|
2020-10-28 22:26:41 +00:00
|
|
|
if not self.authenticated:
|
2020-10-31 16:49:37 +00:00
|
|
|
reactor.callLater(10, self.regPing)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2022-08-14 19:58:41 +00:00
|
|
|
def setup_who_loop(self, channel):
|
|
|
|
# if main.config["Toggles"]["Who"]:
|
|
|
|
lc = LoopingCall(self.who, channel)
|
|
|
|
self._getWho[channel] = lc
|
|
|
|
intrange = main.config["Tweaks"]["Delays"]["WhoRange"]
|
|
|
|
minint = main.config["Tweaks"]["Delays"]["WhoLoop"]
|
|
|
|
interval = randint(minint, minint + intrange)
|
|
|
|
lc.start(interval)
|
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
def joined(self, channel):
|
2022-07-21 12:40:05 +00:00
|
|
|
if channel not in self.channels:
|
2018-02-23 22:05:40 +00:00
|
|
|
self.channels.append(channel)
|
2018-08-27 19:42:49 +00:00
|
|
|
self.names(channel).addCallback(self.got_names)
|
2019-08-11 19:52:10 +00:00
|
|
|
if main.config["Toggles"]["Who"]:
|
2022-08-14 19:58:41 +00:00
|
|
|
reactor.callLater(main.config["Tweaks"]["Delays"]["WhoDelay"], self.setup_who_loop, channel)
|
|
|
|
# lc = LoopingCall(self.who, channel)
|
|
|
|
# self._getWho[channel] = lc
|
|
|
|
# intrange = main.config["Tweaks"]["Delays"]["WhoRange"]
|
|
|
|
# minint = main.config["Tweaks"]["Delays"]["WhoLoop"]
|
|
|
|
# interval = randint(minint, minint + intrange)
|
|
|
|
# lc.start(interval)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2018-08-27 19:42:49 +00:00
|
|
|
def botLeft(self, channel):
|
2018-02-23 22:05:40 +00:00
|
|
|
if channel in self.channels:
|
|
|
|
self.channels.remove(channel)
|
2018-08-27 19:42:49 +00:00
|
|
|
if channel in self._getWho.keys():
|
|
|
|
lc = self._getWho[channel]
|
|
|
|
lc.stop()
|
|
|
|
del self._getWho[channel]
|
2022-07-21 12:40:01 +00:00
|
|
|
userinfo.delChannels(self.net, [channel]) # < we do not need to deduplicate this
|
2022-07-21 12:39:41 +00:00
|
|
|
# log("Can no longer cover %s, removing records" % channel)# as it will only be matched once --
|
|
|
|
# other bots have different nicknames so
|
|
|
|
|
2022-07-21 12:40:05 +00:00
|
|
|
def left(self, user, channel, message): # even if they saw it, they wouldn't react
|
2020-06-02 20:34:15 +00:00
|
|
|
self.event(type="part", muser=user, channel=channel, msg=message)
|
2018-08-27 19:42:49 +00:00
|
|
|
self.botLeft(channel)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def userJoined(self, user, channel):
|
2019-10-04 23:51:00 +00:00
|
|
|
self.event(type="join", muser=user, channel=channel)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2018-03-14 20:13:40 +00:00
|
|
|
def userLeft(self, user, channel, message):
|
2020-06-02 20:34:15 +00:00
|
|
|
self.event(type="part", muser=user, channel=channel, msg=message)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def userQuit(self, user, quitMessage):
|
2020-06-02 20:34:15 +00:00
|
|
|
self.chanlessEvent({"type": "quit", "muser": user, "msg": quitMessage})
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def userKicked(self, kickee, channel, kicker, message):
|
2019-08-12 20:03:47 +00:00
|
|
|
if kickee.lower() == self.nickname.lower():
|
2019-08-11 19:52:10 +00:00
|
|
|
self.botLeft(channel)
|
2020-06-02 20:34:15 +00:00
|
|
|
self.event(type="kick", muser=kicker, channel=channel, msg=message, user=kickee)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2019-08-15 20:20:49 +00:00
|
|
|
def chanlessEvent(self, cast):
|
2022-07-21 12:39:54 +00:00
|
|
|
cast["ts"] = str(datetime.now().isoformat())
|
2019-10-17 19:19:35 +00:00
|
|
|
cast["nick"], cast["ident"], cast["host"] = parsen(cast["muser"])
|
2022-07-21 12:39:41 +00:00
|
|
|
if dedup(self.name, cast): # Needs to be kept self.name until the dedup
|
|
|
|
# function is converted to the new net, num
|
|
|
|
# format
|
|
|
|
return # stop right there sir!
|
2019-08-15 20:20:49 +00:00
|
|
|
chans = userinfo.getChanList(self.net, cast["nick"])
|
2022-07-21 12:40:05 +00:00
|
|
|
if chans is None:
|
2019-08-15 20:20:49 +00:00
|
|
|
error("No channels returned for chanless event: %s" % cast)
|
2022-07-21 12:39:41 +00:00
|
|
|
# self.event(**cast) -- no, should NEVER happen
|
2019-08-11 20:52:46 +00:00
|
|
|
return
|
2019-08-12 20:03:47 +00:00
|
|
|
# getChansSingle returns all channels of the user, we only want to use
|
|
|
|
# ones we have common with them
|
|
|
|
realChans = set(chans).intersection(set(self.channels))
|
2019-08-15 20:20:49 +00:00
|
|
|
for i in realChans:
|
2019-10-04 23:51:00 +00:00
|
|
|
cast["channel"] = i
|
2019-08-11 20:52:46 +00:00
|
|
|
self.event(**cast)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def userRenamed(self, oldname, newname):
|
2019-08-15 20:20:49 +00:00
|
|
|
self.chanlessEvent({"type": "nick", "muser": oldname, "user": newname})
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def topicUpdated(self, user, channel, newTopic):
|
2020-06-02 20:34:15 +00:00
|
|
|
self.event(type="topic", muser=user, channel=channel, msg=newTopic)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def modeChanged(self, user, channel, toset, modes, args):
|
2018-07-28 20:30:50 +00:00
|
|
|
argList = list(args)
|
|
|
|
modeList = [i for i in modes]
|
|
|
|
for a, m in zip(argList, modeList):
|
2022-07-21 12:39:41 +00:00
|
|
|
self.event(
|
|
|
|
type="mode",
|
|
|
|
muser=user,
|
|
|
|
channel=channel,
|
|
|
|
mode=m,
|
|
|
|
status=toset,
|
|
|
|
modearg=a,
|
|
|
|
)
|
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
|
2022-07-21 12:40:05 +00:00
|
|
|
# TODO: strip out relay functionality
|
2018-02-23 22:05:40 +00:00
|
|
|
class IRCBotFactory(ReconnectingClientFactory):
|
2019-08-25 20:29:11 +00:00
|
|
|
def __init__(self, net, num=None, relayCommands=None, user=None, stage2=None):
|
2022-07-21 12:40:05 +00:00
|
|
|
if net is None:
|
2019-08-25 20:29:11 +00:00
|
|
|
self.num = num
|
2019-10-08 17:15:23 +00:00
|
|
|
self.net = None
|
2019-10-05 17:13:04 +00:00
|
|
|
self.name = "relay - %i" % num
|
2019-08-25 20:29:11 +00:00
|
|
|
self.relay = True
|
2019-01-26 01:57:24 +00:00
|
|
|
else:
|
2022-07-21 12:39:41 +00:00
|
|
|
self.name = net + str(num)
|
2019-08-25 20:29:11 +00:00
|
|
|
self.num = num
|
|
|
|
self.net = net
|
|
|
|
self.relay = False
|
2018-02-23 22:05:40 +00:00
|
|
|
self.client = None
|
2019-01-26 01:57:24 +00:00
|
|
|
self.maxDelay = main.config["Tweaks"]["Delays"]["MaxDelay"]
|
|
|
|
self.initialDelay = main.config["Tweaks"]["Delays"]["InitialDelay"]
|
|
|
|
self.factor = main.config["Tweaks"]["Delays"]["Factor"]
|
|
|
|
self.jitter = main.config["Tweaks"]["Delays"]["Jitter"]
|
|
|
|
|
2019-08-25 20:29:11 +00:00
|
|
|
self.relayCommands, self.user, self.stage2 = relayCommands, user, stage2
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def buildProtocol(self, addr):
|
2022-07-21 12:40:07 +00:00
|
|
|
entry = IRCBot(self.net, self.num)
|
|
|
|
main.IRCPool[self.name] = entry
|
2019-01-26 01:57:24 +00:00
|
|
|
|
2018-02-23 22:05:40 +00:00
|
|
|
self.client = entry
|
|
|
|
return entry
|
|
|
|
|
|
|
|
def clientConnectionLost(self, connector, reason):
|
2019-01-26 01:57:24 +00:00
|
|
|
if not self.relay:
|
2019-10-17 19:19:35 +00:00
|
|
|
userinfo.delChannels(self.net, self.client.channels)
|
2022-07-21 12:40:05 +00:00
|
|
|
if self.client is not None:
|
2019-10-31 15:44:59 +00:00
|
|
|
self.client.isconnected = False
|
2020-05-31 20:52:56 +00:00
|
|
|
self.client.authenticated = False
|
2018-02-23 22:05:40 +00:00
|
|
|
self.client.channels = []
|
|
|
|
error = reason.getErrorMessage()
|
2022-07-21 12:40:07 +00:00
|
|
|
log("%s - %i: connection lost: %s" % (self.net, self.num, error))
|
|
|
|
sendAll("%s - %i: connection lost: %s" % (self.net, self.num, error))
|
|
|
|
ctime = str(datetime.now().isoformat())
|
|
|
|
sendRelayNotification(
|
|
|
|
{
|
|
|
|
"type": "conn",
|
|
|
|
"net": self.net,
|
|
|
|
"num": self.num,
|
|
|
|
"status": "lost",
|
|
|
|
"message": error,
|
|
|
|
"ts": ctime,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
self.retry(connector)
|
2022-07-21 12:39:41 +00:00
|
|
|
# ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
|
2018-02-23 22:05:40 +00:00
|
|
|
|
|
|
|
def clientConnectionFailed(self, connector, reason):
|
2022-07-21 12:40:05 +00:00
|
|
|
if self.client is not None:
|
2019-10-31 15:44:59 +00:00
|
|
|
self.client.isconnected = False
|
2020-05-31 20:52:56 +00:00
|
|
|
self.client.authenticated = False
|
2018-02-23 22:05:40 +00:00
|
|
|
self.client.channels = []
|
|
|
|
error = reason.getErrorMessage()
|
2019-10-05 17:13:04 +00:00
|
|
|
log("%s - %i: connection failed: %s" % (self.net, self.num, error))
|
2019-08-05 21:51:16 +00:00
|
|
|
if not self.relay:
|
2020-05-30 20:40:10 +00:00
|
|
|
sendAll("%s - %s: connection failed: %s" % (self.net, self.num, error))
|
2020-10-28 22:26:41 +00:00
|
|
|
ctime = str(datetime.now().isoformat())
|
2022-07-21 12:39:41 +00:00
|
|
|
sendRelayNotification(
|
|
|
|
{
|
|
|
|
"type": "conn",
|
|
|
|
"net": self.net,
|
|
|
|
"num": self.num,
|
|
|
|
"status": "failed",
|
|
|
|
"message": error,
|
2022-07-21 12:39:54 +00:00
|
|
|
"ts": ctime,
|
2022-07-21 12:39:41 +00:00
|
|
|
}
|
|
|
|
)
|
2019-01-26 01:57:24 +00:00
|
|
|
self.retry(connector)
|
2022-07-21 12:39:41 +00:00
|
|
|
# ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
|