added settings about what should be forwarded to logChannel, and scheduler for expires + random fix/changes

This commit is contained in:
Nicolas Coevoet 2013-10-24 23:04:03 +02:00
parent fcdcf0d200
commit c8dcad02d6
2 changed files with 446 additions and 262 deletions

View File

@ -66,11 +66,52 @@ conf.registerGlobalValue(ChanTracker, 'opCommand',
conf.registerChannelValue(ChanTracker, 'autoExpire', conf.registerChannelValue(ChanTracker, 'autoExpire',
registry.Integer(-1, """-1 means disabled, otherwise it's in seconds, only affects new change""")) registry.Integer(-1, """-1 means disabled, otherwise it's in seconds, only affects new change"""))
# related to logChannel
conf.registerChannelValue(ChanTracker, 'logChannel', conf.registerChannelValue(ChanTracker, 'logChannel',
registry.String("", """where bot annonces op's actions, various usefull messages are send, you should set one""")) registry.String("", """where bot annonces op's actions, various usefull messages are send, you should set one"""))
conf.registerChannelValue(ChanTracker, 'forwardMessage', conf.registerChannelValue(ChanTracker, 'announceOthers',
registry.Boolean(False,"""forward quieted/banned users messages to logChannel, usefull when bot stay opped, with a channel mode +z""")) registry.Boolean(True,"""forward quieted/banned users messages to logChannel, used when bot stay opped and channel is +z,
messages from user flagged as bad, or when channel is under attack will not be forwarded"""))
conf.registerChannelValue(ChanTracker, 'announceMode',
registry.Boolean(True,"""announce mode changes to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceModeSync',
registry.Boolean(False,"""announce mode sync to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceKick',
registry.Boolean(True,"""announce kick,remove,kill and kline to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceTopic',
registry.Boolean(True,"""announce topic changes to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceEdit',
registry.Boolean(True,"""announce item edit to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceMark',
registry.Boolean(True,"""announce item mark to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceInTimeEditAndMark',
registry.Boolean(False,"""announce just placed edit and mark to logChannel when using do, q, b, e, i commands"""))
conf.registerChannelValue(ChanTracker, 'announceMassRemoval',
registry.Boolean(False,"""announce undo * ( edit ) changes to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceBotEdit',
registry.Boolean(False,"""announce item autoExpire, bot's triggered protection to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceBotMark',
registry.Boolean(False,"""announce item autoExpire, bot's triggered protection to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceNotice',
registry.Boolean(True,"""announce channel's notices to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceCtcp',
registry.Boolean(True,"""announce channel's ctcps to logChannel"""))
# others settings
conf.registerChannelValue(ChanTracker, 'keepOp', conf.registerChannelValue(ChanTracker, 'keepOp',
registry.Boolean(False, """bot stays opped""")) registry.Boolean(False, """bot stays opped"""))
@ -237,9 +278,9 @@ registry.Integer(600,"""Duration in seconds before item are removed from count,
conf.registerChannelValue(ChanTracker, 'attackDuration', conf.registerChannelValue(ChanTracker, 'attackDuration',
registry.PositiveInteger(1800,"""punition in seconds""")) registry.PositiveInteger(1800,"""punition in seconds"""))
conf.registerChannelValue(ChanTracker, 'attackMode', conf.registerChannelValue(ChanTracker, 'attackMode',
registry.String('+rq $~a',"""mode used by the bot when attack is triggered""")) registry.String('+rq-z $~a',"""mode used by the bot when attack is triggered"""))
conf.registerChannelValue(ChanTracker, 'attackUnMode', conf.registerChannelValue(ChanTracker, 'attackUnMode',
registry.String('-rq $~a',"""mode used by the bot when attackDuration is finished""")) registry.String('-rq+z $~a',"""mode used by the bot when attackDuration is finished"""))
# TODO : banevade, massjoin, clones # TODO : banevade, massjoin, clones

651
plugin.py
View File

@ -337,6 +337,22 @@ class Ircd (object):
self.lowQueue = utils.structures.smallqueue() self.lowQueue = utils.structures.smallqueue()
self.logsSize = logsSize self.logsSize = logsSize
def getChan (self,irc,channel):
if not channel or not irc:
return None
self.irc = irc
if not channel in self.channels:
self.channels[channel] = Chan (self,channel)
return self.channels[channel]
def getNick (self,irc,nick):
if not nick or not irc:
return None
self.irc = irc
if not nick in self.nicks:
self.nicks[nick] = Nick(self.logsSize)
return self.nicks[nick]
def getItem (self,irc,uid): def getItem (self,irc,uid):
# return active item # return active item
if not irc or not uid: if not irc or not uid:
@ -461,56 +477,6 @@ class Ircd (object):
c.close() c.close()
return results return results
def add (self,irc,channel,mode,value,seconds,prefix,db,logFunction,addOnly):
# add new eIqb item
if not ircdb.checkCapability(prefix,'%s,op' % channel):
if prefix != irc.prefix:
return False
if not channel or not mode or not value or not prefix:
return False
c = db.cursor()
c.execute("""SELECT id,oper FROM bans WHERE channel=? AND kind=? AND mask=? AND removed_at is NULL ORDER BY id LIMIT 1""",(channel,mode,value))
L = c.fetchall()
if len(L):
# item exists, so edit it
c.close()
if addOnly:
return False
return self.edit(irc,channel,mode,value,seconds,prefix,db,logFunction,False)
else:
if channel in self.channels:
chan = self.getChan(irc,channel)
item = chan.getItem(mode,value)
hash = '%s%s' % (mode,value)
# prepare item update after being set ( we don't have id yet )
chan.update[hash] = [mode,value,seconds,prefix]
# enqueue mode changes
chan.queue.enqueue(('+%s' % mode,value))
return True
return False
def mark (self,irc,uid,message,prefix,db,logFunction):
# won't use channel,mode,value, because Item may be removed already
if not prefix or not message:
return False
c = db.cursor()
c.execute("""SELECT id,channel,kind,mask FROM bans WHERE id=?""",(uid,))
L = c.fetchall()
b = False
if len(L):
(uid,channel,kind,mask) = L[0]
if not ircdb.checkCapability(prefix,'%s,op' % channel):
if prefix != irc.prefix:
c.close()
return False
current = time.time()
c.execute("""INSERT INTO comments VALUES (?, ?, ?, ?)""",(uid,prefix,current,message))
db.commit()
logFunction(irc,channel,'[%s][#%s +%s %s] marked by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],message))
b = True
c.close()
return b
def search (self,irc,pattern,prefix,db): def search (self,irc,pattern,prefix,db):
# deep search inside database, # deep search inside database,
# results filtered depending prefix capability # results filtered depending prefix capability
@ -590,32 +556,6 @@ class Ircd (object):
return msgs return msgs
return [] return []
def submark (self,irc,channel,mode,value,message,prefix,db,logFunction):
# add mark to an item which is not already in lists
if not channel or not mode or not value or not prefix:
return False
if not ircdb.checkCapability(prefix,'%s,op' % channel):
if prefix != irc.prefix:
return False
c = db.cursor()
c.execute("""SELECT id,oper FROM bans WHERE channel=? AND kind=? AND mask=? AND removed_at is NULL ORDER BY id LIMIT 1""",(channel,mode,value))
L = c.fetchall()
if len(L):
# item exists, so edit it
(uid,oper) = L[0]
c.close()
return self.mark(irc,uid,message,prefix,db,logFunction)
else:
if channel in self.channels:
chan = self.getChan(irc,channel)
item = chan.getItem(mode,value)
if not item:
hash = '%s%s' % (mode,value)
# prepare item update after being set ( we don't have id yet )
chan.mark[hash] = [mode,value,message,prefix]
return True
return False
def affect (self,irc,uid,prefix,db): def affect (self,irc,uid,prefix,db):
# return affected users by a mode change # return affected users by a mode change
if not uid or not prefix: if not uid or not prefix:
@ -643,8 +583,112 @@ class Ircd (object):
c.close() c.close()
return results return results
def markremoved (self,irc,uid,message,prefix,db,ct):
# won't use channel,mode,value, because Item may be removed already
# it's a duplicate of mark, only used to compute logChannel on a removed item
if not prefix or not message:
return False
c = db.cursor()
c.execute("""SELECT id,channel,kind,mask FROM bans WHERE id=?""",(uid,))
L = c.fetchall()
b = False
if len(L):
(uid,channel,kind,mask) = L[0]
if not ircdb.checkCapability(prefix,'%s,op' % channel):
if prefix != irc.prefix:
c.close()
return False
current = time.time()
c.execute("""INSERT INTO comments VALUES (?, ?, ?, ?)""",(uid,prefix,current,message))
db.commit()
f = None
if prefix != irc.prefix and ct.registryValue('announceEdit',channel=item.channel):
f = ct._logChan
elif prefix == irc.prefix and ct.registryValue('announceBotMark',channel=item.channel):
f = ct._logChan
if f:
f(irc,channel,'[%s][#%s +%s %s] marked by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],message))
b = True
c.close()
return b
def edit (self,irc,channel,mode,value,seconds,prefix,db,logFunction,massremoval): def mark (self,irc,uid,message,prefix,db,logFunction):
# won't use channel,mode,value, because Item may be removed already
if not prefix or not message:
return False
c = db.cursor()
c.execute("""SELECT id,channel,kind,mask FROM bans WHERE id=?""",(uid,))
L = c.fetchall()
b = False
if len(L):
(uid,channel,kind,mask) = L[0]
if not ircdb.checkCapability(prefix,'%s,op' % channel):
if prefix != irc.prefix:
c.close()
return False
current = time.time()
c.execute("""INSERT INTO comments VALUES (?, ?, ?, ?)""",(uid,prefix,current,message))
db.commit()
if logFunction:
logFunction(irc,channel,'[%s][#%s +%s %s] marked by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],message))
b = True
c.close()
return b
def submark (self,irc,channel,mode,value,message,prefix,db,logFunction):
# add mark to an item which is not already in lists
if not channel or not mode or not value or not prefix:
return False
if not ircdb.checkCapability(prefix,'%s,op' % channel):
if prefix != irc.prefix:
return False
c = db.cursor()
c.execute("""SELECT id,oper FROM bans WHERE channel=? AND kind=? AND mask=? AND removed_at is NULL ORDER BY id LIMIT 1""",(channel,mode,value))
L = c.fetchall()
if len(L):
# item exists
(uid,oper) = L[0]
c.close()
# must not be occurs, but ..
return self.mark(irc,uid,message,prefix,db,logFunction)
else:
if channel in self.channels:
chan = self.getChan(irc,channel)
item = chan.getItem(mode,value)
if not item:
hash = '%s%s' % (mode,value)
# prepare item update after being set ( we don't have id yet )
chan.mark[hash] = [mode,value,message,prefix]
return True
return False
def add (self,irc,channel,mode,value,seconds,prefix,db):
# add new eIqb item
if not ircdb.checkCapability(prefix,'%s,op' % channel):
if prefix != irc.prefix:
return False
if not channel or not mode or not value or not prefix:
return False
c = db.cursor()
c.execute("""SELECT id,oper FROM bans WHERE channel=? AND kind=? AND mask=? AND removed_at is NULL ORDER BY id LIMIT 1""",(channel,mode,value))
L = c.fetchall()
if len(L):
c.close()
# TODO maybe edit item here ?
return False
else:
if channel in self.channels:
chan = self.getChan(irc,channel)
item = chan.getItem(mode,value)
hash = '%s%s' % (mode,value)
# prepare item update after being set ( we don't have id yet )
chan.update[hash] = [mode,value,seconds,prefix]
# enqueue mode changes
chan.queue.enqueue(('+%s' % mode,value))
return True
return False
def edit (self,irc,channel,mode,value,seconds,prefix,db,scheduleFunction,logFunction):
# edit eIqb duration # edit eIqb duration
if not channel or not mode or not value or not prefix: if not channel or not mode or not value or not prefix:
return False return False
@ -673,14 +717,15 @@ class Ircd (object):
c.execute("""INSERT INTO comments VALUES (?, ?, ?, ?)""",(uid,prefix,current,text)) c.execute("""INSERT INTO comments VALUES (?, ?, ?, ?)""",(uid,prefix,current,text))
c.execute("""UPDATE bans SET end_at=? WHERE id=?""", (newEnd,int(uid))) c.execute("""UPDATE bans SET end_at=? WHERE id=?""", (newEnd,int(uid)))
db.commit() db.commit()
if not massremoval:
logFunction(irc,channel,'[%s][#%s +%s %s] edited by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],reason))
i = chan.getItem(kind,mask) i = chan.getItem(kind,mask)
if i: if i:
if newEnd == begin_at: if newEnd == begin_at:
i.expire = None i.expire = None
else: else:
i.expire = newEnd i.expire = newEnd
scheduleFunction(irc,newEnd)
if logFunction:
logFunction(irc,channel,'[%s][#%s +%s %s] edited by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],reason))
b = True b = True
c.close() c.close()
return b return b
@ -707,25 +752,10 @@ class Ircd (object):
msgs.append('[#%s %s]' % (uid,mask)) msgs.append('[#%s %s]' % (uid,mask))
if commits > 0: if commits > 0:
db.commit() db.commit()
logFunction(irc,channel,'[%s][%s] %s removed: %s' % (channel,mode,commits, ' '.join(msgs))) if logFunction:
logFunction(irc,channel,'[%s][%s] %s removed: %s' % (channel,mode,commits, ' '.join(msgs)))
c.close() c.close()
def getChan (self,irc,channel):
if not channel or not irc:
return None
self.irc = irc
if not channel in self.channels:
self.channels[channel] = Chan (self,channel)
return self.channels[channel]
def getNick (self,irc,nick):
if not nick or not irc:
return None
self.irc = irc
if not nick in self.nicks:
self.nicks[nick] = Nick(self.logsSize)
return self.nicks[nick]
class Chan (object): class Chan (object):
# in memory and in database stores +eIqb list -ov # in memory and in database stores +eIqb list -ov
# no user action from here, only ircd messages # no user action from here, only ircd messages
@ -753,6 +783,12 @@ class Chan (object):
self.repeatLogs = {} self.repeatLogs = {}
self.massPattern = {} self.massPattern = {}
def isWrong (self,pattern):
for key in self.spam:
if self.spam[key].len(pattern) > 0:
return True
return False
def getItems (self): def getItems (self):
# [X][Item.value] is Item # [X][Item.value] is Item
return self._lists return self._lists
@ -930,7 +966,7 @@ def getTs (irc, msg, args, state):
# here there is some glich / ugly hack to allow any('getTs'), with rest('test') after ... # here there is some glich / ugly hack to allow any('getTs'), with rest('test') after ...
# TODO checks that bot can't kill itself with loop # TODO checks that bot can't kill itself with loop
seconds = -1 seconds = -1
items = args items = list(args)
for arg in items: for arg in items:
if not arg or arg[-1] not in 'ywdhms': if not arg or arg[-1] not in 'ywdhms':
try: try:
@ -1000,7 +1036,12 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
for id in ids: for id in ids:
item = i.getItem(irc,id) item = i.getItem(irc,id)
if item: if item:
b = b and i.edit(irc,item.channel,item.mode,item.value,getDuration(seconds),msg.prefix,self.getDb(irc.network),self._logChan,False) f = None
if msg.prefix != irc.prefix and self.registryValue('announceEdit',channel=item.channel):
f = self._logChan
elif msg.prefix == irc.prefix and self.registryValue('announceBotEdit',channel=item.channel):
f = self._logChan
b = b and i.edit(irc,item.channel,item.mode,item.value,getDuration(seconds),msg.prefix,self.getDb(irc.network),self._schedule,f)
else: else:
b = False b = False
if not msg.nick == irc.nick: if not msg.nick == irc.nick:
@ -1058,7 +1099,16 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
i = self.getIrc(irc) i = self.getIrc(irc)
b = True b = True
for id in ids: for id in ids:
b = b and i.mark(irc,id,message,msg.prefix,self.getDb(irc.network),self._logChan) item = i.getItem(irc,id)
if item:
f = None
if msg.prefix != irc.prefix and self.registryValue('announceEdit',channel=item.channel):
f = self._logChan
elif msg.prefix == irc.prefix and self.registryValue('announceBotMark',channel=item.channel):
f = self._logChan
b = b and i.mark(irc,id,message,msg.prefix,self.getDb(irc.network),f)
else:
b = b and i.markremoved(irc,id,message,msg.prefix,self.getDb(irc.network),self)
if not msg.nick == irc.nick: if not msg.nick == irc.nick:
if b: if b:
irc.replySuccess() irc.replySuccess()
@ -1106,11 +1156,10 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if mode in self.registryValue('modesToAsk') or mode in self.registryValue('modesToAskWhenOpped'): if mode in self.registryValue('modesToAsk') or mode in self.registryValue('modesToAskWhenOpped'):
b = self._adds(irc,msg,args,channel,mode,items,getDuration(seconds),reason) b = self._adds(irc,msg,args,channel,mode,items,getDuration(seconds),reason)
if not msg.nick == irc.nick: if not msg.nick == irc.nick:
# why msg.nick == irc.nick, it because with that it can be used with BanHammer plugin
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('item already active or not enough rights') irc.error('unknown pattern, or pattern already active')
else: else:
irc.error('selected mode is not supported by config') irc.error('selected mode is not supported by config')
@ -1125,7 +1174,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern') irc.error('unknown pattern, or pattern already active')
q = wrap(q,['op',commalist('something'),any('getTs',True),rest('text')]) q = wrap(q,['op',commalist('something'),any('getTs',True),rest('text')])
def b (self, irc, msg, args, channel, items, seconds,reason): def b (self, irc, msg, args, channel, items, seconds,reason):
@ -1137,7 +1186,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern') irc.error('unknown pattern, or pattern already active')
b = wrap(b,['op',commalist('something'),any('getTs',True),rest('text')]) b = wrap(b,['op',commalist('something'),any('getTs',True),rest('text')])
def i (self, irc, msg, args, channel, items, seconds): def i (self, irc, msg, args, channel, items, seconds):
@ -1149,7 +1198,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern') irc.error('unknown pattern, or pattern already active')
i = wrap(i,['op',commalist('something'),any('getTs',True),rest('text')]) i = wrap(i,['op',commalist('something'),any('getTs',True),rest('text')])
def e (self, irc, msg, args, channel, items,seconds,reason): def e (self, irc, msg, args, channel, items,seconds,reason):
@ -1161,7 +1210,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern') irc.error('unknown pattern, or pattern already active')
e = wrap(e,['op',commalist('something'),any('getTs'),rest('text')]) e = wrap(e,['op',commalist('something'),any('getTs'),rest('text')])
def undo (self, irc, msg, args, channel, mode, items): def undo (self, irc, msg, args, channel, mode, items):
@ -1269,6 +1318,29 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
irc.error('nick not found or wrong hostmask given') irc.error('nick not found or wrong hostmask given')
getmask = wrap(getmask,['owner','text']) getmask = wrap(getmask,['owner','text'])
#def isvip (self,irc,msg,args,channel,nick):
#"""[<channel>] <nick>
#tell is <nick> is vip in <channel>, mostly used for debug"""
#i = self.getIrc(irc)
#if nick in i.nicks:
#irc.reply(self._isVip(irc,channel,self.getNick(irc,nick)))
#else:
#irc.error('nick not found')
#isvip = wrap(isvip,['op','nick'])
#def isbad (self,irc,msg,args,channel,nick):
#"""[<channel>] <nick>
#tell is <nick> is flagged as bad in <channel>, mostly used for debug"""
#i = self.getIrc(irc)
#if nick in i.nicks:
#chan = self.getChan(irc,channel)
#irc.reply(chan.isWrong(getBestPattern(self.getNick(irc,nick))[0]))
#else:
#irc.error('nick not found')
#isbad = wrap(isbad,['op','nick'])
def _adds (self,irc,msg,args,channel,mode,items,duration,reason): def _adds (self,irc,msg,args,channel,mode,items,duration,reason):
i = self.getIrc(irc) i = self.getIrc(irc)
targets = [] targets = []
@ -1284,8 +1356,14 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
targets.append(patterns[0]) targets.append(patterns[0])
n = 0 n = 0
for item in targets: for item in targets:
if i.add(irc,channel,mode,item,duration,msg.prefix,self.getDb(irc.network),self._logChan,False): if i.add(irc,channel,mode,item,duration,msg.prefix,self.getDb(irc.network)):
if reason: if reason:
f = None
if self.registryValue('announceInTimeEditAndMark',channel=channel):
if msg.prefix != irc.prefix and self.registryValue('announceMark',channel=channel):
f = self._logChan
elif msg.prefix == irc.prefix and self.registryValue('announceBotMark',channel=channel):
f = self._logChan
i.submark(irc,channel,mode,item,reason,msg.prefix,self.getDb(irc.network),self._logChan) i.submark(irc,channel,mode,item,reason,msg.prefix,self.getDb(irc.network),self._logChan)
n = n+1 n = n+1
self.forceTickle = True self.forceTickle = True
@ -1312,12 +1390,23 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
targets.append(pattern) targets.append(pattern)
elif item == '*': elif item == '*':
massremove = True massremove = True
targets = []
if channel in list(irc.state.channels.keys()): if channel in list(irc.state.channels.keys()):
L = chan.getItemsFor(mode) L = chan.getItemsFor(mode)
for pattern in L: for pattern in L:
targets.append(pattern) targets.append(pattern)
break
f = None
if massremove:
if self.registryValue('announceMassRemoval',channel=channel):
f = self._logChan
else:
if msg.prefix != irc.prefix and self.registryValue('announceEdit',channel=channel):
f = self._logChan
elif msg.prefix == irc.prefix and self.registryValue('announceBotEdit',channel=channel):
f = self._logChan
for item in targets: for item in targets:
if i.edit(irc,channel,mode,item,0,msg.prefix,self.getDb(irc.network),self._logChan,massremove): if i.edit(irc,channel,mode,item,0,msg.prefix,self.getDb(irc.network),self._schedule,f):
count = count + 1 count = count + 1
self.forceTickle = True self.forceTickle = True
self._tickle(irc) self._tickle(irc)
@ -1445,7 +1534,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
item.asked = True item.asked = True
retickle = True retickle = True
# dequeue pending actions # dequeue pending actions
log.debug('[%s] isOpped : %s, opAsked : %s, deopAsked %s, deopPending %s' % (channel,irc.nick in irc.state.channels[channel].ops,chan.opAsked,chan.deopAsked,chan.deopPending)) #log.debug('[%s] isOpped : %s, opAsked : %s, deopAsked %s, deopPending %s' % (channel,irc.nick in irc.state.channels[channel].ops,chan.opAsked,chan.deopAsked,chan.deopPending))
if chan.syn: if chan.syn:
if not irc.nick in irc.state.channels[channel].ops: if not irc.nick in irc.state.channels[channel].ops:
chan.deopAsked = False chan.deopAsked = False
@ -1513,7 +1602,13 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
(m,value,expire,prefix) = update (m,value,expire,prefix) = update
item = chan.getItem(m,value) item = chan.getItem(m,value)
if item and item.expire != expire: if item and item.expire != expire:
b = i.edit(irc,item.channel,item.mode,item.value,expire,prefix,self.getDb(irc.network),self._logChan,False) f = None
if self.registryValue('announceInTimeEditAndMark',channel=item.channel):
if prefix != irc.prefix and self.registryValue('announceEdit',channel=item.channel):
f = self._logChan
elif prefix == irc.prefix and self.registryValue('announceBotEdit',channel=item.channel):
f = self._logChan
b = i.edit(irc,item.channel,item.mode,item.value,expire,prefix,self.getDb(irc.network),self._schedule,f)
key = '%s%s' % (m,value) key = '%s%s' % (m,value)
del chan.update[key] del chan.update[key]
retickle = True retickle = True
@ -1526,7 +1621,13 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
(m,value,reason,prefix) = mark (m,value,reason,prefix) = mark
item = chan.getItem(m,value) item = chan.getItem(m,value)
if item: if item:
i.mark(irc,item.uid,reason,prefix,self.getDb(irc.network),self._logChan) f = None
if self.registryValue('announceInTimeEditAndMark',channel=item.channel):
if prefix != irc.prefix and self.registryValue('announceMark',channel=item.channel):
f = self._logChan
elif prefix == irc.prefix and self.registryValue('announceBotMark',channel=item.channel):
f = self._logChan
i.mark(irc,item.uid,reason,prefix,self.getDb(irc.network),f)
key = '%s%s' % (item.mode,value) key = '%s%s' % (item.mode,value)
del chan.mark[key] del chan.mark[key]
if irc.nick in irc.state.channels[channel].ops and not self.registryValue('keepOp',channel=channel) and not chan.deopPending and not chan.deopAsked: if irc.nick in irc.state.channels[channel].ops and not self.registryValue('keepOp',channel=channel) and not chan.deopPending and not chan.deopAsked:
@ -1556,7 +1657,12 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
chan.dones.append(mode) chan.dones.append(mode)
b = True b = True
i = self.getIrc(irc) i = self.getIrc(irc)
i.resync(irc,channel,mode,self.getDb(irc.network),self._logChan) f = None
if self.registryValue('announceModeSync',channel=channel):
f = self._logChan
if b:
self._logChan(irc,channel,'[%s] sync %s' % (channel,chan.dones))
i.resync(irc,channel,mode,self.getDb(irc.network),f)
self._tickle(irc) self._tickle(irc)
def do346 (self,irc,msg): def do346 (self,irc,msg):
@ -1627,7 +1733,8 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
# this flag is mostly used to wait for the full sync before moaming on owners when something wrong happened # this flag is mostly used to wait for the full sync before moaming on owners when something wrong happened
# like not enough rights to take op # like not enough rights to take op
chan.syn = True chan.syn = True
self._logChan(irc,channel,"[%s] is ready" % channel) if self.registryValue('announceModeSync',channel=channel):
self._logChan(irc,channel,"[%s] is ready" % channel)
self._tickle(irc) self._tickle(irc)
def _logChan (self,irc,channel,message): def _logChan (self,irc,channel,message):
@ -1690,7 +1797,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if ircutils.isChannel(channel) and channel in irc.state.channels: if ircutils.isChannel(channel) and channel in irc.state.channels:
if len(reason): if len(reason):
n.addLog(channel,'has left [%s]' % (reason)) n.addLog(channel,'has left [%s]' % (reason))
if reason.startswith('requested by'): if reason.startswith('requested by') and self.registryValue('announceKick',channel=channel):
self._logChan(irc,channel,'[%s] %s has left (%s)' % (channel,msg.prefix,reason)) self._logChan(irc,channel,'[%s] %s has left (%s)' % (channel,msg.prefix,reason))
else: else:
n.addLog(channel,'has left') n.addLog(channel,'has left')
@ -1715,7 +1822,8 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
return return
n = self.getNick(irc,target) n = self.getNick(irc,target)
n.addLog(channel,'kicked by %s (%s)' % (msg.prefix,reason)) n.addLog(channel,'kicked by %s (%s)' % (msg.prefix,reason))
self._logChan(irc,channel,'[%s] %s kicked by %s (%s)' % (channel,target,msg.prefix,reason)) if self.registryValue('announceKick',channel=channel):
self._logChan(irc,channel,'[%s] %s kicked by %s (%s)' % (channel,target,msg.prefix,reason))
self._tickle(irc) self._tickle(irc)
def _rmNick (self,irc,n): def _rmNick (self,irc,n):
@ -1758,9 +1866,11 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
elif reason and reason.startswith('Killed (') or reason.startswith('K-Lined'): elif reason and reason.startswith('Killed (') or reason.startswith('K-Lined'):
if not reason.find('Nickname regained by services') != -1: if not reason.find('Nickname regained by services') != -1:
for channel in irc.state.channels: for channel in irc.state.channels:
for nick in irc.state.channels[channel].users: if self.registryValue('announceKick',channel=channel):
if nick == msg.nick: for nick in irc.state.channels[channel].users:
self._logChan(irc,channel,'[%s] %s has quit (%s)' % (channel,msg.prefix,reason)) if nick == msg.nick:
self._logChan(irc,channel,'[%s] %s has quit (%s)' % (channel,msg.prefix,reason))
break
if removeNick: if removeNick:
i = self.getIrc(irc) i = self.getIrc(irc)
if msg.nick in i.nicks: if msg.nick in i.nicks:
@ -1877,60 +1987,67 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
best = False best = False
if len(patterns): if len(patterns):
best = patterns[0] best = patterns[0]
if not best:
return
for channel in targets.split(','): for channel in targets.split(','):
if irc.isChannel(channel) and channel in irc.state.channels: if irc.isChannel(channel) and channel in irc.state.channels:
chan = self.getChan(irc,channel) chan = self.getChan(irc,channel)
n.addLog(channel,'NOTICE | %s' % text) n.addLog(channel,'NOTICE | %s' % text)
isVip = self._isVip(irc,channel,n)
if self._isVip(irc,channel,n): if not isVip:
# ignore vip isPattern = False
continue for pattern in chan.massPattern:
if self._strcompare(pattern,text) >= self.registryValue('massRepeatPercent',channel=channel):
# why flood logChannel ..
#self._logChan(irc,channel,'[%s] %s notices "%s"' % (channel,msg.prefix,text))
# checking if message matchs living massRepeatPattern
for pattern in chan.massPattern:
if self._strcompare(pattern,text) >= self.registryValue('massRepeatPercent',channel=channel):
kind = 'massRepeat'
mode = self.registryValue('%sMode' % kind,channel=channel)
duration = self.registryValue('%sDuration' % kind,channel=channel)
comment = self.registryValue('%sComment' % kind,channel=channel)
self._act(irc,channel,mode,best,duration,comment)
self._isBad(irc,channel,best) # increment bad
self.forceTickle = True
best = None
if best:
isNotice = self._isSomething(irc,channel,best,'notice')
isMass = False
isBad = False
if len(text) >= self.registryValue('massRepeatChars',channel=channel):
isMass = self._isSomething(irc,channel,text,'massRepeat')
if isNotice:
isBad = self._isSomething(irc,channel,best,'bad')
if isNotice or isMass or isBad:
kind = None
if isBad:
kind = 'bad'
elif isMass:
kind = 'massRepeat' kind = 'massRepeat'
if not text in chan.massPattern: mode = self.registryValue('%sMode' % kind,channel=channel)
chan.massPattern[text] = text duration = self.registryValue('%sDuration' % kind,channel=channel)
def unpattern (): comment = self.registryValue('%sComment' % kind,channel=channel)
if text in chan.massPattern: self._act(irc,channel,mode,best,duration,comment)
del chan.massPattern[text] self._isBad(irc,channel,best) # increment bad
# remove pattern after massRepeatDuration, maybe add another config value ? self.forceTickle = True
schedule.addEvent(unpattern,time.time()+self.registryValue('massRepeatDuration',channel=channel)) isPattern = True
else: if not isPattern:
kind = 'notice' isNotice = self._isSomething(irc,channel,best,'notice')
mode = self.registryValue('%sMode' % kind,channel=channel) isMass = False
if len(mode) > 1: isBad = False
mode = mode[0] if len(text) >= self.registryValue('massRepeatChars',channel=channel):
duration = self.registryValue('%sDuration' % kind,channel=channel) isMass = self._isSomething(irc,channel,text,'massRepeat')
comment = self.registryValue('%sComment' % kind,channel=channel) if isNotice:
self._act(irc,channel,mode,best,duration,comment) isBad = self._isSomething(irc,channel,best,'bad')
self.forceTickle = True if isNotice or isMass or isBad:
kind = None
if isBad:
kind = 'bad'
elif isMass:
kind = 'massRepeat'
if not text in chan.massPattern:
chan.massPattern[text] = text
def unpattern ():
if text in chan.massPattern:
del chan.massPattern[text]
# remove pattern after massRepeatDuration, maybe add another config value ?
schedule.addEvent(unpattern,time.time()+self.registryValue('massRepeatDuration',channel=channel))
else:
kind = 'notice'
mode = self.registryValue('%sMode' % kind,channel=channel)
if len(mode) > 1:
mode = mode[0]
duration = self.registryValue('%sDuration' % kind,channel=channel)
comment = self.registryValue('%sComment' % kind,channel=channel)
self._act(irc,channel,mode,best,duration,comment)
self.forceTickle = True
if self.registryValue('announceNotice',channel=channel):
if not chan.isWrong(best):
self._logChan(irc,channel,'[%s] %s notice "%s"' % (channel,msg.prefix,text))
self._tickle(irc) self._tickle(irc)
def _schedule(self,irc,end):
def do():
self.forceTickle = True
self._tickle(irc)
schedule.addEvent(do,end)
def _isVip (self,irc,channel,n): def _isVip (self,irc,channel,n):
chan = self.getChan(irc,channel) chan = self.getChan(irc,channel)
ignoresModes = self.registryValue('modesToAskWhenOpped') ignoresModes = self.registryValue('modesToAskWhenOpped')
@ -1974,101 +2091,116 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
message = text message = text
if isCtcpMsg and not isAction: if isCtcpMsg and not isAction:
message = 'CTCP | %s' % text message = 'CTCP | %s' % text
self._logChan(irc,channel,'[%s] %s ctcps "%s"' % (channel,msg.prefix,text))
elif isAction: elif isAction:
message = '- %s -' % text message = '- %s -' % text
n.addLog(channel,message) n.addLog(channel,message)
# protection features # protection features
if self._isVip(irc,channel,n): isVip = self._isVip(irc,channel,n)
# ignore vip
continue
# checking if message matchs living massRepeatPattern # checking if message matchs living massRepeatPattern
for pattern in chan.massPattern: if not isVip:
if self._strcompare(pattern,text) >= self.registryValue('massRepeatPercent',channel=channel): for pattern in chan.massPattern:
kind = 'massRepeat' if self._strcompare(pattern,text) >= self.registryValue('massRepeatPercent',channel=channel):
kind = 'massRepeat'
mode = self.registryValue('%sMode' % kind,channel=channel)
duration = self.registryValue('%sDuration' % kind,channel=channel)
comment = self.registryValue('%sComment' % kind,channel=channel)
self._act(irc,channel,mode,best,duration,comment)
self._isBad(irc,channel,best) # increment bad user count
self.forceTickle = True
# no needs to check others protection
continue
isCtcp = False
if isCtcpMsg:
isCtcp = self._isSomething(irc,channel,best,'ctcp')
isFlood = self._isFlood(irc,channel,best)
isLowFlood = self._isLowFlood(irc,channel,best)
isRepeat = self._isRepeat(irc,channel,best,text)
isHilight = self._isHilight(irc,channel,best,text)
isCap = self._isCap(irc,channel,best,text)
isMass = False
if len(text) >= self.registryValue('massRepeatChars',channel=channel):
isMass = self._isSomething(irc,channel,text,'massRepeat')
if isFlood or isHilight or isRepeat or isCap or isCtcp or isLowFlood or isMass:
isBad = self._isBad(irc,channel,best)
kind = None
duration = 0
if isBad:
kind = 'bad'
duration = self.registryValue('badDuration',channel=channel)
else:
# todo select the hardest duration
if isMass:
kind = 'massRepeat'
duration = self.registryValue('massRepeatDuration',channel=channel)
if not text in chan.massPattern:
chan.massPattern[text] = text
def unpattern ():
if text in chan.massPattern:
del chan.massPattern[text]
# remove pattern after massRepeatDuration, maybe add another config value ?
schedule.addEvent(unpattern,time.time()+duration)
if isFlood:
d = self.registryValue('floodDuration',channel=channel)
if d > duration:
kind = 'flood'
duration = d
if isLowFlood:
d = self.registryValue('lowFloodDuration',channel=channel)
if d > duration:
kind = 'lowFlood'
duration = d
if isRepeat:
d = self.registryValue('repeatDuration',channel=channel)
if d > duration:
kind = 'repeat'
duration = d
if isHilight:
d = self.registryValue('hilightDuration',channel=channel)
if d > duration:
kind = 'hilight'
duration = d
if isCap:
d = self.registryValue('capDuration',channel=channel)
if d > duration:
kind = 'cap'
duration = d
if isCtcp:
d = self.registryValue('ctcpDuration',channel=channel)
if d > duration:
kind = 'ctcp'
duration = d
mode = self.registryValue('%sMode' % kind,channel=channel) mode = self.registryValue('%sMode' % kind,channel=channel)
if len(mode) > 1:
mode = mode[0]
duration = self.registryValue('%sDuration' % kind,channel=channel) duration = self.registryValue('%sDuration' % kind,channel=channel)
comment = self.registryValue('%sComment' % kind,channel=channel) comment = self.registryValue('%sComment' % kind,channel=channel)
self._act(irc,channel,mode,best,duration,comment) self._act(irc,channel,mode,best,duration,comment)
self._isBad(irc,channel,best) # increment bad user count
self.forceTickle = True self.forceTickle = True
# no needs to check others protection if not chan.isWrong(best):
continue if self.registryValue('announceCtcp',channel=channel) and isCtcpMsg:
self._logChan(irc,channel,'[%s] %s ctcps "%s"' % (channel,msg.prefix,text))
isCtcp = False self.forceTickle = True
if isCtcpMsg: elif self.registryValue('announceOthers') and irc.nick in irc.state.channels[channel].ops:
isCtcp = self._isSomething(irc,channel,best,'ctcp') if 'z' in irc.state.channels[channel].modes and not msg.nick in irc.state.channels[channel].voices and not msg.nick in irc.state.channels[channel].ops:
isFlood = self._isFlood(irc,channel,best) modes = self.registryValue('modesToAsk')
isLowFlood = self._isLowFlood(irc,channel,best) found = False
isRepeat = self._isRepeat(irc,channel,best,text) for mode in modes:
isHilight = self._isHilight(irc,channel,best,text) items = chan.getItemsFor(mode)
isCap = self._isCap(irc,channel,best,text) for item in items:
isMass = False f = match(items[item].value,n)
if len(text) >= self.registryValue('massRepeatChars',channel=channel): if f:
isMass = self._isSomething(irc,channel,text,'massRepeat') found = [items[item],f]
if isFlood or isHilight or isRepeat or isCap or isCtcp or isLowFlood or isMass: if found:
isBad = self._isBad(irc,channel,best) break
kind = None if found:
duration = 0 break
if isBad: if found:
kind = 'bad' self._logChan(irc,channel,'[%s][#%s +%s %s] <%s> %s' % (channel,found[0].uid,found[0].mode,found[0].value,msg.nick,text))
duration = self.registryValue('badDuration',channel=channel)
else:
# todo select the hardest duration
if isMass:
kind = 'massRepeat'
duration = self.registryValue('massRepeatDuration',channel=channel)
if not text in chan.massPattern:
chan.massPattern[text] = text
def unpattern ():
if text in chan.massPattern:
del chan.massPattern[text]
# remove pattern after massRepeatDuration, maybe add another config value ?
schedule.addEvent(unpattern,time.time()+duration)
if isFlood:
d = self.registryValue('floodDuration',channel=channel)
if d > duration:
kind = 'flood'
duration = d
if isLowFlood:
d = self.registryValue('lowFloodDuration',channel=channel)
if d > duration:
kind = 'lowFlood'
duration = d
if isRepeat:
d = self.registryValue('repeatDuration',channel=channel)
if d > duration:
kind = 'repeat'
duration = d
if isHilight:
d = self.registryValue('hilightDuration',channel=channel)
if d > duration:
kind = 'hilight'
duration = d
if isCap:
d = self.registryValue('capDuration',channel=channel)
if d > duration:
kind = 'cap'
duration = d
if isCtcp:
d = self.registryValue('ctcpDuration',channel=channel)
if d > duration:
kind = 'ctcp'
duration = d
mode = self.registryValue('%sMode' % kind,channel=channel)
if len(mode) > 1:
mode = mode[0]
duration = self.registryValue('%sDuration' % kind,channel=channel)
comment = self.registryValue('%sComment' % kind,channel=channel)
self._act(irc,channel,mode,best,duration,comment)
self.forceTickle = True
#else:
#if self.registryValue('forwardMessage',channel=channel):
# todo forward msgs to logChannel for quieted/banned and moderated channel, with channel mode +z
# todo prevent +e to be forwarded
# todo prevent bot to flood logChannel with flooder msgs
elif 'm' in irc.state.channels[channel].modes:
if not msg.nick in irc.state.channels[channel].voices and not msg.nick in irc.state.channels[channel].ops:
self._logChan(irc,channel,'[%s][+m] <%s> %s' % (channel,msg.prefix,text))
self._tickle(irc) self._tickle(irc)
@ -2081,7 +2213,10 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if channel in irc.state.channels: if channel in irc.state.channels:
if n: if n:
n.addLog(channel,'sets topic "%s"' % msg.args[1]) n.addLog(channel,'sets topic "%s"' % msg.args[1])
self._logChan(irc,channel,'[%s] %s sets topic "%s"' % (channel,msg.prefix,msg.args[1])) if self.registryValue('announceTopic',channel=channel):
self._logChan(irc,channel,'[%s] %s sets topic "%s"' % (channel,msg.prefix,msg.args[1]))
self.forceTickle = True
self._tickle(irc)
def unOp (self,irc,channel): def unOp (self,irc,channel):
# remove irc.nick from op, if nothing pending # remove irc.nick from op, if nothing pending
@ -2098,7 +2233,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
chan.deopPending = False chan.deopPending = False
chan.queue.enqueue(('-o',irc.nick)) chan.queue.enqueue(('-o',irc.nick))
# little trick here, tickle before setting deopFlag # little trick here, tickle before setting deopFlag
self.forceTicke = True self.forceTickle = True
self._tickle(irc) self._tickle(irc)
chan.deopAsked = True chan.deopAsked = True
else: else:
@ -2139,7 +2274,10 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
# overwrite expires # overwrite expires
if msg.nick != irc.nick: if msg.nick != irc.nick:
# an op do something, and over expires is enabled, announce or not ? currently not. change last flag # an op do something, and over expires is enabled, announce or not ? currently not. change last flag
i.edit(irc,channel,m,value,overexpire,irc.prefix,self.getDb(irc.network),self._logChan,True) f = None
if self.registryValue('announceBotEdit',channel=channel):
f = self._logChan
i.edit(irc,channel,m,value,overexpire,irc.prefix,self.getDb(irc.network),self._schedule,f)
self.forceTickle = True self.forceTickle = True
# here bot could add others mode changes or actions # here bot could add others mode changes or actions
if item and len(item.affects): if item and len(item.affects):
@ -2207,7 +2345,8 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
msgs.append(mode) msgs.append(mode)
if irc.nick in irc.state.channels[channel].ops and not self.registryValue('keepOp',channel=channel): if irc.nick in irc.state.channels[channel].ops and not self.registryValue('keepOp',channel=channel):
self.forceTickle = True self.forceTickle = True
self._logChan(irc,channel,'[%s] %s sets %s' % (channel,msg.prefix,' '.join(msgs))) if self.registryValue('announceMode',channel=channel):
self._logChan(irc,channel,'[%s] %s sets %s' % (channel,msg.prefix,' '.join(msgs)))
self._tickle(irc) self._tickle(irc)
def do474(self,irc,msg): def do474(self,irc,msg):
@ -2229,9 +2368,13 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
def _act (self,irc,channel,mode,mask,duration,reason): def _act (self,irc,channel,mode,mask,duration,reason):
if mode in self.registryValue('modesToAsk') or mode in self.registryValue('modesToAskWhenOpped'): if mode in self.registryValue('modesToAsk') or mode in self.registryValue('modesToAskWhenOpped'):
i = self.getIrc(irc) i = self.getIrc(irc)
if i.add(irc,channel,mode,mask,duration,irc.prefix,self.getDb(irc.network),self._logChan,True): if i.add(irc,channel,mode,mask,duration,irc.prefix,self.getDb(irc.network)):
if reason and len(reason): if reason and len(reason):
i.submark(irc,channel,mode,mask,reason,irc.prefix,self.getDb(irc.network),self._logChan) f = None
if self.registryValue('announceInTimeEditAndMark',channel=channel):
if self.registryValue('announceBotMark',channel=channel):
f = self._logChan
i.submark(irc,channel,mode,mask,reason,irc.prefix,self.getDb(irc.network),f)
self.forceTickle = True self.forceTickle = True
self._tickle(irc) self._tickle(irc)
else: else: