Implement ChanKeep joining functions

* Low-key channel joining with incrementally increasing delay
* Spin up needed instances to be able to cover a certain channel space
* Fix provisioning functions to prevent race conditions with lots of
relays being created at once
* Tweakable switchover from covering all channels to only covering
channels with more users than the mean of the cumulative user count
This commit is contained in:
2019-10-11 13:07:57 +01:00
parent c3d0cb04b6
commit 7a6e3338c0
7 changed files with 126 additions and 37 deletions

View File

@@ -1,8 +1,8 @@
from twisted.internet.protocol import ReconnectingClientFactory
from twisted.words.protocols.irc import IRCClient
from twisted.internet.defer import Deferred
from twisted.internet.task import LoopingCall, deferLater
from twisted.internet import reactor
from twisted.internet.task import LoopingCall
from twisted.internet import reactor, task
from string import digits
from random import randint
@@ -25,7 +25,6 @@ from utils.logging.send import *
from twisted.internet.ssl import DefaultOpenSSLContextFactory
def deliverRelayCommands(num, relayCommands, user=None, stage2=None):
# where relay is a dictionary extracted from the Network object
keyFN = main.certPath+main.config["Key"]
certFN = main.certPath+main.config["Certificate"]
contextFactory = DefaultOpenSSLContextFactory(keyFN.encode("utf-8", "replace"),
@@ -53,6 +52,7 @@ class IRCRelay(IRCClient):
self.relayCommands = relayCommands
self.num = num
self.stage2 = stage2
self.loop = None
def parsen(self, user):
step = user.split("!")
@@ -68,6 +68,10 @@ class IRCRelay(IRCClient):
def privmsg(self, user, channel, msg):
nick, ident, host = self.parsen(user)
if "does not exist" in msg or "doesn't exist" in msg:
error("ZNC issue:", msg)
if "Unable to load" in msg:
error("ZNC issue:", msg)
if nick[0] == main.config["Tweaks"]["ZNC"]["Prefix"]:
nick = nick[1:]
if nick in self.relayCommands.keys():
@@ -77,20 +81,27 @@ class IRCRelay(IRCClient):
log("%s: relay password mismatch" % self.num)
sendAll("%s: relay password mismatch" % self.num)
def signedOn(self):
self.connected = True
log("signed on as a relay: %s" % self.num)
#sendRelayNotification("Relay", {"type": "conn", "status": "connected"}) nobody actually cares
for i in self.relayCommands.keys():
for x in self.relayCommands[i]:
self.msg(main.config["Tweaks"]["ZNC"]["Prefix"]+i, x)
def sendStage2(self):
if not self.stage2 == None: # [["user", {"sasl": ["message1", "message2"]}], []]
if not len(self.stage2) == 0:
user = self.stage2[0].pop(0)
commands = self.stage2[0].pop(0)
del self.stage2[0]
deliverRelayCommands(self.num, commands, user, self.stage2)
deferLater(reactor, 1, self.transport.loseConnection)
def signedOn(self):
self.connected = True
log("signed on as a relay: %s" % self.num)
#sendRelayNotification("Relay", {"type": "conn", "status": "connected"}) nobody actually cares
sleeptime = 0
increment = 0.8
for i in self.relayCommands.keys():
for x in self.relayCommands[i]:
reactor.callLater(sleeptime, self.msg, main.config["Tweaks"]["ZNC"]["Prefix"]+i, x)
sleeptime += increment
increment += 0.8
reactor.callLater(sleeptime, self.sendStage2)
reactor.callLater(sleeptime+5, self.transport.loseConnection)
return
class IRCBot(IRCClient):
@@ -140,6 +151,26 @@ class IRCBot(IRCClient):
return [nick, ident, host]
def joinChannels(self, channels):
sleeptime = 0.0
increment = 0.8
for i in channels:
if not i in self.channels:
debug(self.net, "-", self.num, ": joining", i, "in", sleeptime, "seconds")
reactor.callLater(sleeptime, self.join, i)
sleeptime += increment
if sleeptime == 10:
sleeptime = 0.0
increment = 0.7
increment += 0.1
else:
print("Already on %s, skipping." % i)
def checkChannels(self):
if self.net in main.TempChan.keys():
if self.num in main.TempChan[self.net].keys():
self.joinChannels(main.TempChan[self.net][self.num])
def event(self, **cast):
for i in list(cast.keys()): # Make a copy of the .keys() as Python 3 cannot handle iterating over
if cast[i] == "": # a dictionary that changes length with each iteration
@@ -157,6 +188,8 @@ class IRCBot(IRCClient):
del cast["ident"]
del cast["host"]
del cast["channel"]
if "Disconnected from IRC" in cast["msg"]:
self.connected = False
if not cast["type"] in ["query", "self", "highlight", "znc", "who"]:
if "channel" in cast.keys() and not cast["type"] == "mode": # don't handle modes here
if cast["channel"].lower() == self.nickname.lower(): # as they are channel == nickname
@@ -230,8 +263,6 @@ class IRCBot(IRCClient):
self.setNick(self._attemptedNick)
def irc_ERR_PASSWDMISMATCH(self, prefix, params):
print(locals())
print(globals())
log("%s - %i: password mismatch" % (self.net, self.num))
sendAll("%s - %i: password mismatch" % (self.net, self.num))
@@ -364,9 +395,6 @@ class IRCBot(IRCClient):
chankeep.initialList(self.net, self.num, listinfo, self.chanlimit)
def irc_unknown(self, prefix, command, params):
debug("Unknown message: %s - %s - %s" % (prefix, command, params))
def isupport(self, options):
for i in options:
if i.startswith("CHANLIMIT"):
@@ -379,6 +407,9 @@ class IRCBot(IRCClient):
return
except TypeError:
warn("Invalid CHANLIMIT: %s" % i)
if self.num == 1: # Only one instance should do a list, so
self.list() # why not this one? :P
self.checkChannels()
#twisted sucks so i have to do this to actually get the user info
def irc_JOIN(self, prefix, params):